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( } path="/accounts/:accountId/events" /> , ); 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(); }); });