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
@@ -24,8 +24,10 @@ import { server } from "@/test/server";
import { AccountOperationsPage } from "./AccountOperationsPage";
const operation: BrokerageOperation = (/* 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 operation: BrokerageOperation
= {
accruedInterest: null,
category: "trade",
commission: { currency: "RUB", value: "1" },
@@ -41,18 +43,21 @@ const operation: BrokerageOperation = (/* eslint-disable unicorn/no-null -- Fixt
quantity: "2",
ticker: "SBER",
type: "OPERATION_TYPE_BUY",
}
/* eslint-enable unicorn/no-null */);
};
/* eslint-enable unicorn/no-null */
const Navigation: FC = () => {
const location = useLocation();
const navigate = useNavigate();
return (
<>
<output aria-label="URL">{location.search}</output>
<button onClick={() => navigate(-1)} type="button">
History back
</button>
<button onClick={() => navigate(1)} type="button">
History forward
</button>
@@ -225,6 +230,7 @@ describe("AccountOperationsPage", () => {
"*/api/v1/brokerage-accounts/:accountId/operations",
({ request }) => {
searches.push(new URL(request.url).searchParams);
return HttpResponse.json(
resource({
hasNext: true,
@@ -249,6 +255,7 @@ describe("AccountOperationsPage", () => {
initialIndex={0}
>
<Navigation />
<Routes>
<Route
element={<AccountOperationsPage />}
@@ -262,6 +269,7 @@ describe("AccountOperationsPage", () => {
await screen.findByLabelText("Период");
expect(searches[0].get("from")).toBeNull();
expect(searches[0].get("to")).toBeNull();
for (const expected of [
"?categories=trade",
"?categories=trade",
@@ -276,6 +284,7 @@ describe("AccountOperationsPage", () => {
);
await screen.findByLabelText("Период");
}
expect(searches.at(-1)?.get("from")).toBe("2026-02-01T00:00:00.000Z");
expect(searches.at(-1)?.get("to")).toBeNull();
fireEvent.click(screen.getByRole("button", { name: "Далее" }));
@@ -313,9 +322,11 @@ describe("AccountOperationsPage", () => {
new URL(request.url).searchParams.has("from")
? HttpResponse.json({ code: "VALIDATION_ERROR" }, { status: 400 })
: HttpResponse.json(
resource((/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{ hasNext: false, items: [], nextCursor: null }
/* eslint-enable unicorn/no-null */)),
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
resource(
{ hasNext: false, items: [], nextCursor: null },
/* eslint-enable unicorn/no-null */
),
),
),
);
@@ -328,6 +339,7 @@ describe("AccountOperationsPage", () => {
]}
>
<Navigation />
<Routes>
<Route
element={<AccountOperationsPage />}
@@ -354,16 +366,19 @@ describe("AccountOperationsPage", () => {
({ request }) => {
const url = new URL(request.url);
searches.push(url.search);
return HttpResponse.json(
resource((/* 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. */
resource(
{
accountId: "account-1",
hasNext: !url.searchParams.get("cursor"),
items: [operation],
nextCursor: url.searchParams.get("cursor") ? null : "next-1",
observedAt: "2026-08-14T00:00:00Z",
}
/* eslint-enable unicorn/no-null */)),
},
/* eslint-enable unicorn/no-null */
),
);
},
),
@@ -394,16 +409,19 @@ describe("AccountOperationsPage", () => {
({ request }) => {
const url = new URL(request.url);
searches.push(url.search);
return HttpResponse.json(
resource((/* 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. */
resource(
{
accountId: "account-1",
hasNext: false,
items: [operation],
nextCursor: null,
observedAt: "2026-08-14T00:00:00Z",
}
/* eslint-enable unicorn/no-null */)),
},
/* eslint-enable unicorn/no-null */
),
);
},
),
@@ -431,16 +449,19 @@ describe("AccountOperationsPage", () => {
({ request }) => {
const url = new URL(request.url);
searches.push(url.search);
return HttpResponse.json(
resource((/* 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. */
resource(
{
accountId: "account-1",
hasNext: false,
items: [operation],
nextCursor: null,
observedAt: "2026-08-14T00:00:00Z",
}
/* eslint-enable unicorn/no-null */)),
},
/* eslint-enable unicorn/no-null */
),
);
},
),
@@ -484,17 +505,26 @@ it("keeps UTC operation boundaries in GET and source sync requests", async () =>
"*/api/v1/brokerage-accounts/:accountId/operations",
({ request }) => {
requested.push(Object.fromEntries(new URL(request.url).searchParams));
return HttpResponse.json(
resource(null, (/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
resource(
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
null,
{
sync: { errorCode: "TBANK_UNAVAILABLE", id: null, state: "failed" },
}
/* eslint-enable unicorn/no-null */)),
sync: {
errorCode: "TBANK_UNAVAILABLE",
id: null,
state: "failed",
},
},
/* eslint-enable unicorn/no-null */
),
);
},
),
http.post("*/api/v1/tbank-sync", async ({ request }) => {
syncRequests.push(await request.json());
return HttpResponse.json({ code: "TBANK_UNAVAILABLE" }, { status: 503 });
}),
);
@@ -529,16 +559,15 @@ it("does not label incomplete empty operations as a confirmed empty period", asy
server.use(
http.get("*/api/v1/brokerage-accounts/:accountId/operations", () =>
HttpResponse.json(
/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
resource(
(/* eslint-disable unicorn/no-null -- Fixture preserves the production contract’s explicit null values. */
{
accountId: "account-1",
hasNext: false,
items: [],
nextCursor: null,
}
/* eslint-enable unicorn/no-null */),
{ availability: "partial" },
{
accountId: "account-1",
hasNext: false,
items: [],
nextCursor: null,
},
/* eslint-enable unicorn/no-null */ { availability: "partial" },
),
),
),
@@ -562,6 +591,7 @@ it("recovers an invalid cursor at the first page without clearing applied filter
{ code: "INVALID_OPERATIONS_CURSOR" },
{ status: 400 },
);
return HttpResponse.json(
resource({
hasNext: true,