refactor: align frontend ownership boundaries

Close audit gaps across API drift, query policy, UI, and tests.
This commit is contained in:
2026-09-06 18:20:05 +03:00
parent 175727bc93
commit c97f62bd98
118 changed files with 2190 additions and 2254 deletions
@@ -1,13 +1,14 @@
import { MantineProvider } from "@mantine/core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { cleanup, render, screen, waitFor } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import { HttpResponse, http } from "msw";
import type { FC, PropsWithChildren } from "react";
import { MemoryRouter, Route, Routes } from "react-router";
import { afterEach, describe, expect, it, vi } from "vitest";
import { describe, expect, it, vi } from "vitest";
import type { IncomeAnalytics } from "@/entities/income";
import { configureAuthClient } from "@/shared/api";
import { createQueryWrapper } from "@/test/query";
import { server } from "@/test/server";
import { AccountIncomePage } from "./AccountIncomePage";
import { isDate, toIncomeDisplayModel } from "./AccountIncomePage.vm";
const analytics = {
accountId: "account-1",
@@ -41,7 +42,7 @@ const analytics = {
},
],
entries: [entry()],
};
} satisfies IncomeAnalytics;
function money(currency: string, value: string) {
return { currency, value };
@@ -65,7 +66,7 @@ function comparison(currency: string) {
};
}
function entry() {
function entry(): IncomeAnalytics["entries"][number] {
return {
id: "coupon-1",
occurredAt: "2026-08-01T12:00:00Z",
@@ -91,36 +92,45 @@ function renderPage() {
refresh: async () => true,
clear: vi.fn(),
});
const Wrapper: FC<PropsWithChildren> = (props) => (
<MantineProvider>
<QueryClientProvider client={new QueryClient()}>
{props.children}
</QueryClientProvider>
</MantineProvider>
);
const { wrapper: QueryWrapper } = createQueryWrapper();
return render(
<Wrapper>
<MemoryRouter
initialEntries={[
"/accounts/account-1/income?from=2026-08-01&to=2026-08-27",
]}
>
<Routes>
<Route
path="/accounts/:accountId/income"
element={<AccountIncomePage />}
/>
<Route path="/accounts/:accountId/events" element={<h1>Events</h1>} />
</Routes>
</MemoryRouter>
</Wrapper>,
<MantineProvider>
<QueryWrapper>
<MemoryRouter
initialEntries={[
"/accounts/account-1/income?from=2026-08-01&to=2026-08-27",
]}
>
<Routes>
<Route
path="/accounts/:accountId/income"
element={<AccountIncomePage />}
/>
<Route
path="/accounts/:accountId/events"
element={<h1>Events</h1>}
/>
</Routes>
</MemoryRouter>
</QueryWrapper>
</MantineProvider>,
);
}
describe("AccountIncomePage", () => {
afterEach(() => {
cleanup();
configureAuthClient(null);
it("rejects malformed calendar dates without throwing", () => {
expect(isDate("2026-99-99")).toBe(false);
expect(isDate("2026-02-29")).toBe(false);
expect(isDate("2024-02-29")).toBe(true);
});
it("groups income entries once in the view model", () => {
const display = toIncomeDisplayModel(analytics);
expect(display.instruments[0].entries).toEqual([analytics.entries[0]]);
expect(display.trend[0].buckets[0].total).toEqual(
analytics.buckets[0].totalsByCurrency[0],
);
});
it("renders realized analytics, drill-down, and the independent forecast", async () => {