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()); 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));