refactor: enforce frontend view model boundaries
Frontend verify / commits (pull_request) Successful in 28s
Frontend verify / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 8m19s

This commit is contained in:
2026-09-28 18:48:23 +03:00
parent 5b69003516
commit ca0ad4f824
287 changed files with 8558 additions and 4538 deletions
@@ -8,8 +8,10 @@ import type { Portfolio } from "@/entities/portfolio";
import { toAccountPagePositionSections } from "./AccountPage.vm";
import { PositionSection } from "./components/PositionSection.view";
const position: Portfolio["positions"][number] = (/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
const position: Portfolio["positions"][number]
= {
absoluteReturn: { currency: "RUB", value: "25" },
account: { id: "a", name: "Основной" },
averagePurchasePrice: { currency: "RUB", value: "280" },
@@ -21,18 +23,21 @@ const position: Portfolio["positions"][number] = (/* eslint-disable unicorn/no-n
quantity: "1.25",
ticker: "SBER",
type: "STOCK",
}
/* eslint-enable unicorn/no-null */);
const second = (/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{
};
/* eslint-enable unicorn/no-null */
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
const second
= {
...position,
absoluteReturn: null,
account: { id: "b", name: "ИИС" },
averagePurchasePrice: null,
currentValue: null,
quantity: "-2",
}
/* eslint-enable unicorn/no-null */);
};
/* eslint-enable unicorn/no-null */
function sections(positions: Portfolio["positions"]) {
return toAccountPagePositionSections({
cashBalances: [],
@@ -55,9 +60,11 @@ describe("Positions grouped by security", () => {
</MantineProvider>,
);
expect(screen.getAllByText("Сбербанк")).toHaveLength(1);
const rows = within(
screen.getByRole("list", { name: "Позиции по счетам" }),
).getAllByRole("listitem");
expect(rows).toHaveLength(2);
expect(
within(rows[0]).getByRole("link", { name: "Основной" }),
@@ -77,26 +84,31 @@ describe("Positions grouped by security", () => {
it("uses ISIN only when FIGI is absent, never merging by name or ticker", () => {
expect(
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
sections([
(/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{ ...position, figi: null }
/* eslint-enable unicorn/no-null */),
(/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{ ...second, figi: null }
/* eslint-enable unicorn/no-null */),
{
...position,
figi: null,
},
/* eslint-enable unicorn/no-null */ { ...second, figi: null }, // eslint-disable-line unicorn/no-null -- Preserve explicit null in the API fixture.
])[0].groups,
).toHaveLength(1);
expect(
sections([position, { ...second, figi: "OTHER-FIGI" }])[0].groups,
).toHaveLength(2);
expect(
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
sections([
(/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{ ...position, figi: null, isin: null }
/* eslint-enable unicorn/no-null */),
(/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{ ...second, figi: null, isin: null }
/* eslint-enable unicorn/no-null */),
{
...position,
figi: null,
isin: null,
},
/* eslint-enable unicorn/no-null */ {
...second,
figi: null, // eslint-disable-line unicorn/no-null -- Preserve explicit null in the API fixture.
isin: null, // eslint-disable-line unicorn/no-null -- Preserve explicit null in the API fixture.
},
])[0].groups,
).toHaveLength(2);
});
@@ -122,6 +134,7 @@ describe("Positions grouped by security", () => {
currentValue: { currency: "USD", value: "1.01" },
},
];
const before = structuredClone(source);
const [group] = sections(source)[0].groups;
expect(