From 39092aa936d8b7d08dabb06faf62325649d54a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 23 Oct 2026 08:14:32 +0200 Subject: [PATCH] test(e2e): take the check-in in the reader's language For German, Dutch, Korean and Brazilian Portuguese, the note and the switch to the English original appear on an unvalidated translation and nowhere else, the question carries the language it is written in, and the result of an unvalidated translation says so. The switch shows the English original and the result then carries no note, and a Brazilian PHQ-9 with item 9 above zero leads with the crisis card. Each screen is scanned with axe, at desktop and phone width. --- e2e/mental-health-check-in.spec.ts | 156 +++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/e2e/mental-health-check-in.spec.ts b/e2e/mental-health-check-in.spec.ts index 3dda44f2e..c7867b16b 100644 --- a/e2e/mental-health-check-in.spec.ts +++ b/e2e/mental-health-check-in.spec.ts @@ -171,3 +171,159 @@ test.describe("mental-health check-in, every instrument", () => { }); } }); + +/** + * The questionnaires in the reader's language. Where a validated translation + * exists the form shows it and nothing else; where the text is HealthLog's + * own translation it carries the note and the one-tap switch to the English + * original, and the result says it was answered in an unvalidated + * translation. Which cells are unvalidated is pinned by + * `instrument-wording.test.ts`; this journey proves the surface follows it. + */ +const UNVALIDATED: Record = { + de: ["SCI"], + nl: ["SCI"], + ko: ["SCI"], + "pt-BR": ["WHO5", "SCI"], +}; + +function caseOf(id: Case["id"]): Case { + const found = CASES.find((c) => c.id === id); + if (!found) throw new Error(`no case for ${id}`); + return found; +} + +async function openInLocale(page: Page, locale: string) { + await page.context().addCookies([ + { + name: "healthlog-locale", + value: locale, + url: process.env.E2E_BASE_URL ?? "http://localhost:3000", + }, + ]); + await page.goto("/mental-wellbeing"); +} + +async function answerAll(page: Page, c: Case, position: number) { + const wizard = page.locator( + `[data-slot="mental-health-check-in"][data-instrument="${c.id}"]`, + ); + for (let step = 1; step <= c.items; step++) { + const current = wizard.locator( + `[data-slot="check-in-step"][data-step="${step}"]`, + ); + await expect(current).toBeVisible(); + await current + .locator('[role="radio"]') + .nth(position - 1) + .click(); + } + if (c.followUp) { + await wizard.locator('[data-slot="check-in-next"]').click(); + } + await expect(wizard.locator('[data-slot="check-in-review"]')).toBeVisible(); + await wizard.locator('[data-slot="check-in-submit"]').click(); + const result = page.locator( + `[data-slot="mental-health-result"][data-instrument="${c.id}"]`, + ); + await expect(result).toBeVisible(); + return result; +} + +test.describe("mental-health check-in, in the reader's language", () => { + test.use({ storageState: STORAGE_STATE_PATH }); + + for (const [locale, unvalidated] of Object.entries(UNVALIDATED)) { + test(`${locale}: the note and the switch appear only on an unvalidated translation`, async ({ + page, + }) => { + test.setTimeout(120_000); + await openInLocale(page, locale); + await expect(page.locator("html")).toHaveAttribute("lang", locale); + + for (const c of CASES) { + await page + .locator(`[data-slot="instrument-card"][data-instrument="${c.id}"]`) + .locator('[data-slot="instrument-card-start"]') + .click(); + const wizard = page.locator( + `[data-slot="mental-health-check-in"][data-instrument="${c.id}"]`, + ); + await expect( + wizard.locator('[data-slot="check-in-step"][data-step="1"]'), + ).toBeVisible(); + const expected = unvalidated.includes(c.id) ? 1 : 0; + await expect( + wizard.locator('[data-slot="check-in-unvalidated-note"]'), + ).toHaveCount(expected); + await expect( + wizard.locator('[data-slot="check-in-wording-toggle"]'), + ).toHaveCount(expected); + await expect( + wizard.locator('[data-slot="check-in-question"]'), + ).toHaveAttribute("lang", locale); + if (expected === 1) + await expectNoSeriousViolations(page, `${locale} ${c.id}`); + await wizard.locator('[data-slot="check-in-back-to-landing"]').click(); + } + + // An unvalidated translation answered as shown: the result says so. + const sci = caseOf("SCI"); + await page + .locator('[data-slot="instrument-card"][data-instrument="SCI"]') + .locator('[data-slot="instrument-card-start"]') + .click(); + const result = await answerAll(page, sci, 2); + await expect( + result.locator('[data-slot="result-wording-note"]'), + ).toBeVisible(); + await expectNoSeriousViolations(page, `${locale} SCI result`); + }); + } + + test("nl: the switch shows the English original, and the result records it", async ({ + page, + }) => { + test.setTimeout(90_000); + await openInLocale(page, "nl"); + await page + .locator('[data-slot="instrument-card"][data-instrument="SCI"]') + .locator('[data-slot="instrument-card-start"]') + .click(); + const wizard = page.locator( + '[data-slot="mental-health-check-in"][data-instrument="SCI"]', + ); + const question = wizard.locator('[data-slot="check-in-question"]'); + const translated = await question.textContent(); + const toggle = wizard.locator('[data-slot="check-in-wording-toggle"]'); + await toggle.click(); + await expect(toggle).toHaveAttribute("data-showing", "original_en"); + await expect(question).toHaveAttribute("lang", "en"); + await expect(question).not.toHaveText(translated ?? ""); + const sci = caseOf("SCI"); + const result = await answerAll(page, sci, 1); + await expect( + result.locator('[data-slot="result-wording-note"]'), + ).toHaveCount(0); + }); + + test("pt-BR: PHQ-9 item 9 above zero leads the result with the crisis card", async ({ + page, + }) => { + test.setTimeout(90_000); + await openInLocale(page, "pt-BR"); + await page + .locator('[data-slot="instrument-card"][data-instrument="PHQ9"]') + .locator('[data-slot="instrument-card-start"]') + .click(); + const phq = caseOf("PHQ9"); + // Position 2 is "VĂ¡rios dias" (value 1) on every item, item 9 included. + const result = await answerAll(page, phq, 2); + await expect( + result.locator('[data-slot="mental-health-crisis-card"]'), + ).toBeVisible(); + await expect( + result.locator('[data-slot="result-wording-note"]'), + ).toHaveCount(0); + }); +});