From 47c4560253a0288289d8365e2b02d065e355755a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 7 Aug 2026 03:56:30 +0200 Subject: [PATCH] test: prove the banner names the kind of record, not just tags it recordKindLabel chooses between two sentences that mean different things and nothing could fail it: the journey asserted the data attribute, which that function never reads, so inverting the arms left the suite green while a guardian standing inside a managed profile was told it was an ordinary shared record. --- e2e/v137-sharing-managed-profiles.spec.ts | 11 ++- .../shared-record-banner-label.test.tsx | 78 +++++++++++++++++++ .../layout/shared-record-banner.tsx | 12 ++- 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/components/layout/__tests__/shared-record-banner-label.test.tsx diff --git a/e2e/v137-sharing-managed-profiles.spec.ts b/e2e/v137-sharing-managed-profiles.spec.ts index 5ad2d5f0a..116fd643e 100644 --- a/e2e/v137-sharing-managed-profiles.spec.ts +++ b/e2e/v137-sharing-managed-profiles.spec.ts @@ -595,9 +595,18 @@ test.describe.serial("managed profile browser journey", () => { const banner = page.locator('[data-slot="shared-record-banner"]'); await expect(banner).toBeVisible(); await expect(banner).toHaveAttribute("data-record-kind", "managed"); + // The WORDING, not only the attribute, and this is the one place in the + // suite where pinning copy is the right call rather than the lazy one: the + // claim is that the banner NAMES it as a managed record, and the attribute + // is not what a person reads. `recordKindLabel`'s ternary chooses between + // two sentences that mean different things, and inverting it left every + // attribute assertion green while telling a Guardian they were inside an + // ordinary shared record. The pure function is pinned in + // `src/components/layout/__tests__/shared-record-banner-label.test.tsx`; + // this line proves the rendered banner really calls it. await expect( page.locator('[data-slot="shared-record-banner-context"]'), - ).toBeVisible(); + ).toContainText("Managed profile"); // And back out, with the record still on the list. // diff --git a/src/components/layout/__tests__/shared-record-banner-label.test.tsx b/src/components/layout/__tests__/shared-record-banner-label.test.tsx new file mode 100644 index 000000000..472d3f301 --- /dev/null +++ b/src/components/layout/__tests__/shared-record-banner-label.test.tsx @@ -0,0 +1,78 @@ +/** + * The banner names which KIND of record this is, and the two names differ. + * + * ## Why this file exists + * + * `recordKindLabel` is a two-arm ternary, and until this file nothing could + * fail it. The browser journey asserted `data-record-kind="managed"` — an + * attribute the function does not read — so inverting the arms left the whole + * suite green while the banner told a Guardian standing inside a managed + * profile that they were in an ordinary shared record. That is not a cosmetic + * difference: the two records have different rules, and the banner is the one + * line of chrome whose job is to say which one you are in. + * + * The mapping is asserted through the real EN bundle rather than against key + * names, because a key that resolves to the wrong sentence passes a key-level + * check. Both arms are named, and the two are asserted to be DIFFERENT — a + * function that returned one sentence for both would satisfy every "contains" + * assertion anybody would think to write. + */ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { recordKindLabel } from "../shared-record-banner"; + +/** The real bundle, so the assertion is about sentences and not about keys. */ +const EN = JSON.parse( + readFileSync(join(process.cwd(), "messages/en.json"), "utf8"), +) as { + recordSharing: { lookingAfter: { kindManaged: string; kindShared: string } }; +}; + +/** The i18n `t` as the banner receives it, resolved against the real bundle. */ +const t = (key: string): string => { + const leaf = key + .split(".") + .reduce( + (node, segment) => + typeof node === "object" && node !== null + ? (node as Record)[segment] + : undefined, + EN, + ); + if (typeof leaf !== "string") throw new Error(`unresolved key: ${key}`); + return leaf; +}; + +describe("what the shared-record banner calls the record", () => { + it("has a translator that really resolves", () => { + // Positive control. A `t` that returned its own key would make both legs + // below pass on the key names while proving nothing about the sentences. + expect(t("recordSharing.lookingAfter.kindManaged")).toBe( + EN.recordSharing.lookingAfter.kindManaged, + ); + expect(EN.recordSharing.lookingAfter.kindManaged.length).toBeGreaterThan(3); + expect(EN.recordSharing.lookingAfter.kindShared.length).toBeGreaterThan(3); + }); + + it("names a managed profile as one", () => { + expect(recordKindLabel("managed", t)).toBe( + EN.recordSharing.lookingAfter.kindManaged, + ); + }); + + it("names an ordinary shared record as one", () => { + expect(recordKindLabel("shared", t)).toBe( + EN.recordSharing.lookingAfter.kindShared, + ); + }); + + it("does not give the two the same sentence", () => { + // The failure mode a pair of "contains" assertions cannot see. + expect(recordKindLabel("managed", t)).not.toBe( + recordKindLabel("shared", t), + ); + }); +}); diff --git a/src/components/layout/shared-record-banner.tsx b/src/components/layout/shared-record-banner.tsx index 9d30ce719..de3764e52 100644 --- a/src/components/layout/shared-record-banner.tsx +++ b/src/components/layout/shared-record-banner.tsx @@ -118,7 +118,17 @@ function accessLabel( : t("recordSharing.banner.readOnly"); } -function recordKindLabel( +/** + * Which kind of record this is, in the banner's own words. + * + * Exported and pure because the ternary is the whole claim and nothing could + * fail it: the browser journey asserted `data-record-kind="managed"`, which is + * the attribute this function does not read, so inverting the two arms left + * every test in the suite green while telling a Guardian they were inside an + * ordinary shared record. Two sentences that mean different things are worth + * one test. + */ +export function recordKindLabel( recordKind: ReturnType["recordKind"], t: ReturnType["t"], ): string {