import { MantineProvider } from "@mantine/core"; import { fireEvent, render, screen, within } from "@testing-library/react"; import { MemoryRouter, Route, Routes } from "react-router"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type * as BrokerageEvents from "@/entities/brokerage-event"; import { useBrokerageEvents } from "@/entities/brokerage-event"; import { defaultPeriod } from "@/shared/lib"; import { AccountEventsPage } from "./AccountEventsPage"; const { refresh } = vi.hoisted(() => ({ refresh: vi.fn() })); vi.mock("@/features/tbank-connection", () => ({ useScreenSync: () => ({ refresh, refreshing: false, retry: refresh, sourceError: false, transportError: false, }), })); vi.mock("@/entities/brokerage-event", async importOriginal => ({ ...(await importOriginal()), useBrokerageEvents: vi.fn(), })); const query = vi.mocked(useBrokerageEvents); function renderPage() { return render( , ); } describe("AccountEventsPage", () => { beforeEach(() => { refresh.mockClear(); query.mockReturnValue( { data: { accountId: "account", calculatedAt: "2026-08-14T00:00:00Z", events: [], }, isError: false, isLoading: false, meta: { availability: "available", completeness: "complete", lastAttempt: { finishedAt: null, problems: [], startedAt: null, state: "none" }, }, refetch: vi.fn(), } as never, ); }); it("passes the selected route account to the query", () => { render( } path="/accounts/:accountId/events" /> , ); expect(query).toHaveBeenCalledWith("selected-account", { from: defaultPeriod()[0], to: defaultPeriod()[1], }); }); it("keeps the loading fallback when no result or account is available", () => { query.mockReturnValue( { data: undefined, isError: false, isLoading: false, meta: { availability: "available", completeness: "complete", lastAttempt: { finishedAt: null, problems: [], startedAt: null, state: "none" }, }, refetch: vi.fn(), } as never, ); renderPage(); expect(query).toHaveBeenCalledWith("", { from: defaultPeriod()[0], to: defaultPeriod()[1], }); expect(screen.getByRole("status", { name: "Загрузка" })).toHaveAttribute( "aria-busy", "true", ); }); it("renders events and marks an offer without an amount", () => { query.mockReturnValue( { data: { accountId: "account", calculatedAt: "2026-08-14T00:00:00Z", events: [ { date: "2026-08-20", dateKind: "event", displayName: "ОФЗ 26238", figi: "FIGI", instrumentUid: "UID", isin: "RU000A0JX0J2", logoName: "ofz.png", projectedAmount: { currency: "RUB", value: "1250" }, ticker: "OFZ", type: "coupon", }, { date: "2026-08-25", dateKind: "event", displayName: "ОФЗ 26238", figi: "FIGI", instrumentUid: "UID", isin: null, logoName: null, projectedAmount: null, ticker: "OFZ", type: "offer", }, ], }, isError: false, isLoading: false, meta: { availability: "available", completeness: "complete", lastAttempt: { finishedAt: null, problems: [], startedAt: null, state: "none" }, }, refetch: vi.fn(), } as never, ); renderPage(); expect(screen.getByText("1 250 ₽")).toBeInTheDocument(); expect(screen.getByText("Сумма неизвестна")).toBeInTheDocument(); expect(screen.getByText("Купон · ОФЗ 26238")).toBeVisible(); expect(screen.getByText("Оферта · ОФЗ 26238")).toBeVisible(); expect(screen.getByText("20.08.2026 · RU000A0JX0J2")).toBeVisible(); expect(screen.getAllByText(/RU000A0JX0J2$/)).toHaveLength(1); expect(screen.getByText("25.08.2026")).toBeVisible(); expect(screen.queryByText(/Дата события/)).not.toBeInTheDocument(); expect(screen.getAllByText("Прогноз")).toHaveLength(2); const logo = screen.queryByAltText(""); expect(logo).toHaveAttribute( "src", "https://invest-brands.cdn-tinkoff.ru/ofzx160.png", ); expect(screen.getAllByText("O")).toHaveLength(1); if (!logo) throw new Error("Expected the event logo"); fireEvent.error(logo); expect(screen.queryByAltText("")).not.toBeInTheDocument(); expect(screen.getAllByText("O")).toHaveLength(2); }); it("renders empty and quiet error states", async () => { const { rerender } = renderPage(); expect( screen.getByText("За выбранный период событий не найдено"), ).toBeInTheDocument(); query.mockReturnValue({ data: undefined, isError: true, isLoading: false, } as never); rerender( , ); expect(await screen.findByRole("button", { name: "Сбросить" })).toBeVisible(); expect(screen.queryByRole("button", { name: "Повторить" })).not.toBeInTheDocument(); }); it("shows a structural busy state before events are available", () => { query.mockReturnValue( { data: undefined, isError: false, isLoading: true, meta: { availability: "available", completeness: "complete", lastAttempt: { finishedAt: null, problems: [], startedAt: null, state: "none" }, }, refetch: vi.fn(), } as never, ); renderPage(); const status = screen.getByRole("status", { name: "Загрузка" }); expect(status).toHaveAttribute("aria-busy", "true"); expect(within(status).queryByRole("button")).not.toBeInTheDocument(); }); it("keeps a null events envelope loading instead of an empty period", () => { query.mockReturnValue( { data: null, isError: false, isLoading: false, meta: { availability: "available", completeness: "complete", lastAttempt: { finishedAt: null, problems: [], startedAt: null, state: "none" }, }, refetch: vi.fn(), } as never, ); renderPage(); expect(screen.getByRole("status", { name: "Загрузка" })).toHaveAttribute( "aria-busy", "true", ); expect( screen.queryByText("За выбранный период событий не найдено"), ).not.toBeInTheDocument(); }); });