test(e2e): reconcile shell and smoke consumers with approved lifetime and locale contracts
This commit is contained in:
@@ -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.
|
||||||
@@ -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."
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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."
|
||||||
|
}
|
||||||
@@ -323,33 +323,28 @@ for (const mode of ["light", "dark"] as const) for (const language of ["en", "nb
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
await choose(t.settingsTabBackup);
|
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<void>(resolve => { release = resolve; });
|
|
||||||
await page.route(`${apiUrl}/export/account`, async route => {
|
await page.route(`${apiUrl}/export/account`, async route => {
|
||||||
// One native ZIP, replayed for viewport checks to respect the 2/minute owner limit.
|
// One native ZIP, replayed for viewport checks to respect the 2/minute owner limit.
|
||||||
exported ??= await route.fetch();
|
exported ??= await route.fetch();
|
||||||
expect(exported.status()).toBe(200);
|
expect(exported.status()).toBe(200);
|
||||||
await held;
|
|
||||||
await route.fulfill({ response: exported });
|
await route.fulfill({ response: exported });
|
||||||
}, { times: 1 });
|
}, { times: 1 });
|
||||||
const download = page.waitForEvent("download");
|
const download = page.waitForEvent("download");
|
||||||
await main.getByRole("button", { name: t.accountExportDownload, exact: true }).click();
|
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$/);
|
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 });
|
const toast = page.getByRole("alert").filter({ hasText: t.accountExportDownloaded });
|
||||||
await expect(toast).toBeVisible();
|
await expect(toast).toBeVisible();
|
||||||
// Deliberately align at the viewport end: reserved page space alone does
|
// Deliberately align at the viewport end: reserved page space alone does
|
||||||
// not stop native scrolling from placing an action underneath the toast.
|
// 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.
|
// Hover uses Snackbar's normal pause behavior; timing is not shortened or mocked.
|
||||||
await toast.hover();
|
await toast.hover();
|
||||||
await toast.getByRole("button").focus();
|
await toast.getByRole("button").focus();
|
||||||
await exposed(manage);
|
await exposed(encrypted);
|
||||||
await capture(page, info, `open-toast-${width}-${suffix}`);
|
await capture(page, info, `open-toast-${width}-${suffix}`);
|
||||||
await expect(toast).toBeVisible();
|
await expect(toast).toBeVisible();
|
||||||
await exposed(toast.getByRole("button"));
|
await exposed(toast.getByRole("button"));
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ async function loginAs(page: Page, email: string, password: string) {
|
|||||||
await page.getByLabel("Current password").fill(password);
|
await page.getByLabel("Current password").fill(password);
|
||||||
await page.getByRole("button", { name: "Sign in", exact: true }).click();
|
await page.getByRole("button", { name: "Sign in", exact: true }).click();
|
||||||
await expect(page).toHaveURL(/\/dashboard$/);
|
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();
|
await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +53,7 @@ async function login(page: Page) {
|
|||||||
|
|
||||||
async function loginForApiSetup(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);
|
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) {
|
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 };
|
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 }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.addInitScript(() => window.localStorage.setItem("uiLanguage", "en"));
|
await page.addInitScript(() => window.localStorage.setItem("uiLanguage", "en"));
|
||||||
});
|
});
|
||||||
@@ -117,7 +127,7 @@ test("login establishes an authenticated session", async ({ page }) => {
|
|||||||
await login(page);
|
await login(page);
|
||||||
await expect(page.getByText("e2e@example.test").first()).toBeVisible();
|
await expect(page.getByText("e2e@example.test").first()).toBeVisible();
|
||||||
await expect(page.getByTestId("admin-version-badge")).toHaveText("ve2e-verification");
|
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 page.getByRole("button", { name: "Notifications" }).click();
|
||||||
await expect(page.getByLabel("Notifications panel")).toBeVisible();
|
await expect(page.getByLabel("Notifications panel")).toBeVisible();
|
||||||
|
|||||||
Reference in New Issue
Block a user