From 508d68e16d49625fb083824a5bee0211c85f3432 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Wed, 21 Oct 2026 12:48:44 +0200 Subject: [PATCH] test(e2e): reconcile shell and smoke consumers with approved lifetime and locale contracts --- .../phase-7/p7-12/TERMINAL_CONSUMERS.md | 19 ++ .../terminal-consumers-static-checks.json | 104 +++++++++++ .../p7-12/evidence/terminal-consumers.json | 176 ++++++++++++++++++ job-tracker-ui/e2e/shell-readiness.spec.ts | 17 +- job-tracker-ui/e2e/smoke.spec.ts | 12 +- 5 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 docs/redesign/phase-7/p7-12/TERMINAL_CONSUMERS.md create mode 100644 docs/redesign/phase-7/p7-12/evidence/terminal-consumers-static-checks.json create mode 100644 docs/redesign/phase-7/p7-12/evidence/terminal-consumers.json diff --git a/docs/redesign/phase-7/p7-12/TERMINAL_CONSUMERS.md b/docs/redesign/phase-7/p7-12/TERMINAL_CONSUMERS.md new file mode 100644 index 0000000..3c6ad14 --- /dev/null +++ b/docs/redesign/phase-7/p7-12/TERMINAL_CONSUMERS.md @@ -0,0 +1,19 @@ +# Integrated shell and smoke consumers + +## Complete failed run and bounded correction + +The WAL-source ordinary suite completed06:05:40–07:01:53UTC with **343PASS/8FAIL/0skipped/0flaky**, zero configured retries. SQLite Audit/language persistence and populated Users pagination now pass. Ordinary P2-D01 passes as an unchanged case. The eight remaining failures are retained in [portable evidence](evidence/terminal-consumers.json); this completed failed run is not reported as acceptance. + +Four shell tooltip/toast cases waited for a private account export after navigating away from Backup. The [approved P7-09 lifetime boundary](../p7-09/DATA_PRIVACY_BACKUP.md) intentionally disposes that lazy owner on Settings-tab changes, preventing late private downloads/toasts. The existing four unit lifetime cases and native disposal case already enforce that behavior. Removing the guard to satisfy this older shell scenario would violate that acceptance contract. + +The shell consumer therefore completes its real account ZIP while Backup stays mounted, then checks the enabled encrypted-backup action beneath the live success toast. All three widths, four theme/language pairs, actual ZIP filename, real first server response, shared2/minute export admission, focus/exposure, normal toast timing, screenshots, tooltip/axe and dismiss assertions remain. The existing response reuse across viewport samples stays unchanged. No product download, owner/session guard or notification implementation changes. Navigation and disposal remain covered in their responsible Settings tests. + +The four smoke failures share a bootstrap account left in Bokmål by preceding bilingual cases. Local storage alone cannot override the persisted account preference. The existing root UI/API login helpers now explicitly persist English with real CSRF/session headers, asserting204. UI login still enters credentials and proves its redirect, then reloads the persisted-language result before English workflow assertions. Other accounts and bilingual tests are unchanged. `Career Workspace` remains the correct approved English heading; it is not renamed to make the test pass. The version-badge selector now uses the current semantic `img` role with the original exact accessible name; its version/commit content assertions remain. + +## Results and limits + +All **8affected native cases PASS**,10:44:02–10:45:21UTC, with0skips/flaky/retries. This composition deliberately runs the four language/theme shell cases before the four smoke workflows. Browser TypeScript and lint pass10:45:57–10:46:22UTC. No cases are added or removed from351; no timeout, authorization, persistence, route, draft or save assertion is relaxed. + +Fresh whole frontend remains899/101PASS,0pending/failures,06:10:48–06:15:42UTC. Offline sidecar56PASS10:45:23–10:45:32UTC with model loading disabled. The unchanged fidelity oracle verifies the newly recorded standard16fixtures/36preview-PDF pages and Settings8fixtures/24pages10:47:16–10:47:30UTC. Stale earlier aggregate reports were archived, not reused: the Settings inputs are the four actual new locale/theme measurement files. Tolerances/oracles/renderer are unchanged. + +The full351-case successor and separate lanes still follow. An overlapping8-case pass is not added to343to manufacture a completed full-suite pass. Product runtime inputs and Release assemblies retain the [WAL source identity](evidence/wal-entry.json); only these two consumer files changed after the completed failed run. All57group qualifications, vendor localisation residuals, human/device/output/provider/release requirements, P5-13 withholding and Phase8 boundary remain. diff --git a/docs/redesign/phase-7/p7-12/evidence/terminal-consumers-static-checks.json b/docs/redesign/phase-7/p7-12/evidence/terminal-consumers-static-checks.json new file mode 100644 index 0000000..d3eb44b --- /dev/null +++ b/docs/redesign/phase-7/p7-12/evidence/terminal-consumers-static-checks.json @@ -0,0 +1,104 @@ +{ + "schemaVersion": 1, + "checkpoint": "p7-12", + "status": "PASS", + "checkedUtc": "2026-10-21T10:48:38.452513+00:00", + "commands": [ + { + "name": "secret-scan", + "command": [ + "rtk", + "proxy", + "C:\\Users\\Connor\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", + "-X", + "utf8", + "scripts/supply-chain.py", + "scan-secrets" + ], + "startedUtc": "2026-10-21T10:48:34.965434+00:00", + "finishedUtc": "2026-10-21T10:48:37.641222+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261021T104834Z/secret-scan.log", + "logSha256Raw": "be5119e6057287d397e87759f855c91e634e3bfac61c8be7584d05592ba2dfcd" + }, + { + "name": "working-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--check" + ], + "startedUtc": "2026-10-21T10:48:37.641726+00:00", + "finishedUtc": "2026-10-21T10:48:37.726433+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261021T104834Z/working-whitespace.log", + "logSha256Raw": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "staged-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--cached", + "--check" + ], + "startedUtc": "2026-10-21T10:48:37.726433+00:00", + "finishedUtc": "2026-10-21T10:48:37.810579+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261021T104834Z/staged-whitespace.log", + "logSha256Raw": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "phase7-whitespace", + "command": [ + "rtk", + "proxy", + "git", + "diff", + "--check", + "b5319981ad3d1bd635daff861453d23c0de5320b" + ], + "startedUtc": "2026-10-21T10:48:37.810579+00:00", + "finishedUtc": "2026-10-21T10:48:38.032817+00:00", + "exitCode": 0, + "log": ".artifacts/phase7/current/static/20261021T104834Z/phase7-whitespace.log", + "logSha256Raw": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "untrackedSecretScan": { + "files": 0, + "findings": 0 + }, + "documentation": { + "files": 47, + "relativeLinks": 708, + "errors": [] + }, + "preservation": { + "groups": 57, + "statuses": { + "PARTIAL": 56, + "PASS": 0, + "FAIL": 0, + "DEFERRED": 1 + }, + "originalRoutesPreserved": true, + "currentConcreteRoutes": 33 + }, + "production": { + "main": "5f835e54a9b74e6b71418422e28431b2af06453f", + "originMain": "5f835e54a9b74e6b71418422e28431b2af06453f", + "status": "NO-GO", + "qualification": "Local fetched refs; direct push observation recorded separately." + }, + "hashPolicy": "Portable JSON UTF8 LF; raw log hashes measure exact saved bytes.", + "limits": [ + "Pattern scanner skips binary and files over2MiB; not historical Git/production secrets.", + "Local links check target existence, not URL/heading anchors.", + "Static evidence is not runtime/manual/device/provider/production acceptance." + ] +} diff --git a/docs/redesign/phase-7/p7-12/evidence/terminal-consumers.json b/docs/redesign/phase-7/p7-12/evidence/terminal-consumers.json new file mode 100644 index 0000000..de913ff --- /dev/null +++ b/docs/redesign/phase-7/p7-12/evidence/terminal-consumers.json @@ -0,0 +1,176 @@ +{ + "observedUtc": "2026-10-21T10:48:08.631477+00:00", + "failedCompleteRun": { + "stats": { + "startTime": "2026-10-21T06:05:41.420Z", + "duration": 3371710.188, + "expected": 343, + "skipped": 0, + "unexpected": 8, + "flaky": 0 + }, + "command": { + "name": "main", + "command": [ + "rtk", + "proxy", + "node", + "node_modules/@playwright/test/cli.js", + "test", + "--retries=0", + "--reporter=list,json", + "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\main-results" + ], + "startedUtc": "2026-10-21T06:05:40.915843+00:00", + "finishedUtc": "2026-10-21T07:01:53.642856+00:00", + "exitCode": 1, + "failureLog": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\main.log", + "stats": { + "startTime": "2026-10-21T06:05:41.420Z", + "duration": 3371710.188, + "expected": 343, + "skipped": 0, + "unexpected": 8, + "flaky": 0 + } + }, + "raw": [ + { + "path": ".artifacts/phase7/current/ui/main.json", + "sha256": "c5c2381f12572d07b63f39b54af0e8e19679576372f4c9009c921eca1f82fb7e" + }, + { + "path": ".artifacts/phase7/current/ui/main.log", + "sha256": "9cf5784d7480d229303971f75897344358c2c86740f654a13ebe2b54244b843f" + }, + { + "path": ".artifacts/phase7/current/ui/main-command.json", + "sha256": "ba09d8604684b77fd44896bd1087c9bf69793731b9437121519b9220fcbf902a" + } + ], + "failures": [ + { + "file": "shell-readiness.spec.ts", + "title": "[P5-00 PRES-E08 C06 S05] light-en real Tooltip contrast and open-toast action access", + "errors": [ + "\u001b[31mTest timeout of 120000ms exceeded.\u001b[39m", + "Error: page.waitForEvent: Test timeout of 120000ms exceeded.\n=========================== logs ===========================\nwaiting for event \"download\"\n============================================================\n\n 335 | await route.fulfill({ response: exported });\n 336 | }, { times: 1 });\n> 337 | const download = page.waitForEvent(\"download\");\n | ^\n 338 | await main.getByRole(\"button\", { name: t.accountExportDownload, exact: true }).click();\n 339 | await choose(t.settingsTabGeneral);\n 340 | const manage = main.getByRole(\"button\", { name: t.settingsManageConnectedAccounts, exact: true });\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\shell-readiness.spec.ts:337:29" + ] + }, + { + "file": "shell-readiness.spec.ts", + "title": "[P5-00 PRES-E08 C06 S05] light-nb real Tooltip contrast and open-toast action access", + "errors": [ + "\u001b[31mTest timeout of 120000ms exceeded.\u001b[39m", + "Error: page.waitForEvent: Test timeout of 120000ms exceeded.\n=========================== logs ===========================\nwaiting for event \"download\"\n============================================================\n\n 335 | await route.fulfill({ response: exported });\n 336 | }, { times: 1 });\n> 337 | const download = page.waitForEvent(\"download\");\n | ^\n 338 | await main.getByRole(\"button\", { name: t.accountExportDownload, exact: true }).click();\n 339 | await choose(t.settingsTabGeneral);\n 340 | const manage = main.getByRole(\"button\", { name: t.settingsManageConnectedAccounts, exact: true });\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\shell-readiness.spec.ts:337:29" + ] + }, + { + "file": "shell-readiness.spec.ts", + "title": "[P5-00 PRES-E08 C06 S05] dark-en real Tooltip contrast and open-toast action access", + "errors": [ + "\u001b[31mTest timeout of 120000ms exceeded.\u001b[39m", + "Error: page.waitForEvent: Test timeout of 120000ms exceeded.\n=========================== logs ===========================\nwaiting for event \"download\"\n============================================================\n\n 335 | await route.fulfill({ response: exported });\n 336 | }, { times: 1 });\n> 337 | const download = page.waitForEvent(\"download\");\n | ^\n 338 | await main.getByRole(\"button\", { name: t.accountExportDownload, exact: true }).click();\n 339 | await choose(t.settingsTabGeneral);\n 340 | const manage = main.getByRole(\"button\", { name: t.settingsManageConnectedAccounts, exact: true });\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\shell-readiness.spec.ts:337:29" + ] + }, + { + "file": "shell-readiness.spec.ts", + "title": "[P5-00 PRES-E08 C06 S05] dark-nb real Tooltip contrast and open-toast action access", + "errors": [ + "\u001b[31mTest timeout of 120000ms exceeded.\u001b[39m", + "Error: page.waitForEvent: Test timeout of 120000ms exceeded.\n=========================== logs ===========================\nwaiting for event \"download\"\n============================================================\n\n 335 | await route.fulfill({ response: exported });\n 336 | }, { times: 1 });\n> 337 | const download = page.waitForEvent(\"download\");\n | ^\n 338 | await main.getByRole(\"button\", { name: t.accountExportDownload, exact: true }).click();\n 339 | await choose(t.settingsTabGeneral);\n 340 | const manage = main.getByRole(\"button\", { name: t.settingsManageConnectedAccounts, exact: true });\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\shell-readiness.spec.ts:337:29" + ] + }, + { + "file": "smoke.spec.ts", + "title": "login establishes an authenticated session", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoBeVisible\u001b[2m(\u001b[22m\u001b[2m)\u001b[22m failed\n\nLocator: getByLabel('Application version e2e-verification, commit e2e1234')\nExpected: visible\nTimeout: 5000ms\nError: element(s) not found\n\nCall log:\n\u001b[2m - Expect \"toBeVisible\" with timeout 5000ms\u001b[22m\n\u001b[2m - waiting for getByLabel('Application version e2e-verification, commit e2e1234')\u001b[22m\n\n\n 118 | await expect(page.getByText(\"e2e@example.test\").first()).toBeVisible();\n 119 | await expect(page.getByTestId(\"admin-version-badge\")).toHaveText(\"ve2e-verification\");\n> 120 | await expect(page.getByLabel(\"Application version e2e-verification, commit e2e1234\")).toBeVisible();\n | ^\n 121 |\n 122 | await page.getByRole(\"button\", { name: \"Notifications\" }).click();\n 123 | await expect(page.getByLabel(\"Notifications panel\")).toBeVisible();\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\smoke.spec.ts:120:89" + ] + }, + { + "file": "smoke.spec.ts", + "title": "a saved job can be created through the reviewed UI flow", + "errors": [ + "\u001b[31mTest timeout of 30000ms exceeded.\u001b[39m", + "Error: locator.click: Test timeout of 30000ms exceeded.\nCall log:\n\u001b[2m - waiting for getByRole('button', { name: 'Add Job' })\u001b[22m\n\n\n 180 | await login(page);\n 181 | await page.goto(\"/jobs\");\n> 182 | await page.getByRole(\"button\", { name: \"Add Job\" }).click();\n | ^\n 183 | await page.getByRole(\"button\", { name: \"Enter details manually\" }).click();\n 184 | await page.getByRole(\"dialog\").getByRole(\"combobox\", { name: \"Company\" }).fill(`E2E Company ${suffix}`);\n 185 | await page.getByLabel(\"Job title\").fill(title);\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\smoke.spec.ts:182:55" + ] + }, + { + "file": "smoke.spec.ts", + "title": "the dedicated application workspace survives deep links, long data and unsaved navigation", + "errors": [ + "\u001b[31mTest timeout of 60000ms exceeded.\u001b[39m", + "Error: locator.click: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for getByRole('tablist', { name: 'Workspace sections' }).getByRole('tab', { name: 'Application', exact: true })\u001b[22m\n\n\n 261 |\n 262 | const workspaceNav = page.getByRole(\"tablist\", { name: \"Workspace sections\" });\n> 263 | await workspaceNav.getByRole(\"tab\", { name: \"Application\", exact: true }).click();\n | ^\n 264 | await page.getByRole(\"tab\", { name: \"Answers & message draft\", exact: true }).click();\n 265 | await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}\\\\?section=application&sub=answers$`));\n 266 | await page.getByLabel(\"Application answer\", { exact: true }).fill(\"A reviewed answer that must not be lost.\");\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\smoke.spec.ts:263:77" + ] + }, + { + "file": "smoke.spec.ts", + "title": "Career Workspace loads from the authenticated application shell", + "errors": [ + "Error: \u001b[2mexpect(\u001b[22m\u001b[31mlocator\u001b[39m\u001b[2m).\u001b[22mtoBeVisible\u001b[2m(\u001b[22m\u001b[2m)\u001b[22m failed\n\nLocator: getByRole('heading', { name: 'Career Workspace' })\nExpected: visible\nTimeout: 5000ms\nError: element(s) not found\n\nCall log:\n\u001b[2m - Expect \"toBeVisible\" with timeout 5000ms\u001b[22m\n\u001b[2m - waiting for getByRole('heading', { name: 'Career Workspace' })\u001b[22m\n\n\n 324 | await page.evaluate(() => window.localStorage.setItem(\"jobtracker.themeMode\", \"light\"));\n 325 | await page.goto(\"/career\");\n> 326 | await expect(page.getByRole(\"heading\", { name: \"Career Workspace\" })).toBeVisible();\n | ^\n 327 | await expect(page.getByRole(\"link\", { name: \"Open CV Builder\" })).toHaveAttribute(\"href\", \"/career/builder\");\n 328 |\n 329 | await page.setViewportSize({ width: 375, height: 900 });\n at E:\\Storage\\Projects\\Active\\Job tracker\\job-tracker-ui\\e2e\\smoke.spec.ts:326:73" + ] + } + ] + }, + "correctedConsumerVector": { + "stats": { + "startTime": "2026-10-21T10:44:02.866Z", + "duration": 78218.83300000001, + "expected": 8, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "command": { + "name": "terminal-consumers", + "command": [ + "rtk", + "proxy", + "node", + "node_modules/@playwright/test/cli.js", + "test", + "e2e/shell-readiness.spec.ts", + "e2e/smoke.spec.ts", + "--grep", + "real Tooltip contrast|login establishes|saved job can be created|dedicated application workspace|Career Workspace loads", + "--retries=0", + "--reporter=list,json", + "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\terminal-consumers-results" + ], + "startedUtc": "2026-10-21T10:44:02.342682+00:00", + "finishedUtc": "2026-10-21T10:45:21.120749+00:00", + "exitCode": 0 + }, + "raw": [ + { + "path": ".artifacts/phase7/current/ui/terminal-consumers.json", + "sha256": "e5b1eac14270bb33ad0b29ebbbefe256481155afe8ddf983ac33e86649bd7afa" + }, + { + "path": ".artifacts/phase7/current/ui/terminal-consumers.log", + "sha256": "d468adfe7ea83c594a3e5082b2516b7d90c3e603f2a93deb048942650d1fe59e" + }, + { + "path": ".artifacts/phase7/current/ui/terminal-consumers-command.json", + "sha256": "6c35ccb9b3c264ce453d66840b7da6d2a2b67e13181b72dcf411cfbad780f4c3" + } + ] + }, + "ordinaryP2D01": [ + { + "file": "redesign-preservation.spec.ts", + "title": "[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change", + "status": "passed", + "retry": 0 + } + ], + "postRunSourceObservation": { + "differencesFromWalEntry": [ + "job-tracker-ui/e2e/shell-readiness.spec.ts", + "job-tracker-ui/e2e/smoke.spec.ts" + ], + "qualification": "Both edits made after the completed failed run; tracked product inputs and API/test assemblies retain the WAL identity. A new clean source freeze and whole-suite execution follow publication." + }, + "qualification": "343 passing cases plus an overlapping8-case vector are not reported as a completed351-case pass. No skipped/expected-failure cases, retries, timeout increases or product changes are introduced." +} diff --git a/job-tracker-ui/e2e/shell-readiness.spec.ts b/job-tracker-ui/e2e/shell-readiness.spec.ts index d710f1a..ea07f74 100644 --- a/job-tracker-ui/e2e/shell-readiness.spec.ts +++ b/job-tracker-ui/e2e/shell-readiness.spec.ts @@ -323,33 +323,28 @@ for (const mode of ["light", "dark"] as const) for (const language of ["en", "nb } }; await choose(t.settingsTabBackup); - // Complete the real export after navigating back to the bottom action. - // Otherwise clicking General correctly dismisses the toast via click-away. - let release!: () => void; - const held = new Promise(resolve => { release = resolve; }); await page.route(`${apiUrl}/export/account`, async route => { // One native ZIP, replayed for viewport checks to respect the 2/minute owner limit. exported ??= await route.fetch(); expect(exported.status()).toBe(200); - await held; await route.fulfill({ response: exported }); }, { times: 1 }); const download = page.waitForEvent("download"); await main.getByRole("button", { name: t.accountExportDownload, exact: true }).click(); - await choose(t.settingsTabGeneral); - const manage = main.getByRole("button", { name: t.settingsManageConnectedAccounts, exact: true }); - await manage.focus(); - release(); expect((await download).suggestedFilename()).toMatch(/\.zip$/); + // Keep the private-export owner mounted; leaving Backup intentionally disposes late downloads. + const encrypted = main.getByRole("button", { name: t.backupDownload, exact: true }); + await expect(encrypted).toBeEnabled(); + await encrypted.focus(); const toast = page.getByRole("alert").filter({ hasText: t.accountExportDownloaded }); await expect(toast).toBeVisible(); // Deliberately align at the viewport end: reserved page space alone does // not stop native scrolling from placing an action underneath the toast. - await manage.evaluate(node => node.scrollIntoView({ block: "end" })); + await encrypted.evaluate(node => node.scrollIntoView({ block: "end" })); // Hover uses Snackbar's normal pause behavior; timing is not shortened or mocked. await toast.hover(); await toast.getByRole("button").focus(); - await exposed(manage); + await exposed(encrypted); await capture(page, info, `open-toast-${width}-${suffix}`); await expect(toast).toBeVisible(); await exposed(toast.getByRole("button")); diff --git a/job-tracker-ui/e2e/smoke.spec.ts b/job-tracker-ui/e2e/smoke.spec.ts index 1a411c0..ee9ac79 100644 --- a/job-tracker-ui/e2e/smoke.spec.ts +++ b/job-tracker-ui/e2e/smoke.spec.ts @@ -40,6 +40,10 @@ async function loginAs(page: Page, email: string, password: string) { await page.getByLabel("Current password").fill(password); await page.getByRole("button", { name: "Sign in", exact: true }).click(); await expect(page).toHaveURL(/\/dashboard$/); + if (email === "e2e@example.test") { + await setEnglishPreference(page); + await page.reload(); + } await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible(); } @@ -49,6 +53,7 @@ async function login(page: Page) { async function loginForApiSetup(page: Page) { expect((await page.request.post(`${apiUrl}/auth/login`, { data: { email: "e2e@example.test", password: "E2ePassword123!" } })).status()).toBe(200); + await setEnglishPreference(page); } async function csrfHeader(page: Page) { @@ -59,6 +64,11 @@ async function csrfHeader(page: Page) { return { "X-CSRF-TOKEN": cookie!.value, "X-Session-Context": me.sessionContext }; } +async function setEnglishPreference(page: Page) { + const headers = await csrfHeader(page); + expect((await page.request.put(`${apiUrl}/auth/preferences/language`, { headers, data: { language: "en" } })).status()).toBe(204); +} + test.beforeEach(async ({ page }) => { await page.addInitScript(() => window.localStorage.setItem("uiLanguage", "en")); }); @@ -117,7 +127,7 @@ test("login establishes an authenticated session", async ({ page }) => { await login(page); await expect(page.getByText("e2e@example.test").first()).toBeVisible(); await expect(page.getByTestId("admin-version-badge")).toHaveText("ve2e-verification"); - await expect(page.getByLabel("Application version e2e-verification, commit e2e1234")).toBeVisible(); + await expect(page.getByRole("img", { name: "Application version e2e-verification, commit e2e1234", exact: true })).toBeVisible(); await page.getByRole("button", { name: "Notifications" }).click(); await expect(page.getByLabel("Notifications panel")).toBeVisible();