test: await client settlement in late-response cases (WealthLedger/frontend#61) #81
@@ -88,7 +88,6 @@ const quote
|
||||
function setup(
|
||||
options: {
|
||||
ambiguousTicker?: boolean;
|
||||
failBond?: boolean;
|
||||
failCoupons?: boolean;
|
||||
noPosition?: boolean;
|
||||
path?: string;
|
||||
@@ -100,11 +99,7 @@ function setup(
|
||||
const resolved = { ...bond, slug: options.slug ?? uid };
|
||||
let isFailQuote = false;
|
||||
server.use(
|
||||
http.get("*/api/v1/bonds/:uid", () =>
|
||||
options.failBond
|
||||
? HttpResponse.json({ code: "NOT_FOUND" }, { status: 404 })
|
||||
: HttpResponse.json(resource(resolved)),
|
||||
),
|
||||
http.get("*/api/v1/bonds/:uid", () => HttpResponse.json(resource(resolved))),
|
||||
http.get("*/api/v1/bonds/by-ticker/:ticker", ({ request }) => {
|
||||
requests.push(new URL(request.url));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { http, HttpResponse } from "msw";
|
||||
import { MemoryRouter } from "react-router";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
@@ -12,19 +12,37 @@ import { server } from "@/test/server";
|
||||
import { StocksPage } from "./StocksPage";
|
||||
import { useStocksPageVm } from "./StocksPage.vm";
|
||||
|
||||
const pageOperations: Promise<void>[] = [];
|
||||
|
||||
const StocksPageVmProbe = () => {
|
||||
const vm = useStocksPageVm();
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={vm.onSearch}>search</button>
|
||||
<button onClick={() => vm.onPage(2)}>page</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
pageOperations.push(vm.search());
|
||||
}}
|
||||
>
|
||||
search
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
pageOperations.push(vm.changePage(2));
|
||||
}}
|
||||
>
|
||||
page
|
||||
</button>
|
||||
<output>{vm.items.map(item => item.name).join(", ")}</output>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
afterEach(() => vi.useRealTimers());
|
||||
afterEach(() => {
|
||||
pageOperations.length = 0;
|
||||
|
||||
return vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe("stock search", () => {
|
||||
it("waits for a meaningful query and uses the server-verified ticker link", async () => {
|
||||
@@ -230,9 +248,12 @@ describe("stock search", () => {
|
||||
fireEvent.click(screen.getByRole("button", { name: "page" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "search" }));
|
||||
await screen.findByText("Новый");
|
||||
oldPage.resolve();
|
||||
await act(async () => {
|
||||
oldPage.resolve();
|
||||
await pageOperations[0];
|
||||
});
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Новый")).toBeVisible());
|
||||
expect(screen.getByText("Новый")).toBeVisible();
|
||||
expect(screen.queryByText("Старый результат")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ function useStocksPageVm() {
|
||||
};
|
||||
|
||||
return {
|
||||
changePage,
|
||||
criteria,
|
||||
error,
|
||||
items: page?.items ?? [],
|
||||
@@ -111,6 +112,8 @@ function useStocksPageVm() {
|
||||
onSearch: () => void search(),
|
||||
page: page?.page,
|
||||
resultTime: page?.asOf,
|
||||
// Test seam: settle the actual operation instead of the handler release.
|
||||
search,
|
||||
searched: page !== null,
|
||||
totalPages: page ? page.page + Number(page.hasNext) : 0,
|
||||
};
|
||||
|
||||
@@ -164,10 +164,16 @@ describe("persistent resource query lifecycle", () => {
|
||||
});
|
||||
|
||||
await waitFor(() => expect(isStarted).toBe(true));
|
||||
await act(() => result.current.observe());
|
||||
// Capture the observation promise before releasing the old handler; the
|
||||
// final assertion must follow its settlement, not the handler release.
|
||||
let observation: Promise<unknown> = Promise.resolve();
|
||||
await act(async () => {
|
||||
observation = result.current.observe() ?? Promise.resolve();
|
||||
await observation;
|
||||
});
|
||||
await act(async () => {
|
||||
release();
|
||||
await blocked;
|
||||
await observation;
|
||||
});
|
||||
expect(result.current.data).toEqual(snapshot);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user