Addresses /pr-review REQUEST_CHANGES on #255. - useCompare: the "skip first sync" boolean was not StrictMode-safe — the dev double-invoke of effects flipped the flag on setup #1, so setup #2 re-synced the reference month to the civil-year December, re-introducing the very bug Changement 2 fixes (dev only; prod has no double-invoke). Replace it with a value-change guard: a ref seeded with the initial `to` plus a pure syncReferenceOnPeriodChange() that only dispatches when `to` actually changes. Idempotent across the double-invoke, and now unit-tested (5 cases) since the decision is a pure function (the project has no renderHook harness). - Remove the now-orphaned reports.compare.totalRow i18n key (both locales) — the flat grand total it labelled was replaced by the result lines. - ComparePeriodTable: gate the "before transfers" line on results.hasTransfers (previously computed/tested but unused). - ComparePeriodChart: show the no-data empty state when the expense filter leaves nothing (a pure income/transfer period) instead of bare axes. Build + 690 vitest green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
previousMonth,
|
|
defaultReferencePeriod,
|
|
comparisonMeta,
|
|
syncReferenceOnPeriodChange,
|
|
} from "./useCompare";
|
|
|
|
describe("useCompare helpers", () => {
|
|
describe("previousMonth", () => {
|
|
it("goes back one month within the same year", () => {
|
|
expect(previousMonth(2026, 3)).toEqual({ year: 2026, month: 2 });
|
|
expect(previousMonth(2026, 12)).toEqual({ year: 2026, month: 11 });
|
|
});
|
|
|
|
it("wraps around January to December of previous year", () => {
|
|
expect(previousMonth(2026, 1)).toEqual({ year: 2025, month: 12 });
|
|
});
|
|
});
|
|
|
|
describe("defaultReferencePeriod", () => {
|
|
it("returns the month before the given date", () => {
|
|
expect(defaultReferencePeriod(new Date(2026, 3, 15))).toEqual({ year: 2026, month: 3 });
|
|
});
|
|
|
|
it("wraps around when today is in January", () => {
|
|
expect(defaultReferencePeriod(new Date(2026, 0, 10))).toEqual({ year: 2025, month: 12 });
|
|
});
|
|
|
|
it("handles the last day of a month", () => {
|
|
expect(defaultReferencePeriod(new Date(2026, 6, 31))).toEqual({ year: 2026, month: 6 });
|
|
});
|
|
});
|
|
|
|
describe("comparisonMeta", () => {
|
|
it("MoM returns the previous month", () => {
|
|
expect(comparisonMeta("mom", 2026, 3)).toEqual({ previousYear: 2026, previousMonth: 2 });
|
|
});
|
|
|
|
it("MoM wraps around January", () => {
|
|
expect(comparisonMeta("mom", 2026, 1)).toEqual({ previousYear: 2025, previousMonth: 12 });
|
|
});
|
|
|
|
it("YoY returns the same month in the previous year", () => {
|
|
expect(comparisonMeta("yoy", 2026, 3)).toEqual({ previousYear: 2025, previousMonth: 3 });
|
|
});
|
|
|
|
it("YoY for January stays on January of previous year", () => {
|
|
expect(comparisonMeta("yoy", 2026, 1)).toEqual({ previousYear: 2025, previousMonth: 1 });
|
|
});
|
|
});
|
|
|
|
describe("syncReferenceOnPeriodChange", () => {
|
|
// The ref is seeded with the initial `to`, so the mount call has to===last.
|
|
it("no-ops on mount, preserving the previous-month default", () => {
|
|
const r = syncReferenceOnPeriodChange("2026-12-31", "2026-12-31", 2026, 6);
|
|
expect(r.next).toBeNull();
|
|
expect(r.lastSyncedTo).toBe("2026-12-31");
|
|
});
|
|
|
|
// StrictMode double-invokes effects in dev: calling twice with the same `to`
|
|
// must not sync (a fire-once boolean would wrongly snap to December on run 2).
|
|
it("is idempotent across a repeated `to` (StrictMode-safe)", () => {
|
|
const a = syncReferenceOnPeriodChange("2026-12-31", "2026-12-31", 2026, 6);
|
|
const b = syncReferenceOnPeriodChange(a.lastSyncedTo, "2026-12-31", 2026, 6);
|
|
expect(a.next).toBeNull();
|
|
expect(b.next).toBeNull();
|
|
});
|
|
|
|
it("syncs the reference month when `to` changes (user navigation)", () => {
|
|
const r = syncReferenceOnPeriodChange("2026-12-31", "2026-06-30", 2026, 12);
|
|
expect(r.next).toEqual({ year: 2026, month: 6 });
|
|
expect(r.lastSyncedTo).toBe("2026-06-30");
|
|
});
|
|
|
|
it("records a changed `to` but does not dispatch when it already matches state", () => {
|
|
const r = syncReferenceOnPeriodChange("2026-12-31", "2026-06-30", 2026, 6);
|
|
expect(r.next).toBeNull();
|
|
expect(r.lastSyncedTo).toBe("2026-06-30");
|
|
});
|
|
|
|
it("ignores an unparseable `to`", () => {
|
|
const r = syncReferenceOnPeriodChange("2026-12-31", "garbage", 2026, 6);
|
|
expect(r.next).toBeNull();
|
|
expect(r.lastSyncedTo).toBe("garbage");
|
|
});
|
|
});
|
|
});
|