132 lines
3.4 KiB
TypeScript
132 lines
3.4 KiB
TypeScript
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
import { http, HttpResponse } from "msw";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { useBrokerageAccounts } from "@/entities/brokerage-account";
|
|
import { configureAuthClient } from "@/shared/api";
|
|
import { createQueryWrapper } from "@/test/query";
|
|
import { resource } from "@/test/resource";
|
|
import { server } from "@/test/server";
|
|
|
|
import { useScreenSync } from "../index";
|
|
|
|
const TRANSPORT_ATTEMPTS = 3;
|
|
const TRANSPORT_RECOVERY_MS = 3000;
|
|
|
|
function auth() {
|
|
configureAuthClient({
|
|
accessToken: () => "synthetic",
|
|
clear: vi.fn(),
|
|
refresh: () => Promise.resolve(true),
|
|
});
|
|
}
|
|
|
|
describe("read-only screen synchronization", () => {
|
|
it("retries the resource with GET without requesting source observation", async () => {
|
|
auth();
|
|
let gets = 0;
|
|
const syncModes: (null | string)[] = [];
|
|
server.use(
|
|
http.get("*/api/v1/brokerage-accounts", ({ request }) => {
|
|
gets += 1;
|
|
syncModes.push(new URL(request.url).searchParams.get("sync"));
|
|
|
|
// The transport retries a transient read twice (1 s, 2 s) on its own.
|
|
return gets <= TRANSPORT_ATTEMPTS
|
|
? HttpResponse.json({ code: "TBANK_UNAVAILABLE" }, { status: 503 })
|
|
: HttpResponse.json(resource([]));
|
|
}),
|
|
);
|
|
vi.useFakeTimers();
|
|
|
|
const { result } = renderHook(
|
|
() => {
|
|
const accounts = useBrokerageAccounts();
|
|
|
|
return {
|
|
accounts,
|
|
sync: useScreenSync({ screen: "accounts" }, [accounts]),
|
|
};
|
|
},
|
|
{ wrapper: createQueryWrapper().wrapper },
|
|
);
|
|
|
|
try {
|
|
// Exhaust the transport back-off so the query settles in error.
|
|
await vi.waitFor(async () => {
|
|
await act(() => vi.advanceTimersByTimeAsync(TRANSPORT_RECOVERY_MS));
|
|
expect(result.current.sync.transportError).toBe(true);
|
|
});
|
|
expect(gets).toBe(TRANSPORT_ATTEMPTS);
|
|
|
|
await act(async () => result.current.sync.retry());
|
|
await vi.waitFor(async () => {
|
|
await act(() => vi.advanceTimersByTimeAsync(0));
|
|
expect(result.current.accounts.data).toEqual([]);
|
|
});
|
|
}
|
|
finally {
|
|
vi.useRealTimers();
|
|
}
|
|
|
|
expect(gets).toBe(TRANSPORT_ATTEMPTS + 1);
|
|
expect(syncModes).toEqual(Array.from({ length: gets }, () => null));
|
|
});
|
|
|
|
it("refreshes the currently open resources together", async () => {
|
|
auth();
|
|
let gets = 0;
|
|
server.use(
|
|
http.get("*/api/v1/brokerage-accounts", () => {
|
|
gets += 1;
|
|
|
|
return HttpResponse.json(resource([]));
|
|
}),
|
|
);
|
|
|
|
const { result } = renderHook(
|
|
() => {
|
|
const accounts = useBrokerageAccounts();
|
|
|
|
return useScreenSync({ screen: "accounts" }, [accounts]);
|
|
},
|
|
{ wrapper: createQueryWrapper().wrapper },
|
|
);
|
|
|
|
await waitFor(() => expect(gets).toBe(1));
|
|
await act(async () => result.current.refresh());
|
|
await waitFor(() => expect(gets).toBe(2));
|
|
});
|
|
|
|
it("does not manually refetch disabled resources", async () => {
|
|
vi.useFakeTimers();
|
|
const refetch = vi.fn(() => Promise.resolve());
|
|
|
|
const resource = {
|
|
isEnabled: false,
|
|
isError: true,
|
|
isFetching: false,
|
|
queryKey: [],
|
|
refetch,
|
|
};
|
|
|
|
try {
|
|
const { result, unmount } = renderHook(() =>
|
|
useScreenSync({ screen: "accounts" }, [resource]),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.refresh();
|
|
await result.current.retry();
|
|
});
|
|
await act(() => vi.advanceTimersByTime(1_800_000));
|
|
|
|
expect(refetch).not.toHaveBeenCalled();
|
|
unmount();
|
|
}
|
|
finally {
|
|
vi.useRealTimers();
|
|
}
|
|
});
|
|
});
|