diff --git a/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_MOCK_FAILURE.json b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_MOCK_FAILURE.json new file mode 100644 index 00000000..dd435788 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_MOCK_FAILURE.json @@ -0,0 +1 @@ +{"numFailedTestSuites":1,"numFailedTests":1,"numPassedTestSuites":0,"numPassedTests":10,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":11,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772475316,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":249,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772478792,"status":"passed","title":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates"},{"ancestorTitles":[],"duration":160,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772479042,"status":"passed","title":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable"},{"ancestorTitles":[],"duration":903,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S02] all widget visibility choices save and survive remount","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772479202,"status":"passed","title":"[PRES-S02] all widget visibility choices save and survive remount"},{"ancestorTitles":[],"duration":239,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772480106,"status":"passed","title":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate"},{"ancestorTitles":[],"duration":69,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a summary failure does not suppress independent trends","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772480346,"status":"passed","title":"[PRES-S01] a summary failure does not suppress independent trends"},{"ancestorTitles":[],"duration":176,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772480416,"status":"passed","title":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields"},{"ancestorTitles":[],"duration":92,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772480592,"status":"passed","title":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning"},{"ancestorTitles":[],"duration":48,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772480685,"status":"passed","title":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage"},{"ancestorTitles":[],"duration":73,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772480733,"status":"passed","title":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend"},{"ancestorTitles":[],"duration":66,"failing":false,"failureDetails":[{"matcherResult":{"message":"expect(jest.fn()).not.toHaveBeenCalled()\n\nExpected number of calls: 0\nReceived number of calls: 1\n\n1: \"/auth/preferences/dashboard\", {\"revision\": 0, \"version\": 2, \"widgets\": [{\"h\": 9, \"id\": \"attention\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"agenda\", \"size\": \"standard\", \"visible\": false, \"w\": 8, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"funnel\", \"size\": \"standard\", \"visible\": false, \"w\": 4, \"x\": 0, \"y\": 0}, {\"h\": 5, \"id\": \"cards\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"activity\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"stageAge\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"sources\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"salary\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"companies\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"skills\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}]}, {\"signal\": {}}","pass":true}}],"failureMessages":["Error: expect(jest.fn()).not.toHaveBeenCalled()\n\nExpected number of calls: 0\nReceived number of calls: 1\n\n1: \"/auth/preferences/dashboard\", {\"revision\": 0, \"version\": 2, \"widgets\": [{\"h\": 9, \"id\": \"attention\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"agenda\", \"size\": \"standard\", \"visible\": false, \"w\": 8, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"funnel\", \"size\": \"standard\", \"visible\": false, \"w\": 4, \"x\": 0, \"y\": 0}, {\"h\": 5, \"id\": \"cards\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"activity\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"stageAge\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"sources\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"salary\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"companies\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"skills\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}]}, {\"signal\": {}}\n at Object.toHaveBeenCalled (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx:141:29)"],"fullName":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772480806,"status":"failed","title":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values"},{"ancestorTitles":[],"duration":246,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772480880,"status":"passed","title":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries"}],"endTime":1793772481136,"message":" ● [PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values\n\n expect(jest.fn()).not.toHaveBeenCalled()\n\n Expected number of calls: 0\n Received number of calls: 1\n\n 1: \"/auth/preferences/dashboard\", {\"revision\": 0, \"version\": 2, \"widgets\": [{\"h\": 9, \"id\": \"attention\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"agenda\", \"size\": \"standard\", \"visible\": false, \"w\": 8, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"funnel\", \"size\": \"standard\", \"visible\": false, \"w\": 4, \"x\": 0, \"y\": 0}, {\"h\": 5, \"id\": \"cards\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 8, \"id\": \"activity\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"stageAge\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"sources\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"salary\", \"size\": \"full\", \"visible\": false, \"w\": 12, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"companies\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}, {\"h\": 4, \"id\": \"skills\", \"size\": \"standard\", \"visible\": false, \"w\": 6, \"x\": 0, \"y\": 0}]}, {\"signal\": {}}\n\n 139 | expect(within(table).getByRole('cell', { name: '4', exact: true })).toBeVisible();\n 140 | expect(within(table).getByRole('cell', { name: '1', exact: true })).toBeVisible();\n > 141 | expect(mockedApi.put).not.toHaveBeenCalled();\n | ^\n 142 | });\n 143 |\n 144 | test('[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries', async () => {\n\n at Object.toHaveBeenCalled (src/dashboard-presentation.test.tsx:141:29)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx","startTime":1793772476205,"status":"failed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_RED.json b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_RED.json new file mode 100644 index 00000000..0c032ad4 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_RED.json @@ -0,0 +1 @@ +{"numFailedTestSuites":1,"numFailedTests":1,"numPassedTestSuites":0,"numPassedTests":0,"numPendingTestSuites":0,"numPendingTests":10,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":11,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772446227,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S02] all widget visibility choices save and survive remount","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S02] all widget visibility choices save and survive remount"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a summary failure does not suppress independent trends","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01] a summary failure does not suppress independent trends"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"pending","title":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend"},{"ancestorTitles":[],"duration":249,"failing":false,"failureDetails":[{"matcherResult":{"message":"expect(received).toHaveLength(expected)\n\nExpected length: 2\nReceived length: 0\nReceived object: []","pass":false}}],"failureMessages":["Error: expect(received).toHaveLength(expected)\n\nExpected length: 2\nReceived length: 0\nReceived object: []\n at Object.toHaveLength (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx:133:19)"],"fullName":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772457876,"status":"failed","title":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772458132,"status":"pending","title":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries"}],"endTime":1793772458141,"message":" ● [PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values\n\n expect(received).toHaveLength(expected)\n\n Expected length: 2\n Received length: 0\n Received object: []\n\n 131 | const activity = await screen.findByRole('region', { name: 'Application activity' });\n 132 | const markers = activity.querySelectorAll('svg circle');\n > 133 | expect(markers).toHaveLength(2);\n | ^\n 134 | expect(markers[0]).toHaveAttribute('cy', '20');\n 135 | expect(markers[1]).toHaveAttribute('cy', '140');\n 136 | for (const marker of markers) expect(Number(marker.getAttribute('r'))).toBeGreaterThan(0);\n\n at Object.toHaveLength (src/dashboard-presentation.test.tsx:133:19)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx","startTime":1793772448287,"status":"failed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_TESTS.json b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_TESTS.json new file mode 100644 index 00000000..848b9ea7 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/ACTIVITY_MARKER_TESTS.json @@ -0,0 +1 @@ +{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":1,"numPassedTests":11,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":11,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772508303,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":256,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772511768,"status":"passed","title":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates"},{"ancestorTitles":[],"duration":157,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772512024,"status":"passed","title":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable"},{"ancestorTitles":[],"duration":940,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S02] all widget visibility choices save and survive remount","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772512181,"status":"passed","title":"[PRES-S02] all widget visibility choices save and survive remount"},{"ancestorTitles":[],"duration":237,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772513121,"status":"passed","title":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate"},{"ancestorTitles":[],"duration":71,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a summary failure does not suppress independent trends","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772513358,"status":"passed","title":"[PRES-S01] a summary failure does not suppress independent trends"},{"ancestorTitles":[],"duration":140,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772513430,"status":"passed","title":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields"},{"ancestorTitles":[],"duration":95,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772513570,"status":"passed","title":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning"},{"ancestorTitles":[],"duration":45,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772513665,"status":"passed","title":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage"},{"ancestorTitles":[],"duration":60,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772513710,"status":"passed","title":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend"},{"ancestorTitles":[],"duration":118,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772513771,"status":"passed","title":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values"},{"ancestorTitles":[],"duration":232,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772513889,"status":"passed","title":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries"}],"endTime":1793772514122,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx","startTime":1793772509220,"status":"passed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/CAREER_DOCUMENT_ACTIVITY.md b/docs/ui-modernisation/visual-correction/CAREER_DOCUMENT_ACTIVITY.md new file mode 100644 index 00000000..8e426f0b --- /dev/null +++ b/docs/ui-modernisation/visual-correction/CAREER_DOCUMENT_ACTIVITY.md @@ -0,0 +1,17 @@ +# Career/document surfaces and single-month activity + +4 November 2026. Continue from published and independently verified `79d7bbe0a151472c551dfdc73cce628e92207d5a`, runtimea7d72673. Earlier source-bound evidence remains valid within its scope. New application edits are within approved IM10 visual correction. + +Career: remove decorative overview status/import/recent-CV section surfaces and action-card frames. Keep14 edit targets, completion indicators, warning/error/progress states and every link. Open the workspace navigation, profile/import container, import suggestion, history collection and structured editor without moving their state/ref/ID/conditional boundaries. Form fields, import comparison objects, controls, save/conflict/history, canonical profile ownership and legacy readers remain. No extraction engine or PDF renderer change. + +Documents: move the existing Jump to documents control before the filter stack so phone/keyboard users can reach results immediately. Preserve the same focus-only handler and return action, all filters and URL semantics. Mobile documents become divided articles; desktop table and explicit empty/error states remain. Download/source/permission owners are untouched. + +Activity V15: a nonzero single-month fixture produced move-only SVG paths, leaving the graph blank. Add a filled application point and an outlined response point on the existing common scale. Different radii retain both indicators if values coincide. Recorded values, multi-month paths, zero-data state, legend and accessible exact-value table remain unchanged. No generated/interpolated data is introduced. + +Native evidence:81 tests/seven Career/history/import/evidence/editor/completeness/dashboard suites plus33 document tests/one disjoint suite PASSED (114 distinct). Two new EN/NB document assertions reproduced the late jump control before it moved; the existing focus/no-request checks pass. The marker regression reproduced zero markers. Its first full-suite attempt encountered a prior test's retained PUT call; compare the new test's before/after write count, preserving the no-new-write guarantee. Retain both failure artifacts. An unsupported test selector option was removed after TypeScript rejected it; final types/full lint pass. No additional backend or physical PDF checks are required for these frontend-only changes. + +Rendered preparation: existing calendar, document/mail and Career pilots passed on priora7d72673 export; those are preserved in *-before directories and do not qualify this successor. Existing verifiers have opt-in nine-width output directories and expected-count assertions. Targeted viewport captures show the edited section/message rather than resetting every screenshot to the page top. Career matrix additionally checks that all14 keyboard edit targets are unobscured. A separate activity-marker mode reuses the dashboard fixture while skipping unaffected onboarding/layout-save flows; their published36/72 evidence remains separate. + +Pending: settled source commit/build; pilots;36-configuration Career216, document/mail216, calendar108 and activity36 scans/captures; actual inspection, manifests and publication. These are intended counts, not execution claims. Mail uses intercepted synthetic save/confirmed-send/uncertain-outcome only; no real delivery, provider or model. Full34-route/57-group, complete journeys, human/device/native-language, final integrated security/recovery/release and owner visual acceptance remain OPEN. + +Rollback reverts these presentation changes and marker addition; no schema/data migration or document revision rollback. Review capacity limitation: the previous review agent reached its usage limit; no new independent review or security acceptance is claimed for this successor. diff --git a/docs/ui-modernisation/visual-correction/CAREER_FLAT_ACTIVITY_TESTS.json b/docs/ui-modernisation/visual-correction/CAREER_FLAT_ACTIVITY_TESTS.json new file mode 100644 index 00000000..976ba19e --- /dev/null +++ b/docs/ui-modernisation/visual-correction/CAREER_FLAT_ACTIVITY_TESTS.json @@ -0,0 +1 @@ +{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":7,"numPassedTests":81,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":7,"numTotalTests":81,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772667879,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":434,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"saves factual evidence with provenance and tags","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772671463,"status":"passed","title":"saves factual evidence with provenance and tags"},{"ancestorTitles":[],"duration":252,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"pending evidence reads announce loading without claiming an empty bank or allowing a raced save","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772671897,"status":"passed","title":"pending evidence reads announce loading without claiming an empty bank or allowing a raced save"},{"ancestorTitles":[],"duration":343,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed list reads remain distinct from empty state and retry only GET while preserving the draft","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772672149,"status":"passed","title":"failed list reads remain distinct from empty state and retry only GET while preserving the draft"},{"ancestorTitles":[],"duration":415,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed saves retain factual inputs and retry the same explicit save without reloading the list","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772672493,"status":"passed","title":"failed saves retain factual inputs and retry the same explicit save without reloading the list"},{"ancestorTitles":[],"duration":325,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a pending save prevents duplicate writes and prevents edits being cleared by the response","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772672909,"status":"passed","title":"a pending save prevents duplicate writes and prevents edits being cleared by the response"},{"ancestorTitles":[],"duration":527,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"contextual deletion is named and cancellation performs no writes or draft loss","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772673234,"status":"passed","title":"contextual deletion is named and cancellation performs no writes or draft loss"},{"ancestorTitles":[],"duration":644,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed deletion keeps the row and draft, then reconfirms the same delete without list reload","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772673761,"status":"passed","title":"failed deletion keeps the row and draft, then reconfirms the same delete without list reload"},{"ancestorTitles":[],"duration":82,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"evidence sections and factual fields have semantic headings and required labels in Bokmål","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793772674405,"status":"passed","title":"evidence sections and factual fields have semantic headings and required labels in Bokmål"}],"endTime":1793772674490,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-evidence.test.tsx","startTime":1793772668845,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":4923,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a locale update retains dirty structured and raw drafts without reading their source again","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772675873,"status":"passed","title":"a locale update retains dirty structured and raw drafts without reading their source again"},{"ancestorTitles":[],"duration":2005,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits en canonical projects and certifications through the existing versioned Save without changing their IDs or sibling facts","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772680796,"status":"passed","title":"edits en canonical projects and certifications through the existing versioned Save without changing their IDs or sibling facts"},{"ancestorTitles":[],"duration":2071,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits nb canonical projects and certifications through the existing versioned Save without changing their IDs or sibling facts","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772682801,"status":"passed","title":"edits nb canonical projects and certifications through the existing versioned Save without changing their IDs or sibling facts"},{"ancestorTitles":[],"duration":3997,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"project and certification add/remove controls keep sibling entries and focus before the explicit canonical Save","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772684872,"status":"passed","title":"project and certification add/remove controls keep sibling entries and focus before the explicit canonical Save"},{"ancestorTitles":[],"duration":652,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a pending initial read keeps one request and reports its failure in the latest locale without language writes","invocations":1,"location":null,"numPassingAsserts":15,"retryReasons":[],"startAt":1793772688869,"status":"passed","title":"a pending initial read keeps one request and reports its failure in the latest locale without language writes"},{"ancestorTitles":[],"duration":4812,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a held Save locks every editor mutation, raw input and history restore until its snapshot settles","invocations":1,"location":null,"numPassingAsserts":52,"retryReasons":[],"startAt":1793772689521,"status":"passed","title":"a held Save locks every editor mutation, raw input and history restore until its snapshot settles"},{"ancestorTitles":[],"duration":1504,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"two Save activations before React commits pending state send only one canonical PUT","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772694333,"status":"passed","title":"two Save activations before React commits pending state send only one canonical PUT"},{"ancestorTitles":[],"duration":3460,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed Save retains a scoped localized error and draft for retry through the same Save button","invocations":1,"location":null,"numPassingAsserts":22,"retryReasons":[],"startAt":1793772695837,"status":"passed","title":"a failed Save retains a scoped localized error and draft for retry through the same Save button"},{"ancestorTitles":[],"duration":1211,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the editor exposes named H2/H3 groups and contextual repeated-item remove controls","invocations":1,"location":null,"numPassingAsserts":21,"retryReasons":[],"startAt":1793772699297,"status":"passed","title":"the editor exposes named H2/H3 groups and contextual repeated-item remove controls"},{"ancestorTitles":[],"duration":4789,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"manual edits preserve long-tail data, custom links, opaque IDs and modeled fields without editor controls","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772700508,"status":"passed","title":"manual edits preserve long-tail data, custom links, opaque IDs and modeled fields without editor controls"},{"ancestorTitles":[],"duration":2966,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a rejected save (409) keeps the draft and its original revision for deliberate retry","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772705297,"status":"passed","title":"a rejected save (409) keeps the draft and its original revision for deliberate retry"},{"ancestorTitles":[],"duration":3196,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a rejected save (0) keeps the draft and its original revision for deliberate retry","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772708263,"status":"passed","title":"a rejected save (0) keeps the draft and its original revision for deliberate retry"},{"ancestorTitles":[],"duration":3256,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"consecutive acknowledged career saves advance the revision returned with the saved snapshot","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772711459,"status":"passed","title":"consecutive acknowledged career saves advance the revision returned with the saved snapshot"},{"ancestorTitles":[],"duration":2588,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a delayed prior lifecycle read cannot replace edits in the current career editor","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772714715,"status":"passed","title":"a delayed prior lifecycle read cannot replace edits in the current career editor"},{"ancestorTitles":[],"duration":2057,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a read-only provider cannot edit canonical fields or trigger canonical writes","invocations":1,"location":null,"numPassingAsserts":45,"retryReasons":[],"startAt":1793772717303,"status":"passed","title":"a read-only provider cannot edit canonical fields or trigger canonical writes"}],"endTime":1793772719361,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-editor.test.tsx","startTime":1793772674509,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":939,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"disabled CV worker upload gives actionable en recovery without changing the profile","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772720118,"status":"passed","title":"disabled CV worker upload gives actionable en recovery without changing the profile"},{"ancestorTitles":[],"duration":637,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"disabled CV worker upload gives actionable nb recovery without changing the profile","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772721057,"status":"passed","title":"disabled CV worker upload gives actionable nb recovery without changing the profile"},{"ancestorTitles":[],"duration":1086,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed history reads have a scoped Retry that preserves unsaved source fields","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772721694,"status":"passed","title":"failed history reads have a scoped Retry that preserves unsaved source fields"},{"ancestorTitles":[],"duration":1101,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a dirty canonical profile cannot be replaced through history restore","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772722780,"status":"passed","title":"a dirty canonical profile cannot be replaced through history restore"},{"ancestorTitles":[],"duration":1087,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"each failed comparison can retry its own run without refetching canonical data or losing another comparison","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772723881,"status":"passed","title":"each failed comparison can retry its own run without refetching canonical data or losing another comparison"},{"ancestorTitles":[],"duration":580,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"review shows actual current and imported values and discloses fields outside the comparison","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772724968,"status":"passed","title":"review shows actual current and imported values and discloses fields outside the comparison"},{"ancestorTitles":[],"duration":1082,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"selecting then deselecting low-confidence data sends the explicit empty selection","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772725548,"status":"passed","title":"selecting then deselecting low-confidence data sends the explicit empty selection"},{"ancestorTitles":[],"duration":1688,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"held acceptance locks review choices and all canonical editors and prevents duplicate or overlapping writes","invocations":1,"location":null,"numPassingAsserts":13,"retryReasons":[],"startAt":1793772726630,"status":"passed","title":"held acceptance locks review choices and all canonical editors and prevents duplicate or overlapping writes"},{"ancestorTitles":[],"duration":1415,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed acceptance keeps the chosen stable IDs and shows durable translated recovery without automatically resending","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793772728318,"status":"passed","title":"failed acceptance keeps the chosen stable IDs and shows durable translated recovery without automatically resending"},{"ancestorTitles":[],"duration":1724,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"discarding an import preserves unsaved structured and raw drafts and does not reload the canonical profile","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772729733,"status":"passed","title":"discarding an import preserves unsaved structured and raw drafts and does not reload the canonical profile"},{"ancestorTitles":[],"duration":1550,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"saving canonical changes refreshes history and comparisons and clears choices made against the earlier source","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772731457,"status":"passed","title":"saving canonical changes refreshes history and comparisons and clears choices made against the earlier source"},{"ancestorTitles":[],"duration":1762,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed restore keeps the original source and known history with an explicit retry","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772733007,"status":"passed","title":"a failed restore keeps the original source and known history with an explicit retry"},{"ancestorTitles":[],"duration":1123,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an operation cancellation is guarded while pending and leaves an unsaved source draft intact","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772734769,"status":"passed","title":"an operation cancellation is guarded while pending and leaves an unsaved source draft intact"},{"ancestorTitles":[],"duration":979,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed operation retry stays scoped and only resends the same operation on explicit user retry","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772735892,"status":"passed","title":"a failed operation retry stays scoped and only resends the same operation on explicit user retry"},{"ancestorTitles":[],"duration":692,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Bokmål localizes comparison labels while preserving imported facts named like UI labels or object properties","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772736871,"status":"passed","title":"Bokmål localizes comparison labels while preserving imported facts named like UI labels or object properties"},{"ancestorTitles":[],"duration":716,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the known low-confidence contact placeholder is localized","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772737563,"status":"passed","title":"the known low-confidence contact placeholder is localized"}],"endTime":1793772738280,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-import-review.test.tsx","startTime":1793772719370,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":117,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"first-run workspace presents concise profile, import, general CV and job-specific actions","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772739027,"status":"passed","title":"first-run workspace presents concise profile, import, general CV and job-specific actions"},{"ancestorTitles":[],"duration":77,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"career workspace overview and import state render in Bokmål","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772739144,"status":"passed","title":"career workspace overview and import state render in Bokmål"},{"ancestorTitles":[],"duration":94,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"returning workspace shows missing profile information and recent general and job-specific CVs","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772739221,"status":"passed","title":"returning workspace shows missing profile information and recent general and job-specific CVs"},{"ancestorTitles":[],"duration":44,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"workspace exposes the pending_review import state as a resumable action","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772739316,"status":"passed","title":"workspace exposes the pending_review import state as a resumable action"},{"ancestorTitles":[],"duration":54,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"workspace exposes the queued import state as a resumable action","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772739360,"status":"passed","title":"workspace exposes the queued import state as a resumable action"},{"ancestorTitles":[],"duration":55,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"workspace exposes the failed import state as a resumable action","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772739414,"status":"passed","title":"workspace exposes the failed import state as a resumable action"},{"ancestorTitles":[],"duration":45,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"workspace represents loading and profile-load errors without inventing status","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772739469,"status":"passed","title":"workspace represents loading and profile-load errors without inventing status"},{"ancestorTitles":[],"duration":62,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a settled failed profile read hides the completeness score and retries only through its existing owner","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772739514,"status":"passed","title":"a settled failed profile read hides the completeness score and retries only through its existing owner"},{"ancestorTitles":[],"duration":78,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an unknown import read cannot claim no imports, completion or first-run guidance","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772739576,"status":"passed","title":"an unknown import read cannot claim no imports, completion or first-run guidance"},{"ancestorTitles":[],"duration":63,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed import read stays unknown and keeps its own retry separate from the profile read","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772739654,"status":"passed","title":"a failed import read stays unknown and keeps its own retry separate from the profile read"},{"ancestorTitles":[],"duration":143,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed recent-CV reads do not imply first-run and retry the same list without writes","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772739717,"status":"passed","title":"failed recent-CV reads do not imply first-run and retry the same list without writes"},{"ancestorTitles":[],"duration":3851,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en overview opens every canonical section directly without saving or reloading the profile","invocations":1,"location":null,"numPassingAsserts":59,"retryReasons":[],"startAt":1793772739860,"status":"passed","title":"en overview opens every canonical section directly without saving or reloading the profile"},{"ancestorTitles":[],"duration":3925,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb overview opens every canonical section directly without saving or reloading the profile","invocations":1,"location":null,"numPassingAsserts":59,"retryReasons":[],"startAt":1793772743711,"status":"passed","title":"nb overview opens every canonical section directly without saving or reloading the profile"},{"ancestorTitles":[],"duration":605,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en section indicators retain server scoring and stop claiming saved status for an unsaved draft","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772747636,"status":"passed","title":"en section indicators retain server scoring and stop claiming saved status for an unsaved draft"},{"ancestorTitles":[],"duration":375,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb section indicators retain server scoring and stop claiming saved status for an unsaved draft","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772748241,"status":"passed","title":"nb section indicators retain server scoring and stop claiming saved status for an unsaved draft"},{"ancestorTitles":[],"duration":157,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the parent exposes failed import-read retry without canonical saves or history restores","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772748616,"status":"passed","title":"the parent exposes failed import-read retry without canonical saves or history restores"},{"ancestorTitles":[],"duration":715,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"changing language in overview cannot refetch or overwrite a dirty canonical draft","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772748773,"status":"passed","title":"changing language in overview cannot refetch or overwrite a dirty canonical draft"},{"ancestorTitles":[],"duration":466,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"visited evidence keeps its draft across sections without an unvisited or repeated list request","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772749489,"status":"passed","title":"visited evidence keeps its draft across sections without an unvisited or repeated list request"}],"endTime":1793772749955,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-workspace-page.test.tsx","startTime":1793772738288,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":164,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772750808,"status":"passed","title":"A1 week glance shows recorded upcoming dates from attention data without inventing missing dates"},{"ancestorTitles":[],"duration":157,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772750973,"status":"passed","title":"[PRES-S01] exact monthly values, sample, custom statuses and separate salary group remain readable"},{"ancestorTitles":[],"duration":819,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S02] all widget visibility choices save and survive remount","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772751130,"status":"passed","title":"[PRES-S02] all widget visibility choices save and survive remount"},{"ancestorTitles":[],"duration":201,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772751949,"status":"passed","title":"[PRES-S01] a trends failure retains summary and retries without showing a false zero response rate"},{"ancestorTitles":[],"duration":62,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a summary failure does not suppress independent trends","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772752150,"status":"passed","title":"[PRES-S01] a summary failure does not suppress independent trends"},{"ancestorTitles":[],"duration":225,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772752212,"status":"passed","title":"[PRES-J05 J09 S01] raw status and company destinations use supported exact query fields"},{"ancestorTitles":[],"duration":166,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793772752437,"status":"passed","title":"[PRES-S01 S02] period changes only reload trends and retain current snapshot meaning"},{"ancestorTitles":[],"duration":46,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772752603,"status":"passed","title":"[PRES-S01] zero activity is an explicit window-empty state without a fabricated response percentage"},{"ancestorTitles":[],"duration":77,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772752649,"status":"passed","title":"[PRES-S01] activity chart uses one shared zero-based scale and a readable legend"},{"ancestorTitles":[],"duration":84,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772752726,"status":"passed","title":"[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values"},{"ancestorTitles":[],"duration":140,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772752810,"status":"passed","title":"[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries"}],"endTime":1793772752950,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\dashboard-presentation.test.tsx","startTime":1793772749966,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":16,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the first history read announces progress without claiming an empty history","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772753597,"status":"passed","title":"the first history read announces progress without claiming an empty history"},{"ancestorTitles":[],"duration":74,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed history read retains known rows and retries only through its history owner","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772753614,"status":"passed","title":"a failed history read retains known rows and retries only through its history owner"},{"ancestorTitles":[],"duration":8,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a settled zero-version history is a named empty section rather than a hidden capability","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772753688,"status":"passed","title":"a settled zero-version history is a named empty section rather than a hidden capability"},{"ancestorTitles":[],"duration":7,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"one current version remains visible in Bokmål without offering a restore of the current source","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772753696,"status":"passed","title":"one current version remains visible in Bokmål without offering a restore of the current source"},{"ancestorTitles":[],"duration":59,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the collapsed history keeps twelve rows and contextual restore actions pass the exact selected version","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793772753703,"status":"passed","title":"the collapsed history keeps twelve rows and contextual restore actions pass the exact selected version"},{"ancestorTitles":[],"duration":36,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"dirty source state explains the restore guard and re-enables the same version after the parent clears it","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793772753762,"status":"passed","title":"dirty source state explains the restore guard and re-enables the same version after the parent clears it"},{"ancestorTitles":[],"duration":28,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the existing overall loading guard still blocks history restores","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772753798,"status":"passed","title":"the existing overall loading guard still blocks history restores"},{"ancestorTitles":[],"duration":37,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"restore failure stays visible with the known history until the parent clears it and retry is explicit","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772753827,"status":"passed","title":"restore failure stays visible with the known history until the parent clears it and retry is explicit"}],"endTime":1793772753865,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-history-view.test.tsx","startTime":1793772752958,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preserves unknown completeness label constructor as data","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772754072,"status":"passed","title":"preserves unknown completeness label constructor as data"},{"ancestorTitles":[],"duration":0,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preserves unknown completeness label toString as data","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772754073,"status":"passed","title":"preserves unknown completeness label toString as data"},{"ancestorTitles":[],"duration":0,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preserves unknown completeness label __proto__ as data","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772754073,"status":"passed","title":"preserves unknown completeness label __proto__ as data"},{"ancestorTitles":[],"duration":1,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preserves unknown completeness label Custom section as data","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772754073,"status":"passed","title":"preserves unknown completeness label Custom section as data"},{"ancestorTitles":[],"duration":0,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"translates a known completeness label using its catalogue key","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793772754074,"status":"passed","title":"translates a known completeness label using its catalogue key"}],"endTime":1793772754074,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\career-completeness-labels.test.ts","startTime":1793772753871,"status":"passed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/DOCUMENT_FLAT_TESTS.json b/docs/ui-modernisation/visual-correction/DOCUMENT_FLAT_TESTS.json new file mode 100644 index 00000000..2cfd4c4f --- /dev/null +++ b/docs/ui-modernisation/visual-correction/DOCUMENT_FLAT_TESTS.json @@ -0,0 +1 @@ +{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":1,"numPassedTests":33,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":33,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772792177,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":["en unknown attachment purposes"],"duration":257,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for constructor","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772795992,"status":"passed","title":"uses the localized Other label for constructor"},{"ancestorTitles":["en unknown attachment purposes"],"duration":108,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for toString","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772796249,"status":"passed","title":"uses the localized Other label for toString"},{"ancestorTitles":["en unknown attachment purposes"],"duration":111,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for __proto__","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772796357,"status":"passed","title":"uses the localized Other label for __proto__"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":124,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for constructor","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772796468,"status":"passed","title":"uses the localized Other label for constructor"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":122,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for toString","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772796592,"status":"passed","title":"uses the localized Other label for toString"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":125,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for __proto__","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793772796714,"status":"passed","title":"uses the localized Other label for __proto__"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":108,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-30T09:15:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772796839,"status":"passed","title":"does not invent a timestamp from 2026-02-30T09:15:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":77,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T24:15:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772796947,"status":"passed","title":"does not invent a timestamp from 2026-02-12T24:15:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":116,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:61:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797024,"status":"passed","title":"does not invent a timestamp from 2026-02-12T09:61:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":114,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:15:00junk","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797140,"status":"passed","title":"does not invent a timestamp from 2026-02-12T09:15:00junk"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":123,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates preserves fractional-second wall-clock metadata without timezone conversion","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797254,"status":"passed","title":"preserves fractional-second wall-clock metadata without timezone conversion"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":140,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-30T09:15:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797378,"status":"passed","title":"does not invent a timestamp from 2026-02-30T09:15:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":123,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T24:15:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797518,"status":"passed","title":"does not invent a timestamp from 2026-02-12T24:15:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":122,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:61:00","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797641,"status":"passed","title":"does not invent a timestamp from 2026-02-12T09:61:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":108,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:15:00junk","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797763,"status":"passed","title":"does not invent a timestamp from 2026-02-12T09:15:00junk"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":74,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates preserves fractional-second wall-clock metadata without timezone conversion","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793772797871,"status":"passed","title":"preserves fractional-second wall-clock metadata without timezone conversion"},{"ancestorTitles":[],"duration":152,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en document filters and results support request-free keyboard navigation","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772797945,"status":"passed","title":"en document filters and results support request-free keyboard navigation"},{"ancestorTitles":[],"duration":152,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb document filters and results support request-free keyboard navigation","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772798097,"status":"passed","title":"nb document filters and results support request-free keyboard navigation"},{"ancestorTitles":[],"duration":123,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772798249,"status":"passed","title":"[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search"},{"ancestorTitles":[],"duration":580,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state","invocations":1,"location":null,"numPassingAsserts":15,"retryReasons":[],"startAt":1793772798372,"status":"passed","title":"[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state"},{"ancestorTitles":[],"duration":219,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] literal search all remains free text and preserves unrelated navigation context","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772798952,"status":"passed","title":"[P7-07 documents] literal search all remains free text and preserves unrelated navigation context"},{"ancestorTitles":[],"duration":475,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context","invocations":1,"location":null,"numPassingAsserts":23,"retryReasons":[],"startAt":1793772799171,"status":"passed","title":"[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context"},{"ancestorTitles":[],"duration":242,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772799646,"status":"passed","title":"[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata"},{"ancestorTitles":[],"duration":107,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772799888,"status":"passed","title":"[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work"},{"ancestorTitles":[],"duration":308,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793772799995,"status":"passed","title":"[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct"},{"ancestorTitles":[],"duration":215,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793772800303,"status":"passed","title":"[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch"},{"ancestorTitles":[],"duration":256,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index","invocations":1,"location":null,"numPassingAsserts":17,"retryReasons":[],"startAt":1793772800518,"status":"passed","title":"[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index"},{"ancestorTitles":[],"duration":185,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels","invocations":1,"location":null,"numPassingAsserts":24,"retryReasons":[],"startAt":1793772800774,"status":"passed","title":"[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels"},{"ancestorTitles":[],"duration":169,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal","invocations":1,"location":null,"numPassingAsserts":13,"retryReasons":[],"startAt":1793772800959,"status":"passed","title":"[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal"},{"ancestorTitles":[],"duration":199,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793772801128,"status":"passed","title":"[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button"},{"ancestorTitles":[],"duration":291,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793772801327,"status":"passed","title":"[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL"},{"ancestorTitles":[],"duration":104,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793772801618,"status":"passed","title":"[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body"},{"ancestorTitles":[],"duration":95,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions","invocations":1,"location":null,"numPassingAsserts":15,"retryReasons":[],"startAt":1793772801722,"status":"passed","title":"[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions"}],"endTime":1793772801819,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\documents-page.test.tsx","startTime":1793772793122,"status":"passed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/DOCUMENT_JUMP_RED.json b/docs/ui-modernisation/visual-correction/DOCUMENT_JUMP_RED.json new file mode 100644 index 00000000..3bde96d0 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/DOCUMENT_JUMP_RED.json @@ -0,0 +1 @@ +{"numFailedTestSuites":1,"numFailedTests":2,"numPassedTestSuites":0,"numPassedTests":0,"numPendingTestSuites":0,"numPendingTests":31,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":33,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793772769479,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":["en unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for constructor","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for constructor"},{"ancestorTitles":["en unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for toString","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for toString"},{"ancestorTitles":["en unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en unknown attachment purposes uses the localized Other label for __proto__","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for __proto__"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for constructor","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for constructor"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for toString","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for toString"},{"ancestorTitles":["nb unknown attachment purposes"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb unknown attachment purposes uses the localized Other label for __proto__","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"uses the localized Other label for __proto__"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-30T09:15:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-30T09:15:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T24:15:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T24:15:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:61:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T09:61:00"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:15:00junk","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T09:15:00junk"},{"ancestorTitles":["en uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en uploaded wall-clock dates preserves fractional-second wall-clock metadata without timezone conversion","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"preserves fractional-second wall-clock metadata without timezone conversion"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-30T09:15:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-30T09:15:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T24:15:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T24:15:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:61:00","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T09:61:00"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates does not invent a timestamp from 2026-02-12T09:15:00junk","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"does not invent a timestamp from 2026-02-12T09:15:00junk"},{"ancestorTitles":["nb uploaded wall-clock dates"],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb uploaded wall-clock dates preserves fractional-second wall-clock metadata without timezone conversion","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772772951,"status":"pending","title":"preserves fractional-second wall-clock metadata without timezone conversion"},{"ancestorTitles":[],"duration":293,"failing":false,"failureDetails":[{"matcherResult":{"message":"expect(received).toBeTruthy()\n\nReceived: 0","pass":false}}],"failureMessages":["Error: expect(received).toBeTruthy()\n\nReceived: 0\n at toBeTruthy (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\documents-page.test.tsx:105:83)"],"fullName":"en document filters and results support request-free keyboard navigation","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772772952,"status":"failed","title":"en document filters and results support request-free keyboard navigation"},{"ancestorTitles":[],"duration":127,"failing":false,"failureDetails":[{"matcherResult":{"message":"expect(received).toBeTruthy()\n\nReceived: 0","pass":false}}],"failureMessages":["Error: expect(received).toBeTruthy()\n\nReceived: 0\n at toBeTruthy (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\documents-page.test.tsx:105:83)"],"fullName":"nb document filters and results support request-free keyboard navigation","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793772773257,"status":"failed","title":"nb document filters and results support request-free keyboard navigation"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] validates URL filters and reads one metadata page with literal Nordic search"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] server pagination and filter history preserve unknown query, hash and router state"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] literal search all remains free text and preserves unrelated navigation context","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] literal search all remains free text and preserves unrelated navigation context"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] a batched sort selection and immediate search preserve both filters and navigation context"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] incompatible pending and failed queries hide old actions and late results cannot replace current metadata"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] Retry is one guarded GET and a completion after leaving cannot publish or start follow-up work"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] a true empty index, filtered no-match and request failure remain distinct"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] English and Bokmål presentation changes preserve query context without a metadata refetch"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] an out-of-range page offers explicit first-page recovery without claiming an empty index"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] seven resource kinds retain exact source actions, frozen provenance and honest date labels"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] attachment download is explicit, API-relative and single-flight with original MIME, filename and URL disposal"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] a completed download preserves deliberate Search focus instead of restoring the old button"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] download errors retry only that file and late download completion after leaving creates no URL"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] a published historical CV retains its percent-encoded Nordic public slug without fetching its body"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793772773384,"status":"pending","title":"[P7-07 documents] malformed external and mismatched resource paths never become source, public or download actions"}],"endTime":1793772773397,"message":" ● en document filters and results support request-free keyboard navigation\n\n expect(received).toBeTruthy()\n\n Received: 0\n\n 103 | const jump = screen.getByRole('button', { name: copy('documentsJumpResults', {}, language) });\n 104 | const search = screen.getByRole('textbox', { name: copy('documentsSearch', {}, language) });\n > 105 | expect(jump.compareDocumentPosition(search) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();\n | ^\n 106 | fireEvent.click(jump);\n 107 | expect(screen.getByRole('region', { name: copy('documentsYourDocuments', {}, language) })).toHaveFocus();\n 108 | fireEvent.click(screen.getByRole('button', { name: copy('documentsReturnFilters', {}, language) }));\n\n at toBeTruthy (src/documents-page.test.tsx:105:83)\n\n ● nb document filters and results support request-free keyboard navigation\n\n expect(received).toBeTruthy()\n\n Received: 0\n\n 103 | const jump = screen.getByRole('button', { name: copy('documentsJumpResults', {}, language) });\n 104 | const search = screen.getByRole('textbox', { name: copy('documentsSearch', {}, language) });\n > 105 | expect(jump.compareDocumentPosition(search) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();\n | ^\n 106 | fireEvent.click(jump);\n 107 | expect(screen.getByRole('region', { name: copy('documentsYourDocuments', {}, language) })).toHaveFocus();\n 108 | fireEvent.click(screen.getByRole('button', { name: copy('documentsReturnFilters', {}, language) }));\n\n at toBeTruthy (src/documents-page.test.tsx:105:83)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\documents-page.test.tsx","startTime":1793772770386,"status":"failed","summary":""}],"wasInterrupted":false} diff --git a/docs/ui-modernisation/visual-correction/evidence/calendar-before/PILOT.json b/docs/ui-modernisation/visual-correction/evidence/calendar-before/PILOT.json new file mode 100644 index 00000000..3171b831 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/evidence/calendar-before/PILOT.json @@ -0,0 +1,74 @@ +{ + "screens": [ + { + "width": 320, + "language": "en", + "mode": "light", + "state": "agenda", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "month", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "linked-application", + "violations": [] + } + ], + "journeys": [ + { + "width": 320, + "language": "en", + "mode": "light", + "agendaMonthSelectionPersistedPreferenceBoundedMonthNavigationLinkedDeadline": "PASS" + } + ], + "errors": [], + "unexpectedWrites": [], + "reads": [ + { + "width": 320, + "language": "en", + "mode": "light", + "from": "2026-11-01", + "to": "2026-11-30" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "from": "2026-12-01", + "to": "2026-12-31" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "from": "2026-11-01", + "to": "2026-11-30" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "from": "2026-11-01", + "to": "2026-11-30" + } + ], + "blockedExternal": [ + "https://fonts.googleapis.com", + "https://fonts.googleapis.com" + ], + "limits": [ + "Controlled API fixtures, not a live backend/provider or reminder delivery.", + "Date-level recorded deadline/follow-up display; no fabricated interview appointments or source timezone reconstruction.", + "No native-device, physical rendering, final security or release qualification." + ] +} \ No newline at end of file diff --git a/docs/ui-modernisation/visual-correction/evidence/calendar-before/agenda-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/calendar-before/agenda-en-light-320.png new file mode 100644 index 00000000..8294459f Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/calendar-before/agenda-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/calendar-before/linked-application-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/calendar-before/linked-application-en-light-320.png new file mode 100644 index 00000000..a09fb043 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/calendar-before/linked-application-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/calendar-before/month-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/calendar-before/month-en-light-320.png new file mode 100644 index 00000000..c3e9ffb2 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/calendar-before/month-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/PILOT.json b/docs/ui-modernisation/visual-correction/evidence/career-before/PILOT.json new file mode 100644 index 00000000..bb2f5d91 --- /dev/null +++ b/docs/ui-modernisation/visual-correction/evidence/career-before/PILOT.json @@ -0,0 +1,99 @@ +{ + "screens": [ + { + "width": 320, + "language": "en", + "mode": "light", + "state": "overview", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "profile", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "import-review", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "library", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "cv-editor", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "letter", + "violations": [] + } + ], + "journeys": [ + { + "width": 320, + "language": "en", + "mode": "light", + "all14KeyboardEditTargets": "PASS", + "canonicalSaveStableIds": "PASS", + "legacyImportComparisonSelectionNoApply": "PASS", + "libraryKeyboardOpen": "PASS", + "cvResponsiveRename": "PASS", + "letterResponsiveManualSave": "PASS" + } + ], + "errors": [], + "unexpectedWrites": [], + "mockedWrites": [ + { + "width": 320, + "language": "en", + "mode": "light", + "action": "canonicalProjectSave" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "action": "variantRename" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "action": "manualLetterSave" + } + ], + "renderReads": 1, + "unavailableReads": [ + "/api/companies", + "/api/companies", + "/api/cv/themes/modern/preview", + "/api/jobapplications/42/cv", + "/api/companies" + ], + "blockedExternal": [ + "https://fonts.googleapis.com", + "https://fonts.googleapis.com", + "https://fonts.googleapis.com" + ], + "limits": [ + "All API and preview responses are controlled fixtures; not backend end-to-end or physical PDF qualification.", + "Legacy import comparison and selection only; no parser, model, import acceptance or source mutation.", + "No native device, screen reader, provider, integrated security, deployment or release acceptance." + ] +} \ No newline at end of file diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/cv-editor-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/cv-editor-en-light-320.png new file mode 100644 index 00000000..24ca9f6f Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/cv-editor-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/import-review-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/import-review-en-light-320.png new file mode 100644 index 00000000..24133529 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/import-review-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/letter-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/letter-en-light-320.png new file mode 100644 index 00000000..833b906e Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/letter-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/library-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/library-en-light-320.png new file mode 100644 index 00000000..749cbcd5 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/library-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/overview-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/overview-en-light-320.png new file mode 100644 index 00000000..ba99c13a Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/overview-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/career-before/profile-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/career-before/profile-en-light-320.png new file mode 100644 index 00000000..5e206643 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/career-before/profile-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/PILOT.json b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/PILOT.json new file mode 100644 index 00000000..d3fed53f --- /dev/null +++ b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/PILOT.json @@ -0,0 +1,81 @@ +{ + "screens": [ + { + "width": 320, + "language": "en", + "mode": "light", + "state": "documents", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "document-filtered", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "inbox", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "message", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "send-confirmation", + "violations": [] + }, + { + "width": 320, + "language": "en", + "mode": "light", + "state": "uncertain-send", + "violations": [] + } + ], + "journeys": [ + { + "width": 320, + "language": "en", + "mode": "light", + "documentFilterFocusDownloadSource": "PASS", + "inboxFocusPlainTextReplySaveExplicitConfirmUncertainNoRetry": "PASS" + } + ], + "errors": [], + "unexpectedWrites": [], + "mockedWrites": [ + { + "width": 320, + "language": "en", + "mode": "light", + "action": "saveDraft" + }, + { + "width": 320, + "language": "en", + "mode": "light", + "action": "confirmedSendFixtureUncertain" + } + ], + "unavailableReads": [], + "blockedExternal": [ + "https://fonts.googleapis.com", + "https://fonts.googleapis.com" + ], + "limits": [ + "Synthetic metadata/binary/mail/provider fixtures; not live storage, mailbox, OAuth or delivery.", + "Browser covers draft save, explicit confirmation and controlled uncertain outcome only; other recovery/conflict paths remain targeted tests.", + "No real sending, native device, final integrated security or release acceptance." + ] +} \ No newline at end of file diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/document-filtered-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/document-filtered-en-light-320.png new file mode 100644 index 00000000..d287b642 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/document-filtered-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/documents-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/documents-en-light-320.png new file mode 100644 index 00000000..50acf769 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/documents-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/inbox-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/inbox-en-light-320.png new file mode 100644 index 00000000..e0eaf978 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/inbox-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/message-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/message-en-light-320.png new file mode 100644 index 00000000..578cecca Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/message-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/send-confirmation-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/send-confirmation-en-light-320.png new file mode 100644 index 00000000..a1c35a9d Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/send-confirmation-en-light-320.png differ diff --git a/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/uncertain-send-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/uncertain-send-en-light-320.png new file mode 100644 index 00000000..92491188 Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/documents-inbox-before/uncertain-send-en-light-320.png differ diff --git a/job-tracker-ui/scripts/verify-a3-calendar.mjs b/job-tracker-ui/scripts/verify-a3-calendar.mjs index 358e1309..f42728a5 100644 --- a/job-tracker-ui/scripts/verify-a3-calendar.mjs +++ b/job-tracker-ui/scripts/verify-a3-calendar.mjs @@ -7,12 +7,15 @@ import path from 'node:path'; import assert from 'node:assert/strict'; const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText; const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`); -const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/calendar/browser'); fs.mkdirSync(out, { recursive: true }); +const visualMatrix = process.env.A3_VISUAL_MATRIX === '1'; +const widths = process.env.A3_PILOT ? [320] : visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440]; +const expectedJourneys = widths.length * (process.env.A3_PILOT ? 1 : 4); +const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/calendar' : '../../docs/ui-modernisation/evidence/calendar/browser'); fs.mkdirSync(out, { recursive: true }); const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], reads: [], blockedExternal: [] }; const now = new Date(), month = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; const browser = await chromium.launch(); try { - for (const width of (process.env.A3_PILOT ? [320] : [320, 390, 768, 1440])) for (const language of (process.env.A3_PILOT ? ['en'] : ['en', 'nb'])) for (const mode of (process.env.A3_PILOT ? ['light'] : ['light', 'dark'])) { + for (const width of widths) for (const language of (process.env.A3_PILOT ? ['en'] : ['en', 'nb'])) for (const mode of (process.env.A3_PILOT ? ['light'] : ['light', 'dark'])) { console.log(`Checking ${width}/${language}/${mode}`); const t = (key, params = {}) => translations[language][key].replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? `{${name}}`)); const context = await browser.newContext({ viewport: { width, height: 1000 }, reducedMotion: 'reduce' }); const page = await context.newPage(); @@ -39,7 +42,7 @@ try { const scan = async state => { assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1), false, state); const axe = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); report.screens.push({ width, language, mode, state, violations: axe.violations }); assert.deepEqual(axe.violations, []); - await page.evaluate(() => window.scrollTo(0, 0)); await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: true }); + await page.evaluate(() => window.scrollTo(0, 0)); await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: width >= 768 }); }; await page.goto('http://localhost:4338/reminders'); const calendar = page.getByRole('region', { name: t('calendarAgendaTitle'), exact: true }); await calendar.getByRole('link', { name: /Electrical engineer/ }).waitFor(); @@ -61,6 +64,7 @@ try { assert.equal((await page.locator('main').innerText()).includes('Invalid Date'), false); await scan('linked-application'); report.journeys.push({ width, language, mode, agendaMonthSelectionPersistedPreferenceBoundedMonthNavigationLinkedDeadline: 'PASS' }); await context.close(); } + assert.equal(report.journeys.length, expectedJourneys); assert.equal(report.screens.length, expectedJourneys * 3); assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []); assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com')); console.log(`PASS:${report.journeys.length} calendar journeys,${report.screens.length} axe scans/captures,zero writes.`); } finally { fs.writeFileSync(path.join(out, process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['Controlled API fixtures, not a live backend/provider or reminder delivery.', 'Date-level recorded deadline/follow-up display; no fabricated interview appointments or source timezone reconstruction.', 'No native-device, physical rendering, final security or release qualification.'] }, null, 2)); await browser.close(); } diff --git a/job-tracker-ui/scripts/verify-a3-career.mjs b/job-tracker-ui/scripts/verify-a3-career.mjs index 58f1a493..0e18a4d1 100644 --- a/job-tracker-ui/scripts/verify-a3-career.mjs +++ b/job-tracker-ui/scripts/verify-a3-career.mjs @@ -7,12 +7,13 @@ import fs from 'node:fs'; import path from 'node:path'; const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText; const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`); -const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/career/browser'); +const visualMatrix = process.env.A3_VISUAL_MATRIX === '1'; +const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/career' : '../../docs/ui-modernisation/evidence/career/browser'); fs.mkdirSync(out, { recursive: true }); const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], renderReads: 0, unavailableReads: [], blockedExternal: [] }; const browser = await chromium.launch(); try { - const configurations = process.env.A3_BOUNDARY ? [320, 390, 768, 1000, 1001, 1440].map(width => [width, 'en', 'light']) : process.env.A3_PILOT ? [[320, 'en', 'light']] : [320, 390, 768, 1440].flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode]))); + const configurations = process.env.A3_BOUNDARY ? [320, 390, 768, 1000, 1001, 1440].map(width => [width, 'en', 'light']) : process.env.A3_PILOT ? [[320, 'en', 'light']] : (visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440]).flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode]))); for (const [width, language, mode] of configurations) { console.log(`Checking ${width}/${language}/${mode}`); const t = key => { assert.ok(translations[language][key], key); return translations[language][key]; }; @@ -66,11 +67,13 @@ try { if (Object.hasOwn(fixtures, p)) return answer(fixtures[p]); report.unavailableReads.push(p); return route.fulfill({ status: 404, contentType: 'application/json', body: '{}' }); }); - const scan = async state => { + const scan = async (state, target) => { + if (target) await target.evaluate(node => node.scrollIntoView({ block: 'center', behavior: 'instant' })); + else await page.evaluate(() => window.scrollTo(0, 0)); assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1), false, `overflow:${state}`); const result = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); - report.screens.push({ width, language, mode, state, violations: result.violations }); assert.deepEqual(result.violations, [], `${state} accessibility`); - await page.evaluate(() => window.scrollTo(0, 0)); await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: true }); + report.screens.push({ width, language, mode, state, capture: target ? 'targeted viewport' : width >= 768 ? 'full page at top' : 'viewport at top', violations: result.violations }); assert.deepEqual(result.violations, [], `${state} accessibility`); + await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: width >= 768 && !target }); }; const careerSection = async key => { if (width >= 600) await page.getByRole('tab', { name: t(key), exact: true }).click(); @@ -81,7 +84,7 @@ try { for (const key of ['contact', 'summary', 'skills', 'interests', 'awards', 'publications', 'organisations', 'references', 'languages', 'jobs', 'education', 'certifications', 'projects', 'otherSections']) { await page.locator(`[data-edit-career-section="${key}"]`).press('Enter'); await expect.poll(() => page.locator(`#career-section-${key}`).evaluate(node => node.contains(document.activeElement))).toBe(true); - if (process.env.A3_BOUNDARY) await expect.poll(async () => page.evaluate(() => { + if (process.env.A3_BOUNDARY || visualMatrix) await expect.poll(async () => page.evaluate(() => { const active = document.activeElement, rect = active.getBoundingClientRect(); const x = Math.min(innerWidth - 1, Math.max(0, rect.x + rect.width / 2)), y = Math.min(innerHeight - 1, Math.max(0, rect.y + rect.height / 2)); const hit = document.elementFromPoint(x, y); @@ -91,10 +94,10 @@ try { } await page.locator('[data-edit-career-section="projects"]').click(); const projectName = page.locator('#career-section-projects input').first(); await projectName.fill('Updated community project'); - await page.getByRole('button', { name: t('profileSaveChanges'), exact: true }).click(); await expect.poll(() => career.revision).toBe(8); await expect(projectName).toBeEnabled(); await scan('profile'); + await page.getByRole('button', { name: t('profileSaveChanges'), exact: true }).click(); await expect.poll(() => career.revision).toBe(8); await expect(projectName).toBeEnabled(); await scan('profile', projectName); await careerSection('careerWorkspaceImportTab'); const run = page.locator('#career-import-run-41'); await expect(run).toContainText('A2'); await expect(run).toContainText('B1'); - await run.getByRole('checkbox').check(); await expect(run.getByRole('checkbox')).toBeChecked(); await scan('import-review'); + await run.getByRole('checkbox').check(); await expect(run.getByRole('checkbox')).toBeChecked(); await scan('import-review', run); assert.equal(report.mockedWrites.filter(row => row.width === width && row.language === language && row.mode === mode).length, 1); await page.goto('http://localhost:4338/career/builder'); const cvCard = page.getByRole('link', { name: t('cvDashboardOpenNamed').replace('{name}', 'Teacher CV'), exact: true }); @@ -107,7 +110,7 @@ try { assert.ok(width > 1000 ? right.x >= left.x + left.width - 2 : right.y >= left.y + left.height - 2, 'CV pane geometry'); } const cvName = page.getByRole('textbox', { name: t('cvEditorName'), exact: true }); await cvName.fill('Updated teacher CV'); - await expect.poll(() => variant.name, { timeout: 15000 }).toBe('Updated teacher CV'); await scan('cv-editor'); + await expect.poll(() => variant.name, { timeout: 15000 }).toBe('Updated teacher CV'); await scan('cv-editor', cvName); await page.goto('http://localhost:4338/jobs/42?section=application&sub=letter'); const text = page.getByRole('textbox', { name: t('jobDetailsCoverLetter'), exact: true }); await expect(text).toBeEnabled(); await text.fill('Dear team\n\nUpdated teaching experience.'); @@ -117,7 +120,7 @@ try { await expect(letterPreview).toContainText('Updated teaching experience.'); const panes = page.locator('[data-letter-studio-layout] > div'); const left = await panes.nth(0).boundingBox(), right = await panes.nth(1).boundingBox(); assert.ok(width > 1000 ? right.x >= left.x + left.width - 2 : right.y >= left.y + left.height - 2, 'Letter pane geometry'); } - await page.getByRole('button', { name: t('save'), exact: true }).click(); await expect.poll(() => letter.revision).toBe(8); await expect(text).toBeEnabled(); await scan('letter'); + await page.getByRole('button', { name: t('save'), exact: true }).click(); await expect.poll(() => letter.revision).toBe(8); await expect(text).toBeEnabled(); await scan('letter', text); report.journeys.push({ width, language, mode, all14KeyboardEditTargets: 'PASS', canonicalSaveStableIds: 'PASS', legacyImportComparisonSelectionNoApply: 'PASS', libraryKeyboardOpen: 'PASS', cvResponsiveRename: 'PASS', letterResponsiveManualSave: 'PASS' }); await context.close(); } diff --git a/job-tracker-ui/scripts/verify-a3-dashboard.mjs b/job-tracker-ui/scripts/verify-a3-dashboard.mjs index 30327b1f..342e8e97 100644 --- a/job-tracker-ui/scripts/verify-a3-dashboard.mjs +++ b/job-tracker-ui/scripts/verify-a3-dashboard.mjs @@ -5,11 +5,12 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; const visualMatrix = process.env.A3_VISUAL_MATRIX === '1'; +const markerOnly = process.env.A3_ACTIVITY_MARKER === '1'; const widths = process.env.A3_PILOT ? [320] : visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440]; const languages = process.env.A3_PILOT ? ['en'] : ['en', 'nb']; const modes = process.env.A3_PILOT ? ['light'] : ['light', 'dark']; const expectedJourneys = widths.length * languages.length * modes.length; -const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/dashboard' : '../../docs/ui-modernisation/evidence/dashboard/browser'); +const out = path.resolve(import.meta.dirname, markerOnly ? '../../docs/ui-modernisation/visual-correction/evidence/activity-markers' : visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/dashboard' : '../../docs/ui-modernisation/evidence/dashboard/browser'); fs.mkdirSync(out, { recursive: true }); const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], blockedExternal: [] }; const browser = await chromium.launch(); @@ -68,6 +69,18 @@ try { const a = document.querySelector('[data-dashboard-widget=agenda]').getBoundingClientRect(), b = document.querySelector('[data-dashboard-widget=funnel]').getBoundingClientRect(); return Math.abs(a.top - b.top) < 2 && a.left < b.left; }); + if (markerOnly) { + const activity = page.locator('[data-dashboard-widget=activity]'); + await page.waitForFunction(() => document.querySelectorAll('[data-dashboard-widget=activity] svg circle').length === 2); + const markers = activity.locator('svg circle'); + assert.equal(await markers.nth(0).getAttribute('cy'), '20'); assert.equal(await markers.nth(1).getAttribute('cy'), '140'); + await activity.scrollIntoViewIfNeeded(); + const panel = activity.getByRole('group'); if (await panel.count()) await panel.evaluate(node => { node.scrollTop = 0; }); + const scan = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); + report.screens.push({ width, language, mode, state: 'single-month-markers', violations: scan.violations }); assert.deepEqual(scan.violations, []); + await page.screenshot({ path: path.join(out, `single-month-${language}-${mode}-${width}.png`) }); + report.journeys.push({ width, language, mode, recordedSingleMonthMarkersSharedScale: 'PASS' }); await context.close(); continue; + } const scan = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); report.screens.push({ width, language, mode, violations: scan.violations }); assert.deepEqual(scan.violations, []); await page.screenshot({ path: path.join(out, `dashboard-${language}-${mode}-${width}.png`), fullPage: true }); @@ -100,9 +113,9 @@ try { } assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []); assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com')); - assert.equal(report.mockedWrites.length, expectedJourneys); + assert.equal(report.mockedWrites.length, markerOnly ? 0 : expectedJourneys); assert.equal(report.journeys.length, expectedJourneys); - assert.equal(report.screens.length, expectedJourneys * 2); + assert.equal(report.screens.length, expectedJourneys * (markerOnly ? 1 : 2)); console.log(`PASS:${report.journeys.length} dashboard journeys,${report.screens.length} scoped axe sweeps/captures; controlled API persistence only.`); } finally { fs.writeFileSync(path.join(out, process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['synthetic API fixtures, not real backend end-to-end','no native device/screen reader/model/provider/security/release qualification'] }, null, 2)); diff --git a/job-tracker-ui/scripts/verify-a3-documents-inbox.mjs b/job-tracker-ui/scripts/verify-a3-documents-inbox.mjs index cb24410a..cbfe4bd7 100644 --- a/job-tracker-ui/scripts/verify-a3-documents-inbox.mjs +++ b/job-tracker-ui/scripts/verify-a3-documents-inbox.mjs @@ -7,11 +7,14 @@ import path from 'node:path'; import assert from 'node:assert/strict'; const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText; const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`); -const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/documents-inbox/browser'); fs.mkdirSync(out, { recursive: true }); +const visualMatrix = process.env.A3_VISUAL_MATRIX === '1'; +const widths = process.env.A3_PILOT ? [320] : visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440]; +const expectedJourneys = widths.length * (process.env.A3_PILOT ? 1 : 4); +const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/documents-inbox' : '../../docs/ui-modernisation/evidence/documents-inbox/browser'); fs.mkdirSync(out, { recursive: true }); const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], unavailableReads: [], blockedExternal: [] }; const browser = await chromium.launch(); try { - for (const width of (process.env.A3_PILOT ? [320] : [320, 390, 768, 1440])) for (const language of (process.env.A3_PILOT ? ['en'] : ['en', 'nb'])) for (const mode of (process.env.A3_PILOT ? ['light'] : ['light', 'dark'])) { + for (const width of widths) for (const language of (process.env.A3_PILOT ? ['en'] : ['en', 'nb'])) for (const mode of (process.env.A3_PILOT ? ['light'] : ['light', 'dark'])) { console.log(`Checking ${width}/${language}/${mode}`); const t = key => { assert.ok(translations[language][key], key); return translations[language][key]; }; const context = await browser.newContext({ viewport: { width, height: 1000 }, reducedMotion: 'reduce', acceptDownloads: true }); const page = await context.newPage(); @@ -46,10 +49,12 @@ try { }; if (Object.hasOwn(fixtures, p)) return answer(fixtures[p]); report.unavailableReads.push(p); return route.fulfill({ status: 404, contentType: 'application/json', body: '{}' }); }); - const scan = async state => { + const scan = async (state, target) => { + if (target) await target.evaluate(node => node.scrollIntoView({ block: 'center', behavior: 'instant' })); + else await page.evaluate(() => window.scrollTo(0, 0)); assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1), false, state); - const axe = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); report.screens.push({ width, language, mode, state, violations: axe.violations }); assert.deepEqual(axe.violations, []); - await page.evaluate(() => window.scrollTo(0, 0)); await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: true }); + const axe = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']).analyze(); report.screens.push({ width, language, mode, state, capture: target ? 'targeted viewport' : width >= 768 ? 'full page at top' : 'viewport at top', violations: axe.violations }); assert.deepEqual(axe.violations, []); + await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: width >= 768 && !target }); }; await page.goto('http://localhost:4338/documents'); const docs = page.getByRole('region', { name: t('documentsYourDocuments'), exact: true }); await docs.getByText(document.name, { exact: true }).waitFor(); await scan('documents'); @@ -57,18 +62,18 @@ try { await docs.getByRole('button', { name: t('documentsReturnFilters'), exact: true }).click(); const search = page.getByRole('textbox', { name: t('documentsSearch'), exact: true }); await expect(search).toBeFocused(); assert.equal(documentReads, reads); await search.fill('certificate'); await expect(docs.getByText(cv.name, { exact: true })).toHaveCount(0); await docs.getByText(document.name, { exact: true }).waitFor(); const download = page.waitForEvent('download'); await docs.getByRole('button', { name: t('documentsDownloadAttachment'), exact: true }).click(); const file = await download; assert.equal(file.suggestedFilename(), document.name); assert.equal(fs.readFileSync(await file.path(), 'utf8'), 'Controlled certificate fixture'); - assert.equal(await docs.getByRole('link', { name: t('documentsOpenSource'), exact: true }).getAttribute('href'), document.sourceHref); assert.equal(await docs.locator('time').getAttribute('datetime'), document.recordedAt); await scan('document-filtered'); + assert.equal(await docs.getByRole('link', { name: t('documentsOpenSource'), exact: true }).getAttribute('href'), document.sourceHref); assert.equal(await docs.locator('time').getAttribute('datetime'), document.recordedAt); await scan('document-filtered', docs.getByText(document.name, { exact: true })); await page.goto('http://localhost:4338/correspondence'); const messages = page.getByRole('region', { name: t('emailInboxLinkedMessages'), exact: true }); await messages.getByText(mail.subject, { exact: true }).waitFor(); await scan('inbox'); const initialMailReads = mailReads; await page.getByRole('button', { name: t('emailInboxJumpMessages'), exact: true }).press('Enter'); await expect(messages).toBeFocused(); await messages.getByRole('button', { name: t('emailInboxReturnFilters'), exact: true }).click(); await expect(page.getByRole('textbox', { name: t('correspondenceSearch'), exact: true })).toBeFocused(); assert.equal(mailReads, initialMailReads); - await messages.getByRole('button', { name: t('emailInboxViewMessage'), exact: true }).click(); await messages.getByText('Please choose an interview time. ', { exact: true }).waitFor(); await scan('message'); + await messages.getByRole('button', { name: t('emailInboxViewMessage'), exact: true }).click(); const messageBody = messages.getByText('Please choose an interview time. ', { exact: true }); await messageBody.waitFor(); await scan('message', messageBody); await messages.getByRole('button', { name: t('emailInboxReply'), exact: true }).click(); const compose = page.getByRole('region', { name: t('emailInboxDraftTitle'), exact: true }); await compose.getByRole('textbox', { name: t('emailInboxRecipient'), exact: true }).fill('recruiter@example.test'); await compose.getByRole('textbox', { name: t('emailInboxMessage'), exact: true }).fill('Thank you. Please confirm the available interview times.'); await compose.getByRole('button', { name: t('emailInboxSaveDraft'), exact: true }).click(); await expect.poll(() => draft?.revision).toBe(4); - await compose.getByRole('button', { name: t('emailInboxReviewSend'), exact: true }).click(); const dialog = page.getByRole('dialog', { name: t('emailInboxConfirmSendTitle'), exact: true }); await dialog.waitFor(); assert.equal(sends, 0); await scan('send-confirmation'); + await compose.getByRole('button', { name: t('emailInboxReviewSend'), exact: true }).click(); const dialog = page.getByRole('dialog', { name: t('emailInboxConfirmSendTitle'), exact: true }); await dialog.waitFor(); assert.equal(sends, 0); await scan('send-confirmation', dialog); await dialog.getByRole('button', { name: t('emailInboxSendEmail'), exact: true }).click(); await compose.getByText(t('emailInboxDeliveryUncertain'), { exact: true }).waitFor(); await expect(compose.getByRole('button', { name: t('emailInboxReviewSend'), exact: true })).toBeDisabled(); - assert.equal(sends, 1); await expect(compose.getByRole('textbox', { name: t('emailInboxMessage'), exact: true })).toHaveValue(draft.bodyText); await scan('uncertain-send'); + assert.equal(sends, 1); await expect(compose.getByRole('textbox', { name: t('emailInboxMessage'), exact: true })).toHaveValue(draft.bodyText); await scan('uncertain-send', compose.getByRole('textbox', { name: t('emailInboxMessage'), exact: true })); report.journeys.push({ width, language, mode, documentFilterFocusDownloadSource: 'PASS', inboxFocusPlainTextReplySaveExplicitConfirmUncertainNoRetry: 'PASS' }); await context.close(); } - assert.equal(report.screens.length, report.journeys.length * 6); assert.equal(report.mockedWrites.length, report.journeys.length * 2); assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []); assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com')); + assert.equal(report.journeys.length, expectedJourneys); assert.equal(report.screens.length, report.journeys.length * 6); assert.equal(report.mockedWrites.length, report.journeys.length * 2); assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []); assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com')); console.log(`PASS:${report.journeys.length} document/mail journeys,${report.screens.length} axe scans/captures,${report.mockedWrites.length} fixture-only save/send requests.`); } finally { fs.writeFileSync(path.join(out, process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['Synthetic metadata/binary/mail/provider fixtures; not live storage, mailbox, OAuth or delivery.', 'Browser covers draft save, explicit confirmation and controlled uncertain outcome only; other recovery/conflict paths remain targeted tests.', 'No real sending, native device, final integrated security or release acceptance.'] }, null, 2)); await browser.close(); } diff --git a/job-tracker-ui/src/components/DashboardView.tsx b/job-tracker-ui/src/components/DashboardView.tsx index 68c6e814..1a541163 100644 --- a/job-tracker-ui/src/components/DashboardView.tsx +++ b/job-tracker-ui/src/components/DashboardView.tsx @@ -248,6 +248,10 @@ export default function DashboardView({ mode = "dashboard" }: { mode?: "dashboar {[0, 0.5, 1].map(fraction => )} + {analytics.length === 1 ? <> + + + : null} {monthLabel(analytics[0].month)} diff --git a/job-tracker-ui/src/dashboard-presentation.test.tsx b/job-tracker-ui/src/dashboard-presentation.test.tsx index 0a854d54..86dda7f9 100644 --- a/job-tracker-ui/src/dashboard-presentation.test.tsx +++ b/job-tracker-ui/src/dashboard-presentation.test.tsx @@ -125,6 +125,23 @@ test('[PRES-S01] activity chart uses one shared zero-based scale and a readable expect(firstY(paths[0])).toBeLessThan(firstY(paths[1])); }); +test('[PRES-S01] a single recorded month has visible markers on the shared scale and retains exact values', async () => { + const writesBefore = mockedApi.put.mock.calls.length; + mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: url.endsWith('/analytics') ? [monthly[0]] : data(url) }) as any); + open(); + const activity = await screen.findByRole('region', { name: 'Application activity' }); + const markers = activity.querySelectorAll('svg circle'); + expect(markers).toHaveLength(2); + expect(markers[0]).toHaveAttribute('cy', '20'); + expect(markers[1]).toHaveAttribute('cy', '140'); + for (const marker of markers) expect(Number(marker.getAttribute('r'))).toBeGreaterThan(0); + fireEvent.click(within(activity).getByText('View monthly values')); + const table = within(activity).getByRole('table'); + expect(within(table).getByRole('cell', { name: '4' })).toBeVisible(); + expect(within(table).getByRole('cell', { name: '1' })).toBeVisible(); + expect(mockedApi.put).toHaveBeenCalledTimes(writesBefore); +}); + test('[P7-02 PRES-S01 S02] setup guidance treats pending and failed summaries as unknown until their owner retries', async () => { let resolveStats!: (response: { data: typeof stats }) => void; let fail = false; diff --git a/job-tracker-ui/src/documents-page.test.tsx b/job-tracker-ui/src/documents-page.test.tsx index 9e322e26..66accf22 100644 --- a/job-tracker-ui/src/documents-page.test.tsx +++ b/job-tracker-ui/src/documents-page.test.tsx @@ -100,7 +100,10 @@ test.each(['en', 'nb'] as const)('%s document filters and results support reques localStorage.setItem('uiLanguage', language); renderPage(); await screen.findByText('Metadata CV ÆØÅ', { exact: true }); const count = mockedApi.get.mock.calls.length; - fireEvent.click(screen.getByRole('button', { name: copy('documentsJumpResults', {}, language) })); + const jump = screen.getByRole('button', { name: copy('documentsJumpResults', {}, language) }); + const search = screen.getByRole('textbox', { name: copy('documentsSearch', {}, language) }); + expect(jump.compareDocumentPosition(search) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + fireEvent.click(jump); expect(screen.getByRole('region', { name: copy('documentsYourDocuments', {}, language) })).toHaveFocus(); fireEvent.click(screen.getByRole('button', { name: copy('documentsReturnFilters', {}, language) })); expect(screen.getByRole('textbox', { name: copy('documentsSearch', {}, language) })).toHaveFocus(); diff --git a/job-tracker-ui/src/views/CareerProfilePage.tsx b/job-tracker-ui/src/views/CareerProfilePage.tsx index fd1b9d64..bf879f42 100644 --- a/job-tracker-ui/src/views/CareerProfilePage.tsx +++ b/job-tracker-ui/src/views/CareerProfilePage.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Accordion, AccordionDetails, AccordionSummary, Alert, Avatar, Box, Button, Checkbox, Chip, FormControl, FormControlLabel, InputLabel, LinearProgress, MenuItem, Paper, Select, Tab, Tabs, TextField, Typography } from "@mui/material"; +import { Accordion, AccordionDetails, AccordionSummary, Alert, Avatar, Box, Button, Checkbox, Chip, FormControl, FormControlLabel, InputLabel, LinearProgress, MenuItem, Select, Tab, Tabs, TextField, Typography } from "@mui/material"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; @@ -522,8 +522,8 @@ export default function CareerProfilePage() { : change.id === "Contact" && change.confidence === "Low" && change.label === "Contact details" ? t("careerImportContactDetails") : change.label; return ( - - + + {t("careerWorkspaceNavigation")} ) : null} - {workspaceSection === "profile" ? + {workspaceSection === "profile" ? {t("careerProfileImportTitle")} @@ -659,7 +659,7 @@ export default function CareerProfilePage() { {t("careerProfileImportReady")} : null} {!canUseAi ? {t("profileProImportBody")} : null} - : null} + : null} {workspaceSection === "profile" ? @@ -843,7 +843,7 @@ export default function CareerProfilePage() { - + {runsLoading ? : null} {runsLoadError !== null ? void loadExtractionRuns()}>{t("retry")}}>{t("careerOverviewImportUnavailable")} : null} {latestRun ? ( @@ -1011,7 +1011,7 @@ export default function CareerProfilePage() { - + - : null} + : null} ); } diff --git a/job-tracker-ui/src/views/DocumentsPage.tsx b/job-tracker-ui/src/views/DocumentsPage.tsx index caf8bc9c..7c9a43f3 100644 --- a/job-tracker-ui/src/views/DocumentsPage.tsx +++ b/job-tracker-ui/src/views/DocumentsPage.tsx @@ -274,7 +274,10 @@ export default function DocumentsPage() { ; return - } onClick={refresh} disabled={loading} sx={buttonSx}>{t("documentsRefresh")}} /> + + + + } /> updateQuery({ q: event.target.value, page: "1" }, true)} slotProps={{ htmlInput: { maxLength: 200 } }} /> @@ -286,7 +289,6 @@ export default function DocumentsPage() { {jobsError ? { resultsRef.current?.focus(); return loadJobs(); }} /> : null} {t("documentsDateOrderHelp")} - @@ -301,7 +303,7 @@ export default function DocumentsPage() { {t("documentsName")}{t("documentsJobFilter")}{t("documentsState")}{t("documentsRecordedDate")}{t("documentsActions")}{items.map((item) => {details(item)}{job(item)}{badges(item)}{date(item)}{actions(item)})} -
: {items.map((item) => {details(item)}{job(item)}{badges(item)}{date(item)}{actions(item)})}} + : {items.map((item) => {details(item)}{job(item)}{badges(item)}{date(item)}{actions(item)})}} {result.totalPages > 1 ? { resultsRef.current?.focus(); updateQuery({ page: String(next) }); }} aria-label={t("documentsPages")} getItemAriaLabel={(kind, target) => t(kind === "page" ? "documentsGoToPage" : kind === "previous" ? "documentsPreviousPage" : kind === "next" ? "documentsNextPage" : kind === "first" ? "documentsFirstPage" : "documentsLastPage", { page: target ?? 1 })} sx={{ mt: 2, "& .MuiPagination-ul": { flexWrap: "wrap", gap: 0.5 } }} /> : null} : null}
diff --git a/job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx b/job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx index 83b7fbbc..56cb59c1 100644 --- a/job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx +++ b/job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; -import { Alert, Box, Button, Chip, LinearProgress, Paper, Skeleton, Stack, Typography } from "@mui/material"; +import { Alert, Box, Button, Chip, LinearProgress, Skeleton, Stack, Typography } from "@mui/material"; import DescriptionOutlinedIcon from "@mui/icons-material/DescriptionOutlined"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import UploadFileOutlinedIcon from "@mui/icons-material/UploadFileOutlined"; @@ -43,7 +43,7 @@ function ActionCard({ title, body, href, label, icon, onNavigate }: { onNavigate?: () => void; }) { return ( - + {icon} {title} @@ -57,7 +57,7 @@ function ActionCard({ title, body, href, label, icon, onNavigate }: { > {label} - +
); } @@ -142,7 +142,7 @@ export default function CareerWorkspaceOverview({ completeness, runs, loading, l {isFirstRun ? {t("careerOverviewFirstRun")} : null} - + {t("careerOverviewCompleteness")} {loading ? : profileKnown ? {completeness.percent}% : null} @@ -157,7 +157,7 @@ export default function CareerWorkspaceOverview({ completeness, runs, loading, l ) : null} - + {importUnknown ? {t("loading")} : runsLoadError ? { importLinkRef.current?.focus(); onRetryRuns(); }}>{t("retry")} : undefined}>{t("careerOverviewImportUnavailable")} : {importState.message}} @@ -186,7 +186,7 @@ export default function CareerWorkspaceOverview({ completeness, runs, loading, l } /> - {t("careerOverviewViewAll")}}> + {t("careerOverviewViewAll")}}> {recentLoading ? ( {t("loading")} ) : recentError ? (