118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { MantineProvider } from "@mantine/core";
|
||
import { cleanup, render, screen } from "@testing-library/react";
|
||
import { MemoryRouter } from "react-router";
|
||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
import type { Portfolio } from "../../../entities/portfolio";
|
||
import {
|
||
AccountPageEmpty,
|
||
AccountPageError,
|
||
AccountPageLoaded,
|
||
AccountPageLoading,
|
||
} from "./AccountPage";
|
||
|
||
function renderPage(ui: React.ReactNode) {
|
||
return render(
|
||
<MantineProvider>
|
||
<MemoryRouter>{ui}</MemoryRouter>
|
||
</MantineProvider>,
|
||
);
|
||
}
|
||
|
||
const portfolio: Portfolio = {
|
||
lastSuccessfulAt: "2026-08-13T10:00:00Z",
|
||
isStale: true,
|
||
totalsByCurrency: [{ currency: "RUB", value: "1234.5" }],
|
||
positions: [
|
||
{
|
||
figi: "FIGI-2",
|
||
displayName: "Вторая",
|
||
type: "STOCK",
|
||
quantity: "2",
|
||
currentValue: null,
|
||
averagePurchasePrice: null,
|
||
absoluteReturn: null,
|
||
},
|
||
{
|
||
figi: "FIGI-1",
|
||
displayName: "Первая",
|
||
type: "BOND",
|
||
quantity: "1",
|
||
currentValue: { currency: "RUB", value: "10" },
|
||
averagePurchasePrice: null,
|
||
absoluteReturn: { currency: "RUB", value: "-1" },
|
||
},
|
||
],
|
||
cashBalances: [{ currency: "USD", value: "0.5" }],
|
||
};
|
||
|
||
describe("AccountPage states", () => {
|
||
afterEach(cleanup);
|
||
it("renders loading and empty states", () => {
|
||
renderPage(<AccountPageLoading />);
|
||
expect(screen.getByRole("status")).toBeInTheDocument();
|
||
|
||
renderPage(<AccountPageEmpty />);
|
||
expect(
|
||
screen.getByRole("heading", { name: "Портфель пока пуст" }),
|
||
).toBeInTheDocument();
|
||
});
|
||
|
||
it("renders allowed error actions", () => {
|
||
const retry = vi.fn();
|
||
renderPage(<AccountPageError code="NOT_FOUND" retry={retry} />);
|
||
expect(screen.getByRole("link", { name: "К счетам" })).toHaveAttribute(
|
||
"href",
|
||
"/accounts",
|
||
);
|
||
|
||
renderPage(<AccountPageError code="TBANK_NOT_CONNECTED" retry={retry} />);
|
||
expect(
|
||
screen.getByRole("link", { name: "Открыть настройки T-Bank" }),
|
||
).toHaveAttribute("href", "/settings");
|
||
|
||
renderPage(<AccountPageError code="TBANK_UNAVAILABLE" retry={retry} />);
|
||
screen.getByRole("button", { name: "Повторить" }).click();
|
||
expect(retry).toHaveBeenCalledOnce();
|
||
});
|
||
|
||
it("renders a stale portfolio in upstream order with nullable values", () => {
|
||
renderPage(<AccountPageLoaded portfolio={portfolio} />);
|
||
|
||
expect(
|
||
screen.getByRole("heading", { level: 1, name: "Портфель" }),
|
||
).toBeInTheDocument();
|
||
expect(
|
||
screen.getByText(
|
||
"T-Bank временно недоступен. Показан последний успешный снимок.",
|
||
),
|
||
).toBeInTheDocument();
|
||
expect(screen.getByText("—")).toBeInTheDocument();
|
||
expect(screen.getByText("Доход: —")).toBeInTheDocument();
|
||
const positions = screen
|
||
.getAllByText(/Вторая|Первая/)
|
||
.map((element) => element.textContent);
|
||
expect(positions).toEqual(["Вторая", "Первая"]);
|
||
expect(screen.getByRole("heading", { name: "Акции" })).toBeInTheDocument();
|
||
expect(
|
||
screen.getByRole("heading", { name: "Облигации" }),
|
||
).toBeInTheDocument();
|
||
});
|
||
|
||
it("uses the cached account title when available", () => {
|
||
renderPage(
|
||
<AccountPageLoaded
|
||
portfolio={portfolio}
|
||
account={{
|
||
id: "account-1",
|
||
name: "Основной",
|
||
type: "BROKERAGE",
|
||
openedOn: null,
|
||
}}
|
||
/>,
|
||
);
|
||
expect(
|
||
screen.getByRole("heading", { level: 1, name: "Основной" }),
|
||
).toBeInTheDocument();
|
||
});
|
||
});
|