refactor: align frontend ownership boundaries
Close audit gaps across API drift, query policy, UI, and tests.
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user