fix: follow-ups of the lot-v018 stack reviews #482

Merged
maximus merged 25 commits from review-followups-lot-v018 into main 2026-10-21 00:31:46 +00:00
3 changed files with 43 additions and 0 deletions
Showing only changes of commit 7f25293669 - Show all commits

View file

@ -359,6 +359,11 @@ describe("buildBudgetYearRows — a category whose parent is deleted or gone (#3
);
expect(rows.some((r) => r.category_id === 7 || r.category_id === 8)).toBe(false);
});
it("hides a category that is its own parent too, a loop of one member", () => {
const rows = buildBudgetYearRows([...CATEGORIES, cat(7, "Gifts", "expense", { parent_id: 7 })], [entry(7, 40)], []);
expect(rows.some((r) => r.category_id === 7)).toBe(false);
});
});
describe("buildBudgetYearRows + multi-level collapse (issue #289 end-to-end)", () => {

View file

@ -202,6 +202,35 @@ describe("CategoriesPage — « Vérifier la hiérarchie » (#393)", () => {
expect(render()).toContain("t(categories.hierarchy.errors.too_deep)");
});
it("hands the window every category, not the tree: an orphan in a loop is named", () => {
const node = (id: number, name: string, parent_id: number | null) => ({
id,
name,
parent_id,
color: null,
icon: null,
type: "expense",
is_active: true,
is_inputable: true,
sort_order: id,
keyword_count: 0,
i18n_key: null,
children: [],
});
// 3 <-> 5: no root reaches them, so the tree holds root 1 (and 10 under it) only.
const categories = [node(1, "Logement", null), node(10, "Loyer", 1), node(3, "Cadeaux", 5), node(5, "Fêtes", 3)];
harness.state = {
isLoading: false,
categories,
tree: [{ ...categories[0], children: [categories[1]] }],
hierarchyCheck: { orphans: [{ id: 3, reason: "cycle" }], error: null },
};
const html = render();
expect(html).toContain(">Cadeaux</p>");
expect(html).toContain('aria-label="t(categories.hierarchy.newParentFor[name=Cadeaux])"');
expect(html).not.toContain(">—</p>");
});
it("translates a refused save of the form by its key, and shows any other failure as is", () => {
harness.state = { error: { key: "categories.hierarchy.errors.parent_has_transactions" } };
expect(render()).toContain(">t(categories.hierarchy.errors.parent_has_transactions)</div>");

View file

@ -113,6 +113,10 @@ describe("displaysAsRoot", () => {
expect(displaysAsRoot({ id: 10, parent_id: 1 }, ids)).toBe(false);
expect(displaysAsRoot({ id: 1, parent_id: 2 }, ids)).toBe(false);
});
it("is not a root as its own parent: that one-member loop stays hidden, like the tree hides it", () => {
expect(displaysAsRoot({ id: 10, parent_id: 10 }, ids)).toBe(false);
});
});
describe("isActiveRow", () => {
@ -139,6 +143,11 @@ describe("findOrphans (#393)", () => {
expect(findOrphans(rows)).toEqual([{ id: 3, reason: "cycle" }]);
});
it("names the loop by its own smallest member, never by a smaller id that hangs under it", () => {
// 5 <-> 8, 2 under 5: reattaching 2 would leave 5 and 8 hidden.
expect(findOrphans([r(2, 5), r(5, 8), r(8, 5), r(1, null)])).toEqual([{ id: 5, reason: "cycle" }]);
});
it("lists a three-member cycle once, by its smallest id, whatever the order of the rows", () => {
// 6 -> 4 -> 9 -> 6, 12 under 9.
const rows = [r(6, 4), r(4, 9), r(9, 6), r(12, 9), r(1, null)];