import { MantineProvider } from "@mantine/core"; import { render, screen, within } from "@testing-library/react"; import { MemoryRouter } from "react-router"; import { describe, expect, it } from "vitest"; import type { BrokerageOperation } from "../api/useBrokerageOperations"; import { OperationGroup } from "./OperationGroup"; function operation( id: string, account: { id: string; name: string }, value: null | string, currency = "RUB", ): BrokerageOperation { return { account, accruedInterest: null, category: "income", commission: null, description: "Купон", figi: "FIGI", id, instrumentType: "bond", instrumentUid: "UID", logoName: null, occurredAt: "2026-08-14T10:00:00Z", payment: value === null ? null : { currency, value }, price: null, quantity: null, ticker: "BOND", type: "OPERATION_TYPE_COUPON", }; } function renderGroup( operations: BrokerageOperation[], isTotalVisible = true, ) { return render( , ); } describe("OperationGroup", () => { it("shows the received coupon and positive totals in flat account rows", () => { renderGroup([ { ...operation("1", { id: "broker", name: "Брокерский" }, "0.1"), description: "Выплата купонов СФО ВТБ РКС Эталон 04" }, operation("2", { id: "iis", name: "ИИС" }, "0.2"), ]); expect(screen.getByText("Выплата купонов СФО ВТБ РКС Эталон 04")).toBeInTheDocument(); expect(screen.getByText("+0,3 ₽")).toBeInTheDocument(); const accounts = within(screen.getByRole("list", { name: /Выплата купонов СФО ВТБ РКС Эталон 04, Исполнено 14\.08\.2026.*, \+0,3 ₽/, })); expect(accounts.getByRole("listitem", { name: "Брокерский, +0,1 ₽" })) .toHaveTextContent("Купон"); expect(accounts.getByText("+0,1 ₽")).toHaveClass(/positive/); expect(accounts.getByRole("listitem", { name: "ИИС, +0,2 ₽" })) .toHaveTextContent("Купон"); expect(accounts.getByText("+0,2 ₽")).toHaveClass(/positive/); expect(accounts.getByRole("listitem", { name: "ИИС, +0,2 ₽" })) .not.toHaveClass("EntryGroup_accountCardRow"); expect(accounts.queryByText(/Исполнено/)).not.toBeInTheDocument(); expect(accounts.queryByRole("link")).not.toBeInTheDocument(); }); it("announces the hidden total as unavailable", () => { renderGroup([ operation("1", { id: "broker", name: "Брокерский" }, "100"), operation("2", { id: "iis", name: "ИИС" }, null), ]); expect(screen.getByRole("list", { name: /Выплата купонов BOND, Исполнено .*Итог недоступен/, })).toBeInTheDocument(); expect(screen.getByText("Итог недоступен")).toBeInTheDocument(); expect(screen.getByText("—")).not.toHaveClass(/positive/); }); it.each(["0", "0.000"])( "hides the whole total for a zero source amount (%s)", (value) => { renderGroup([ operation("1", { id: "broker", name: "Брокерский" }, value), operation("2", { id: "iis", name: "ИИС" }, "100"), ]); expect(screen.getAllByText("+100 ₽")).toHaveLength(1); expect(screen.getByText("Итог недоступен")).toBeInTheDocument(); }, ); it("hides the total for incomplete data but keeps compensated zero", () => { const { unmount } = renderGroup([ operation("1", { id: "broker", name: "Брокерский" }, "100"), operation("2", { id: "iis", name: "ИИС" }, "100"), ], false); expect(screen.queryByText("+200 ₽")).not.toBeInTheDocument(); unmount(); renderGroup([ operation("1", { id: "broker", name: "Брокерский" }, "100"), operation("2", { id: "iis", name: "ИИС" }, "-100"), ]); expect(screen.getByText("0 ₽")).toBeInTheDocument(); expect(screen.getByText("−100 ₽")).toHaveClass(/negative/); }); it("renders repeated ids inside one group as separate rows", () => { renderGroup([ operation("same", { id: "broker", name: "Брокерский" }, "10"), operation("same", { id: "iis", name: "ИИС" }, "20"), ]); expect(screen.getByText("+30 ₽")).toBeInTheDocument(); const accounts = within(screen.getByRole("list", { name: /Выплата купонов/ })); expect(accounts.getAllByText("Брокерский")).toHaveLength(1); expect(accounts.getAllByText("ИИС")).toHaveLength(1); expect(accounts.getAllByRole("listitem")).toHaveLength(2); }); });