156 lines
4.2 KiB
TypeScript
156 lines
4.2 KiB
TypeScript
import { MantineProvider } from "@mantine/core";
|
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import { http, HttpResponse } from "msw";
|
|
import { MemoryRouter, Route, Routes } from "react-router";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { configureAuthClient } from "@/shared/api";
|
|
import { createTestQueryClient } from "@/test/query";
|
|
import { resource } from "@/test/resource";
|
|
import { server } from "@/test/server";
|
|
|
|
import { AccountEventsPage } from "./AccountEventsPage";
|
|
|
|
// Automatic read retries wait 1s + 2s before the failure is final.
|
|
const RETRIES_EXHAUSTED_MS = 6000;
|
|
const TEST_MS = 10_000;
|
|
|
|
const snapshot = resource({
|
|
events: [
|
|
{
|
|
date: "2026-10-01",
|
|
dateKind: "event",
|
|
displayName: "Synthetic coupon",
|
|
figi: null,
|
|
instrumentUid: null,
|
|
isin: null,
|
|
logoName: null,
|
|
projectedAmount: { currency: "RUB", value: "123.45" },
|
|
ticker: "SYN",
|
|
type: "coupon",
|
|
},
|
|
],
|
|
incomeTotalsByCurrency: [{ currency: "RUB", value: "123.45" }],
|
|
});
|
|
|
|
function renderEvents() {
|
|
configureAuthClient({
|
|
accessToken: () => "token",
|
|
clear: vi.fn(),
|
|
refresh: () => Promise.resolve(true),
|
|
});
|
|
const queryClient = createTestQueryClient();
|
|
|
|
render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<MantineProvider env="test">
|
|
<MemoryRouter initialEntries={["/accounts/account-1/events"]}>
|
|
<Routes>
|
|
<Route
|
|
element={<AccountEventsPage />}
|
|
path="/accounts/:accountId/events"
|
|
/>
|
|
</Routes>
|
|
</MemoryRouter>
|
|
</MantineProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
return { queryClient };
|
|
}
|
|
|
|
describe("AccountEventsPage read retry", () => {
|
|
it(
|
|
"offers retry after an initial failure and recovers in place",
|
|
{ timeout: TEST_MS },
|
|
async () => {
|
|
let isFailing = true;
|
|
let requests = 0;
|
|
server.use(
|
|
http.get("*/api/v1/brokerage-accounts/:accountId/events", () => {
|
|
requests += 1;
|
|
|
|
return isFailing
|
|
? HttpResponse.json({ code: "TBANK_UNAVAILABLE" }, { status: 503 })
|
|
: HttpResponse.json(snapshot);
|
|
}),
|
|
);
|
|
renderEvents();
|
|
|
|
const retry = await screen.findByRole(
|
|
"button",
|
|
{ name: "Повторить" },
|
|
{ timeout: RETRIES_EXHAUSTED_MS },
|
|
);
|
|
|
|
expect(requests).toBe(3);
|
|
|
|
isFailing = false;
|
|
fireEvent.click(retry);
|
|
|
|
expect(await screen.findByText("Купон · Synthetic coupon")).toBeInTheDocument();
|
|
expect(requests).toBe(4);
|
|
},
|
|
);
|
|
|
|
it(
|
|
"keeps cached events, warns and retries the same GET",
|
|
{ timeout: TEST_MS },
|
|
async () => {
|
|
let isFailing = false;
|
|
let requests = 0;
|
|
server.use(
|
|
http.get("*/api/v1/brokerage-accounts/:accountId/events", () => {
|
|
requests += 1;
|
|
|
|
return isFailing
|
|
? HttpResponse.json({ code: "TBANK_UNAVAILABLE" }, { status: 503 })
|
|
: HttpResponse.json(snapshot);
|
|
}),
|
|
);
|
|
const { queryClient } = renderEvents();
|
|
await screen.findByText("Купон · Synthetic coupon");
|
|
expect(screen.queryByRole("button", { name: "Повторить" }))
|
|
.not.toBeInTheDocument();
|
|
|
|
isFailing = true;
|
|
void queryClient.invalidateQueries({
|
|
queryKey: ["get", "/api/v1/brokerage-accounts/{accountId}/events"],
|
|
});
|
|
|
|
const retry = await screen.findByRole(
|
|
"button",
|
|
{ name: "Повторить" },
|
|
{ timeout: RETRIES_EXHAUSTED_MS },
|
|
);
|
|
|
|
expect(screen.getByText("Не удалось обновить данные")).toBeInTheDocument();
|
|
expect(screen.getByText("Купон · Synthetic coupon")).toBeInTheDocument();
|
|
expect(requests).toBe(4);
|
|
|
|
isFailing = false;
|
|
fireEvent.click(retry);
|
|
|
|
await waitFor(() =>
|
|
expect(screen.queryByText("Не удалось обновить данные"))
|
|
.not.toBeInTheDocument());
|
|
expect(requests).toBe(5);
|
|
},
|
|
);
|
|
|
|
it("does not offer a plain retry when the account is gone", async () => {
|
|
server.use(
|
|
http.get("*/api/v1/brokerage-accounts/:accountId/events", () =>
|
|
HttpResponse.json({ code: "NOT_FOUND" }, { status: 404 })),
|
|
);
|
|
renderEvents();
|
|
|
|
await waitFor(() =>
|
|
expect(screen.queryByRole("heading", { name: "Загрузка" }))
|
|
.not.toBeInTheDocument());
|
|
expect(screen.queryByRole("button", { name: "Повторить" }))
|
|
.not.toBeInTheDocument();
|
|
});
|
|
});
|