166 lines
4.6 KiB
TypeScript
166 lines
4.6 KiB
TypeScript
import { MantineProvider } from "@mantine/core";
|
||
import { fireEvent, render, screen } from "@testing-library/react";
|
||
import { MemoryRouter, Route, Routes } from "react-router";
|
||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||
import { useBrokerageEvents } from "@/entities/brokerage-event";
|
||
import { AccountEventsPage } from "./AccountEventsPage";
|
||
|
||
vi.mock("@/entities/brokerage-event", async (importOriginal) => ({
|
||
...(await importOriginal<typeof import("@/entities/brokerage-event")>()),
|
||
useBrokerageEvents: vi.fn(),
|
||
}));
|
||
|
||
const query = vi.mocked(useBrokerageEvents);
|
||
|
||
function renderPage() {
|
||
return render(
|
||
<MantineProvider>
|
||
<MemoryRouter>
|
||
<AccountEventsPage />
|
||
</MemoryRouter>
|
||
</MantineProvider>,
|
||
);
|
||
}
|
||
|
||
describe("AccountEventsPage", () => {
|
||
beforeEach(() => {
|
||
query.mockReturnValue({
|
||
data: {
|
||
accountId: "account",
|
||
calculatedAt: "2026-08-14T00:00:00Z",
|
||
events: [],
|
||
},
|
||
isError: false,
|
||
isLoading: false,
|
||
refetch: vi.fn(),
|
||
} as never);
|
||
});
|
||
|
||
it("passes the selected route account to the query", () => {
|
||
render(
|
||
<MantineProvider>
|
||
<MemoryRouter initialEntries={["/accounts/selected-account/events"]}>
|
||
<Routes>
|
||
<Route
|
||
path="/accounts/:accountId/events"
|
||
element={<AccountEventsPage />}
|
||
/>
|
||
</Routes>
|
||
</MemoryRouter>
|
||
</MantineProvider>,
|
||
);
|
||
expect(query).toHaveBeenCalledWith("selected-account");
|
||
});
|
||
|
||
it("keeps the loading fallback when no result or account is available", () => {
|
||
query.mockReturnValue({
|
||
data: undefined,
|
||
isError: false,
|
||
isLoading: false,
|
||
refetch: vi.fn(),
|
||
} as never);
|
||
renderPage();
|
||
expect(query).toHaveBeenCalledWith(undefined);
|
||
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",
|
||
projectedAmount: { currency: "RUB", value: "1250" },
|
||
ticker: "OFZ",
|
||
type: "coupon",
|
||
logoName: "ofz.png",
|
||
isin: "RU000A0JX0J2",
|
||
},
|
||
{
|
||
date: "2026-08-25",
|
||
dateKind: "event",
|
||
displayName: "ОФЗ 26238",
|
||
figi: "FIGI",
|
||
instrumentUid: "UID",
|
||
projectedAmount: null,
|
||
ticker: "OFZ",
|
||
type: "offer",
|
||
logoName: null,
|
||
isin: null,
|
||
},
|
||
],
|
||
},
|
||
isError: false,
|
||
isLoading: false,
|
||
refetch: vi.fn(),
|
||
} as never);
|
||
const { container } = 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 авг. 2026 г. · RU000A0JX0J2")).toBeVisible();
|
||
expect(screen.getAllByText(/RU000A0JX0J2$/)).toHaveLength(1);
|
||
expect(screen.getByText("25 авг. 2026 г.")).toBeVisible();
|
||
expect(screen.queryByText(/Дата события/)).not.toBeInTheDocument();
|
||
expect(screen.getAllByText("Ожидаемая сумма")).toHaveLength(2);
|
||
const logo = container.querySelector("img");
|
||
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(container.querySelector("img")).toBeNull();
|
||
expect(screen.getAllByText("O")).toHaveLength(2);
|
||
});
|
||
|
||
it("renders empty and retryable error states", () => {
|
||
const refetch = vi.fn();
|
||
const { rerender } = renderPage();
|
||
expect(
|
||
screen.getByText("На ближайшие 30 дней событий не найдено"),
|
||
).toBeInTheDocument();
|
||
query.mockReturnValue({
|
||
data: undefined,
|
||
isError: true,
|
||
isLoading: false,
|
||
refetch,
|
||
} as never);
|
||
rerender(
|
||
<MantineProvider>
|
||
<MemoryRouter>
|
||
<AccountEventsPage />
|
||
</MemoryRouter>
|
||
</MantineProvider>,
|
||
);
|
||
screen.getByRole("button", { name: "Повторить" }).click();
|
||
expect(refetch).toHaveBeenCalledOnce();
|
||
});
|
||
|
||
it("shows a structural busy state before events are available", () => {
|
||
query.mockReturnValue({
|
||
data: undefined,
|
||
isError: false,
|
||
isLoading: true,
|
||
refetch: vi.fn(),
|
||
} as never);
|
||
|
||
renderPage();
|
||
|
||
const status = screen.getByRole("status", { name: "Загрузка" });
|
||
expect(status).toHaveAttribute("aria-busy", "true");
|
||
expect(status.querySelector("button")).toBeNull();
|
||
});
|
||
});
|