fix: preserve latest stock screener result
This commit is contained in:
@@ -10,6 +10,19 @@ import { createTestQueryClient } from "@/test/query";
|
|||||||
import { server } from "@/test/server";
|
import { server } from "@/test/server";
|
||||||
|
|
||||||
import { StocksPage } from "./StocksPage";
|
import { StocksPage } from "./StocksPage";
|
||||||
|
import { useStocksPageVm } from "./StocksPage.vm";
|
||||||
|
|
||||||
|
const StocksPageVmProbe = () => {
|
||||||
|
const vm = useStocksPageVm();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={vm.onSearch}>search</button>
|
||||||
|
<button onClick={() => vm.onPage(2)}>page</button>
|
||||||
|
<output>{vm.items.map(item => item.name).join(", ")}</output>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => vi.useRealTimers());
|
afterEach(() => vi.useRealTimers());
|
||||||
|
|
||||||
@@ -159,6 +172,70 @@ describe("stock search", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps a newer search when an earlier page request finishes late", async () => {
|
||||||
|
const oldPage = Promise.withResolvers<void>();
|
||||||
|
let searches = 0;
|
||||||
|
|
||||||
|
server.use(
|
||||||
|
http.post("*/api/v1/stocks/screener/search", () => {
|
||||||
|
searches += 1;
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
asOf: "2026-10-10T10:00:00Z",
|
||||||
|
hasNext: searches === 1,
|
||||||
|
items: [{
|
||||||
|
countryOfRisk: "RU",
|
||||||
|
currency: "RUB",
|
||||||
|
instrumentUid: searches === 1 ? "old" : "new",
|
||||||
|
isin: null,
|
||||||
|
logoName: null,
|
||||||
|
lot: 1,
|
||||||
|
name: searches === 1 ? "Старый" : "Новый",
|
||||||
|
realExchange: "REAL_EXCHANGE_MOEX",
|
||||||
|
sector: "financial",
|
||||||
|
shareType: "SHARE_TYPE_COMMON",
|
||||||
|
ticker: searches === 1 ? "OLD" : "NEW",
|
||||||
|
}],
|
||||||
|
page: 1,
|
||||||
|
searchId: searches === 1 ? "old-search" : "new-search",
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/v1/stocks/screener/search/old-search", async () => {
|
||||||
|
await oldPage.promise;
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
asOf: "2026-10-10T10:00:00Z",
|
||||||
|
hasNext: false,
|
||||||
|
items: [{
|
||||||
|
countryOfRisk: "RU",
|
||||||
|
currency: "RUB",
|
||||||
|
instrumentUid: "old-page",
|
||||||
|
isin: null,
|
||||||
|
logoName: null,
|
||||||
|
lot: 1,
|
||||||
|
name: "Старый результат",
|
||||||
|
realExchange: "REAL_EXCHANGE_MOEX",
|
||||||
|
sector: "financial",
|
||||||
|
shareType: "SHARE_TYPE_COMMON",
|
||||||
|
ticker: "OLD",
|
||||||
|
}],
|
||||||
|
page: 2,
|
||||||
|
searchId: "old-search",
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
render(<StocksPageVmProbe />);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "search" }));
|
||||||
|
await screen.findByText("Старый");
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "page" }));
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "search" }));
|
||||||
|
await screen.findByText("Новый");
|
||||||
|
oldPage.resolve();
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.getByText("Новый")).toBeVisible());
|
||||||
|
expect(screen.queryByText("Старый результат")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("disables filters and shows loading while searching", async () => {
|
it("disables filters and shows loading while searching", async () => {
|
||||||
const { promise, resolve } = Promise.withResolvers<void>();
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
server.use(
|
server.use(
|
||||||
|
|||||||
@@ -83,17 +83,20 @@ function useStocksPageVm() {
|
|||||||
const changePage = async (nextPage: number) => {
|
const changePage = async (nextPage: number) => {
|
||||||
if (!page) return;
|
if (!page) return;
|
||||||
|
|
||||||
|
const currentRequestId = requestIdRef.current + 1;
|
||||||
|
requestIdRef.current = currentRequestId;
|
||||||
setPending(true);
|
setPending(true);
|
||||||
setError(false);
|
setError(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setPage(await readPage(page.searchId, nextPage));
|
const result = await readPage(page.searchId, nextPage);
|
||||||
|
if (requestIdRef.current === currentRequestId) setPage(result);
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
setError(true);
|
if (requestIdRef.current === currentRequestId) setError(true);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
setPending(false);
|
if (requestIdRef.current === currentRequestId) setPending(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user