test: pin the collapsed sub-group order and the banner's dot (#446)

reorderRows: a collapsed sub-group (a subtotal with no child row)
followed by a sibling leaf gives Vide, z, A, the order read by running
the case first; then the same block followed by a sibling root, a group
(Vide, z, A, b, B) or a leaf (Vide, z, A, R).

OnboardingBanner: the decorative h-1 w-1 dot is absent while the visit
steps are not initialized and while the Budget entitlement is unknown,
and present, between the counter and the next step, once the total is
known.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
le king fu 2026-10-18 20:59:17 -04:00
parent a62150d926
commit d551c4b0d4
2 changed files with 53 additions and 2 deletions

View file

@ -96,6 +96,13 @@ const buttonNamed = (node: ReactNode, key: string) => {
return button; return button;
}; };
/** The dot between the counter and the next step: a decorative `h-1 w-1` span. */
const isDot = (e: AnyElement) => {
const classes = String(e.props.className).split(/\s+/);
return e.type === "span" && classes.includes("h-1") && classes.includes("w-1");
};
const dots = (props: OnboardingBannerProps) => find(tree(props), isDot);
describe("OnboardingBanner — banner mode (#397)", () => { describe("OnboardingBanner — banner mode (#397)", () => {
it("is a region named for the path, with the counter, the next step and its link", () => { it("is a region named for the path, with the counter, the next step and its link", () => {
const html = markup(bannerProps()); const html = markup(bannerProps());
@ -119,15 +126,35 @@ describe("OnboardingBanner — banner mode (#397)", () => {
}); });
it("the counter waits for the Budget entitlement, the next step does not", () => { it("the counter waits for the Budget entitlement, the next step does not", () => {
const html = markup(bannerProps({ budgetReady: false })); const props = bannerProps({ budgetReady: false });
const html = markup(props);
expect(html).not.toContain("onboarding.banner.progress"); expect(html).not.toContain("onboarding.banner.progress");
expect(html).toMatch(/href="\/import"/); expect(html).toMatch(/href="\/import"/);
// The dot waits with the counter it follows (#446).
expect(dots(props)).toHaveLength(0);
}); });
it("the counter waits for the visit steps' initialization too, the next step does not (#425)", () => { it("the counter waits for the visit steps' initialization too, the next step does not (#425)", () => {
const html = markup(bannerProps({ visitStepsInitialized: false })); const props = bannerProps({ visitStepsInitialized: false });
const html = markup(props);
expect(html).not.toContain("onboarding.banner.progress"); expect(html).not.toContain("onboarding.banner.progress");
expect(html).toMatch(/href="\/import"/); expect(html).toMatch(/href="\/import"/);
// No dot either: it would stand alone before the next step (#446).
expect(dots(props)).toHaveLength(0);
});
it("once the total is known, a decorative dot separates the counter from the next step (#446)", () => {
const props = bannerProps();
const [dot] = dots(props);
expect(dots(props)).toHaveLength(1);
expect(dot.props["aria-hidden"]).toBe("true");
const html = markup(props);
const counter = html.indexOf("t(onboarding.banner.progress");
const dotAt = html.search(/<span aria-hidden="true" class="h-1 w-1 /);
const next = html.indexOf("t(onboarding.banner.next)");
expect(counter).toBeGreaterThan(-1);
expect(dotAt).toBeGreaterThan(counter);
expect(next).toBeGreaterThan(dotAt);
}); });
it("folded: 'Show the list' with aria-expanded false, and no list", () => { it("folded: 'Show the list' with aria-expanded false, and no list", () => {

View file

@ -76,6 +76,30 @@ describe("reorderRows — subtotals at the bottom (issue #424)", () => {
expect(names(reorderRows(rows, false))).toEqual(["Autres", "Paie", "Emploi", "Alloc", "Gouv", "Revenus"]); expect(names(reorderRows(rows, false))).toEqual(["Autres", "Paie", "Emploi", "Alloc", "Gouv", "Revenus"]);
}); });
// A collapsed sub-group: its subtotal row comes with no child row (the
// children hidden by the folding), so its block is empty (#446).
// A
// Vide (collapsed sub-group)
// z <- follows the empty block of "Vide"
it("a collapsed sub-group closes on its own subtotal: the sibling leaf after it stays outside", () => {
const rows: Row[] = [sub("A", 0), sub("Vide", 1), leaf("z", 1)];
expect(names(reorderRows(rows, false))).toEqual(["Vide", "z", "A"]);
});
it("the same block, followed by a sibling root: the root closes after its own rows", () => {
const block: Row[] = [sub("A", 0), sub("Vide", 1), leaf("z", 1)];
// A root group...
expect(names(reorderRows([...block, sub("B", 0), leaf("b", 1)], false))).toEqual([
"Vide",
"z",
"A",
"b",
"B",
]);
// ...or a root leaf.
expect(names(reorderRows([...block, leaf("R", 0)], false))).toEqual(["Vide", "z", "A", "R"]);
});
it("keeps every row exactly once", () => { it("keeps every row exactly once", () => {
const out = reorderRows(TWO_LEVELS, false); const out = reorderRows(TWO_LEVELS, false);
expect(out).toHaveLength(TWO_LEVELS.length); expect(out).toHaveLength(TWO_LEVELS.length);