Files
budget2.0/src/ui/components/BudgetTree.test.tsx
T

51 lines
3.7 KiB
TypeScript

import { expect, test } from "bun:test";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
if (!GlobalRegistrator.isRegistered) GlobalRegistrator.register();
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
import { act } from "react";
import { createRoot } from "react-dom/client";
import { MemoryRouter } from "react-router";
import { createSeedDataset } from "../../domain/seed.ts";
import { addBudgetFolder, addRecurringCost, confirmOccurrence } from "../../store/actions.ts";
import { monthView } from "../../domain/views.ts";
import { store } from "../../store/index.ts";
import { BudgetTree } from "./BudgetTree.tsx";
function setup(run: (container: HTMLDivElement, billId: string) => void, confirm = false) {
const data = createSeedDataset("2026-09");
const housing = addBudgetFolder(data, "Housing", null);
const utilities = addBudgetFolder(data, "Utilities", housing.id); utilities.order = 0;
const post = data.posts[0]!; post.name = "Rent post"; post.folderId = housing.id; post.order = 1;
const bill = addRecurringCost(data, { name: "Rent bill", folderId: housing.id, archived: false, amount: { amount: 8000, currency: "DKK" }, startDate: "2026-09", recurrence: { kind: "everyNMonths", n: 18 }, anchoring: "calendar", splitMode: "percent", splits: [{ postId: post.id, value: 100, absorbsRemainder: true }] }); bill.order = 2;
addRecurringCost(data, { ...bill, name: "Ended bill", folderId: null, endedFrom: "2026-08" });
if (confirm) confirmOccurrence(data, bill.id, "2026-09", { date: "2026-09-01" });
const original = { get: store.get, subscribe: store.subscribe }; store.get = () => data; store.subscribe = () => () => {};
const container = document.createElement("div"); document.body.append(container); const root = createRoot(container);
try { act(() => root.render(<MemoryRouter><BudgetTree monthId="2026-09" rows={monthView(data, "2026-09").rows} /></MemoryRouter>)); run(container, bill.id); }
finally { act(() => root.unmount()); container.remove(); Object.assign(store, original); }
}
function housing(container: HTMLDivElement) { return container.querySelector('button[aria-label="Housing"]') as HTMLButtonElement; }
test("collapsed folder keeps Bills due", () => setup(container => {
expect(housing(container).getAttribute("aria-expanded")).toBe("false");
expect(housing(container).parentElement!.textContent).toContain("8,000");
expect(housing(container).parentElement!.textContent).toContain("Bills due");
expect(container.querySelector('[data-budget-name="Rent post"]')).toBeNull();
}));
test("expanded tree preserves mixed order", () => setup(container => {
act(() => housing(container).click());
for (const scope of [container.querySelector("table")!, container.querySelector("ul")!]) {
const names = Array.from(scope.querySelectorAll("[data-budget-item]")).map(item => item.getAttribute("data-budget-name"));
expect(names.slice(0, 4)).toEqual(["Housing", "Utilities", "Rent post", "Rent bill"]);
}
expect((container.querySelector('table [data-budget-name="Rent post"] td') as HTMLElement).style.paddingInlineStart).toBe("16px");
expect((container.querySelector('ul [data-budget-name="Rent post"]') as HTMLElement).style.paddingInlineStart).toBe("16px");
expect(container.textContent).toContain("Unfiled"); expect(container.textContent).toContain("Social");
expect(container.querySelector("table > div")).toBeNull();
}));
test("bill edit reaches its settings row", () => setup((container, billId) => {
act(() => housing(container).click());
expect(container.querySelector(`[data-budget-item="${billId}"] a`)!.getAttribute("href")).toBe(`/settings#bill-${billId}`);
expect(container.textContent).toContain("Ended");
expect(container.textContent).toContain("Next 2028-03");
}, true));