test(admin): qualify read-only account status

This commit is contained in:
cesnimda
2026-11-10 12:46:45 +01:00
parent e22f2d6450
commit 2717c6afaf
54 changed files with 2820 additions and 11 deletions
@@ -7,7 +7,7 @@ Owner addendum received 9 November 2026. Source `E:/Storage/Projects/Active/temp
| Admin information isolation | First slice published on `codex/security-offline-preflight` at `ecadc29b`. Reproduced and removed imported mail subjects from the Admin Audit projection. 62 selected offline tests passed, including owner-bound notices and operations. | Integrated security, remaining delivery paths, broader minimisation and deployment checks remain separate. | | Admin information isolation | First slice published on `codex/security-offline-preflight` at `ecadc29b`. Reproduced and removed imported mail subjects from the Admin Audit projection. 62 selected offline tests passed, including owner-bound notices and operations. | Integrated security, remaining delivery paths, broader minimisation and deployment checks remain separate. |
| Discover Jobs | [Search-first layout](visual-correction/DISCOVERY_SEARCH_FIRST.md) moves the query above filters and qualifies the existing NAV request, keyboard submit and source disclosure. | Expose new filters, map and multilingual suggestions only with qualified data and the existing GJ architecture. Mobile filter drawer and focused detail remain open. | | Discover Jobs | [Search-first layout](visual-correction/DISCOVERY_SEARCH_FIRST.md) moves the query above filters and qualifies the existing NAV request, keyboard submit and source disclosure. | Expose new filters, map and multilingual suggestions only with qualified data and the existing GJ architecture. Mobile filter drawer and focused detail remain open. |
| Applications | [Title-first rows and explicit bulk mode](visual-correction/APPLICATION_SELECTION_REVIEW.md) qualified at 36 configurations. | Secondary action consolidation and final native/visual review remain open. | | Applications | [Title-first rows and explicit bulk mode](visual-correction/APPLICATION_SELECTION_REVIEW.md) qualified at 36 configurations. | Secondary action consolidation and final native/visual review remain open. |
| Admin Users and Audit | [User row alignment](visual-correction/ADMIN_USER_ALIGNMENT_REVIEW.md) passed 36 configurations. Review existing read metadata, available filters and expandable records next. The MFA proposal is in `docs/security/MFA_RECOVERY_PROPOSAL.md` on the security branch. | 2FA recovery and private support access require explicit owner approval of their technical and privacy designs. | | Admin Users and Audit | [Aligned rows](visual-correction/ADMIN_USER_ALIGNMENT_REVIEW.md) and [read-only 2FA status](visual-correction/ADMIN_USER_2FA_REVIEW.md) each passed their own 36-configuration checks. Compact user actions and audit filter/record presentation remain open. | 2FA recovery and private support access require explicit owner approval of their technical and privacy designs. |
| Job Correspondence | Continue inbox layout, mailbox-status presentation and existing explicit consent flow. | Provider verification, restricted scopes, multiple-account persistence and retention decisions retain their gates. Signing in grants no mailbox access. | | Job Correspondence | Continue inbox layout, mailbox-status presentation and existing explicit consent flow. | Provider verification, restricted scopes, multiple-account persistence and retention decisions retain their gates. Signing in grants no mailbox access. |
| System Status | [Status refresh evidence](visual-correction/SYSTEM_STATUS_REFRESH_REVIEW.md) and [diagnostics design](SYSTEM_DIAGNOSTICS_DESIGN.md) distinguish current, stale and unavailable readings. CV Benchmark Review is retained. | Predefined diagnostics require approved suites, isolated execution, role checks and audit. Live execution and per-signal telemetry remain open. | | System Status | [Status refresh evidence](visual-correction/SYSTEM_STATUS_REFRESH_REVIEW.md) and [diagnostics design](SYSTEM_DIAGNOSTICS_DESIGN.md) distinguish current, stale and unavailable readings. CV Benchmark Review is retained. | Predefined diagnostics require approved suites, isolated execution, role checks and audit. Live execution and per-signal telemetry remain open. |
| Terminology | “Prepare application” / “Klargjør søknaden” is visible in the scoped EN/NB application checks. | Continue broader terminology and European catalogue review. | | Terminology | “Prepare application” / “Klargjør søknaden” is visible in the scoped EN/NB application checks. | Continue broader terminology and European catalogue review. |
+2
View File
@@ -1,5 +1,7 @@
# A3-IM10 Cross-application qualification # A3-IM10 Cross-application qualification
Admin Users runtime `e22f2d64` presents read-only 2FA and confirmation status. Seven backend tests, 14 frontend tests, lint, export and 36 scoped browser scans passed. See [account status evidence](visual-correction/ADMIN_USER_2FA_REVIEW.md).
System Status runtime `4981a242` displays the last successful browser read and preserves its timestamp when a refresh fails. See [refresh evidence](visual-correction/SYSTEM_STATUS_REFRESH_REVIEW.md) and the [separate diagnostics design](SYSTEM_DIAGNOSTICS_DESIGN.md). System Status runtime `4981a242` displays the last successful browser read and preserves its timestamp when a refresh fails. See [refresh evidence](visual-correction/SYSTEM_STATUS_REFRESH_REVIEW.md) and the [separate diagnostics design](SYSTEM_DIAGNOSTICS_DESIGN.md).
Application list successor `1246994c` moves role ahead of employer and shows bulk controls in explicit selection mode. Passed 57 native tests, types, lint, build and 72 rendered states. [Application selection evidence](visual-correction/APPLICATION_SELECTION_REVIEW.md) Application list successor `1246994c` moves role ahead of employer and shows bulk controls in explicit selection mode. Passed 57 native tests, types, lint, build and 72 rendered states. [Application selection evidence](visual-correction/APPLICATION_SELECTION_REVIEW.md)
@@ -0,0 +1,11 @@
# Admin Users account status
Runtime `e22f2d6450e6d1d52c3b9e5356e1fdb90422a790`. The existing admin-only Users DTO adds the Identity `TwoFactorEnabled` boolean. The same projection is used by the existing list and create responses. The change requires no new database read or endpoint. The read model adds no authenticator details, recovery codes or credentials. There is no new recovery or factor-change action.
The desktop account-status column and phone card show explicit confirmation and 2FA labels. An older or malformed response without a recognised boolean shows “2FA status unavailable”. The final-administrator controls, action handlers and existing server authorisation are unchanged.
- **Backend PASSED** seven selected UsersController cases using the pinned local SDK. The relational case verifies enabled and disabled values and retains its fixed two-query assertion.
- **Frontend PASSED** 14 selected tests, including desktop and phone checks for enabled, disabled and missing values. The retained red run failed on the missing status in those two new cases. The successful run emitted an existing asynchronous test warning. Full lint and the export build, including its TypeScript check, passed.
- **Browser PASSED** 36 configurations across nine widths, EN/NB and both themes, with 36 scoped axe scans and captures. The enabled status and confirmation label rendered in all cases. Desktop alignment stayed within 0.5 px. Phone cards and disabled final-admin remove/delete controls remained available. Zero writes and page errors were recorded. The harness recorded 36 unavailable company reads outside the Users route's data.
Reviewed the 1440 px English light and 320 px Bokmål dark captures. The mobile action stack still takes vertical space. Compact action menus, device and native-language review, integrated HTTP security qualification and protected recovery are open.
File diff suppressed because it is too large Load Diff
@@ -38,6 +38,7 @@ Owner correction: 3 November 2026. This register extends IM10 and the original 3
| V28 | Application list | Ordinary rows show permanent selection controls and company before the role | RESOLVED in `1246994c` | [Selection review](APPLICATION_SELECTION_REVIEW.md) covers title-first hierarchy and the explicit mode. Secondary action consolidation remains open. | | V28 | Application list | Ordinary rows show permanent selection controls and company before the role | RESOLVED in `1246994c` | [Selection review](APPLICATION_SELECTION_REVIEW.md) covers title-first hierarchy and the explicit mode. Secondary action consolidation remains open. |
| V29 | Admin Users | Row details sit above the centre of the action controls | RESOLVED in `dddec043` | [Alignment review](ADMIN_USER_ALIGNMENT_REVIEW.md) records the before and after geometry and phone layout. | | V29 | Admin Users | Row details sit above the centre of the action controls | RESOLVED in `dddec043` | [Alignment review](ADMIN_USER_ALIGNMENT_REVIEW.md) records the before and after geometry and phone layout. |
| V30 | Admin System | Operators cannot tell when the displayed status response last succeeded | RESOLVED in `4981a242` | [Status refresh review](SYSTEM_STATUS_REFRESH_REVIEW.md) qualifies timestamp, stale warning and keyboard retry. Per-signal telemetry remains open. | | V30 | Admin System | Operators cannot tell when the displayed status response last succeeded | RESOLVED in `4981a242` | [Status refresh review](SYSTEM_STATUS_REFRESH_REVIEW.md) qualifies timestamp, stale warning and keyboard retry. Per-signal telemetry remains open. |
| V31 | Admin Users | Confirmation lacks an explicit phone label and 2FA status is absent | RESOLVED in `e22f2d64` | [Account status review](ADMIN_USER_2FA_REVIEW.md) covers the read-only flag and aligned presentation. Compact menus and protected recovery remain open. |
The owner-approved design foundation remains original A1 dashboard, D1 visual system, B1 tracking, editor left / preview right, application context right, desktop sidebar and hybrid mobile navigation. No controls, imports, revisions, permissions or provider rights may be removed to obtain visual simplicity. The owner-approved design foundation remains original A1 dashboard, D1 visual system, B1 tracking, editor left / preview right, application context right, desktop sidebar and hybrid mobile navigation. No controls, imports, revisions, permissions or provider rights may be removed to obtain visual simplicity.
@@ -229,9 +229,9 @@
"AdminUsersPage" "AdminUsersPage"
], ],
"visualStatus": "MEANINGFUL_USER_FORM_TABLE_BOUNDARIES_RETAINED", "visualStatus": "MEANINGFUL_USER_FORM_TABLE_BOUNDARIES_RETAINED",
"afterStatus": "PASSED:36 configurations for aligned grid rows or phone cards, with final-administrator protections; creation/dialog/error states remain open", "afterStatus": "PASSED:36 configurations for read-only 2FA, confirmation and aligned rows or phone cards; creation/dialog/error states remain open",
"functionalCriteria": "Original preservation register retained; no capability acceptance promotion", "functionalCriteria": "Original preservation register retained; no capability acceptance promotion",
"evidence": "ADMIN_USER_ALIGNMENT_REVIEW.md; evidence/admin-user-alignment/BROWSER.json; earlier ADMIN_HEADINGS_REVIEW.md and ADMIN_REVIEW.md" "evidence": "ADMIN_USER_2FA_REVIEW.md; evidence/admin-user-2fa/BROWSER.json; earlier ADMIN_USER_ALIGNMENT_REVIEW.md and ADMIN_HEADINGS_REVIEW.md"
}, },
{ {
"route": "/admin/system", "route": "/admin/system",
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
{
"screens": [
{
"width": 320,
"language": "en",
"mode": "light",
"state": "last-admin",
"capture": "viewport at top",
"violations": []
},
{
"width": 1440,
"language": "en",
"mode": "light",
"state": "last-admin",
"capture": "full page at top",
"violations": []
}
],
"journeys": [
{
"width": 320,
"language": "en",
"mode": "light",
"cellOffsets": [],
"lastAdminActionsDisabled": "PASS"
},
{
"width": 1440,
"language": "en",
"mode": "light",
"cellOffsets": [
{
"field": "email",
"offset": 0.5
},
{
"field": "userName",
"offset": 0.375
},
{
"field": "roles",
"offset": 0.5
},
{
"field": "emailConfirmed",
"offset": 0.5
},
{
"field": "actions",
"offset": 0.5
}
],
"lastAdminActionsDisabled": "PASS"
}
],
"errors": [],
"unexpectedWrites": [],
"exportDenials": [],
"unavailableReads": [
"/api/companies",
"/api/companies"
],
"blockedExternal": [
"https://fonts.googleapis.com",
"https://fonts.googleapis.com"
],
"limits": [
"Every API response is synthetic. In admin-only mode all mutations are blocked and account flows are NOT RUN; otherwise account export is a controlled 403 and confirmations are cancelled.",
"Read-only 2FA and confirmation statuses are displayed alongside the existing last-administrator safeguards.",
"No real credentials, provider activation, model, native device, integrated security, production or release acceptance."
]
}
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<TestRun id="ab5e8888-b32c-4cd8-aca1-59eb7703896f" name="Connor@CESNIMDA 2026-11-10 12:41:47" runUser="Cesnimda\Connor" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Times creation="2026-11-10T12:41:47.5942225+01:00" queuing="2026-11-10T12:41:47.5942229+01:00" start="2026-11-10T12:41:45.3324215+01:00" finish="2026-11-10T12:41:48.9637621+01:00" />
<TestSettings name="default" id="2420ed74-a43e-4f05-a0cf-2bf29c36f2ff">
<Deployment runDeploymentRoot="Connor_CESNIMDA_2026-11-10_12_41_47" />
</TestSettings>
<Results>
<UnitTestResult executionId="4385a0c1-702a-455c-97a7-382b2c525519" testId="c306a523-edb1-3483-b9d9-2894e7823843" testName="JobTrackerApi.Tests.UsersControllerTests.Delete_never_falls_back_to_identity_only_removal" computerName="CESNIMDA" duration="00:00:00.0090178" startTime="2026-11-10T12:41:47.2064354+01:00" endTime="2026-11-10T12:41:47.2064356+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="4385a0c1-702a-455c-97a7-382b2c525519" />
<UnitTestResult executionId="d6aa739f-14b2-4024-97c6-34f4c65520aa" testId="1d05c8f0-b4f5-5979-c2ee-b89fb865b0c0" testName="JobTrackerApi.Tests.UsersControllerTests.Delete_rejects_deleting_the_final_administrator" computerName="CESNIMDA" duration="00:00:00.0020046" startTime="2026-11-10T12:41:48.8915576+01:00" endTime="2026-11-10T12:41:48.8915580+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="d6aa739f-14b2-4024-97c6-34f4c65520aa" />
<UnitTestResult executionId="f08e2ed3-5c72-4ddb-bcd9-c5405a8ec273" testId="49384e74-fc74-cc87-a819-9b1ba4a72e05" testName="JobTrackerApi.Tests.UsersControllerTests.SetRoles_allows_a_confirmed_self_demotion_when_another_admin_exists" computerName="CESNIMDA" duration="00:00:00.0059303" startTime="2026-11-10T12:41:47.2123906+01:00" endTime="2026-11-10T12:41:47.2123909+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="f08e2ed3-5c72-4ddb-bcd9-c5405a8ec273" />
<UnitTestResult executionId="80439873-f110-4688-a9c7-a3ba3f3ffe3a" testId="c290d6c2-2e12-8508-613f-1faef3e1e045" testName="JobTrackerApi.Tests.UsersControllerTests.List_uses_a_fixed_number_of_role_queries_for_many_users" computerName="CESNIMDA" duration="00:00:01.4775228" startTime="2026-11-10T12:41:48.6900837+01:00" endTime="2026-11-10T12:41:48.6900840+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="80439873-f110-4688-a9c7-a3ba3f3ffe3a" />
<UnitTestResult executionId="891e3bee-c98e-4b7c-9f4e-da26e582d3b4" testId="a55b51f5-22e2-c043-4279-8e10e60d7349" testName="JobTrackerApi.Tests.UsersControllerTests.SetRoles_revokes_existing_sessions_and_trusted_devices_when_roles_change" computerName="CESNIMDA" duration="00:00:00.1964371" startTime="2026-11-10T12:41:48.8895062+01:00" endTime="2026-11-10T12:41:48.8895064+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="891e3bee-c98e-4b7c-9f4e-da26e582d3b4" />
<UnitTestResult executionId="0c1ee557-fc22-4270-ae43-bd6d371f3fbf" testId="60b6bfd7-91ef-c23f-ffe4-217e3ea1388b" testName="JobTrackerApi.Tests.UsersControllerTests.List_marks_the_current_user_and_last_admin_safety_state" computerName="CESNIMDA" duration="00:00:00.3294224" startTime="2026-11-10T12:41:47.1978544+01:00" endTime="2026-11-10T12:41:47.1978680+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="0c1ee557-fc22-4270-ae43-bd6d371f3fbf" />
<UnitTestResult executionId="8a70a6f2-954d-4275-bccd-48ee3ea9f411" testId="aa87bb5b-2b89-868b-47a2-39dc87ec48cf" testName="JobTrackerApi.Tests.UsersControllerTests.SetRoles_rejects_removing_the_final_administrator" computerName="CESNIMDA" duration="00:00:00.0028368" startTime="2026-11-10T12:41:48.6929631+01:00" endTime="2026-11-10T12:41:48.6929633+01:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="8a70a6f2-954d-4275-bccd-48ee3ea9f411" />
</Results>
<TestDefinitions>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.SetRoles_revokes_existing_sessions_and_trusted_devices_when_roles_change" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="a55b51f5-22e2-c043-4279-8e10e60d7349">
<Execution id="891e3bee-c98e-4b7c-9f4e-da26e582d3b4" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="SetRoles_revokes_existing_sessions_and_trusted_devices_when_roles_change" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.List_uses_a_fixed_number_of_role_queries_for_many_users" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="c290d6c2-2e12-8508-613f-1faef3e1e045">
<Execution id="80439873-f110-4688-a9c7-a3ba3f3ffe3a" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="List_uses_a_fixed_number_of_role_queries_for_many_users" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.List_marks_the_current_user_and_last_admin_safety_state" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="60b6bfd7-91ef-c23f-ffe4-217e3ea1388b">
<Execution id="0c1ee557-fc22-4270-ae43-bd6d371f3fbf" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="List_marks_the_current_user_and_last_admin_safety_state" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.SetRoles_allows_a_confirmed_self_demotion_when_another_admin_exists" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="49384e74-fc74-cc87-a819-9b1ba4a72e05">
<Execution id="f08e2ed3-5c72-4ddb-bcd9-c5405a8ec273" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="SetRoles_allows_a_confirmed_self_demotion_when_another_admin_exists" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.Delete_never_falls_back_to_identity_only_removal" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="c306a523-edb1-3483-b9d9-2894e7823843">
<Execution id="4385a0c1-702a-455c-97a7-382b2c525519" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="Delete_never_falls_back_to_identity_only_removal" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.Delete_rejects_deleting_the_final_administrator" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="1d05c8f0-b4f5-5979-c2ee-b89fb865b0c0">
<Execution id="d6aa739f-14b2-4024-97c6-34f4c65520aa" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="Delete_rejects_deleting_the_final_administrator" />
</UnitTest>
<UnitTest name="JobTrackerApi.Tests.UsersControllerTests.SetRoles_rejects_removing_the_final_administrator" storage="c:\users\connor\.codex\worktrees\direct-roadmap-integration\job tracker\jobtrackerapi.tests\bin\release\net9.0\jobtrackerapi.tests.dll" id="aa87bb5b-2b89-868b-47a2-39dc87ec48cf">
<Execution id="8a70a6f2-954d-4275-bccd-48ee3ea9f411" />
<TestMethod codeBase="C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\JobTrackerApi.Tests\bin\Release\net9.0\JobTrackerApi.Tests.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="JobTrackerApi.Tests.UsersControllerTests" name="SetRoles_rejects_removing_the_final_administrator" />
</UnitTest>
</TestDefinitions>
<TestEntries>
<TestEntry testId="c306a523-edb1-3483-b9d9-2894e7823843" executionId="4385a0c1-702a-455c-97a7-382b2c525519" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="1d05c8f0-b4f5-5979-c2ee-b89fb865b0c0" executionId="d6aa739f-14b2-4024-97c6-34f4c65520aa" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="49384e74-fc74-cc87-a819-9b1ba4a72e05" executionId="f08e2ed3-5c72-4ddb-bcd9-c5405a8ec273" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="c290d6c2-2e12-8508-613f-1faef3e1e045" executionId="80439873-f110-4688-a9c7-a3ba3f3ffe3a" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="a55b51f5-22e2-c043-4279-8e10e60d7349" executionId="891e3bee-c98e-4b7c-9f4e-da26e582d3b4" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="60b6bfd7-91ef-c23f-ffe4-217e3ea1388b" executionId="0c1ee557-fc22-4270-ae43-bd6d371f3fbf" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="aa87bb5b-2b89-868b-47a2-39dc87ec48cf" executionId="8a70a6f2-954d-4275-bccd-48ee3ea9f411" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
</TestEntries>
<TestLists>
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" />
</TestLists>
<ResultSummary outcome="Completed">
<Counters total="7" executed="7" passed="7" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
<Output>
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 9.0.19)&#xD;
[xUnit.net 00:00:00.56] Discovering: JobTrackerApi.Tests&#xD;
[xUnit.net 00:00:00.73] Discovered: JobTrackerApi.Tests&#xD;
[xUnit.net 00:00:00.73] Starting: JobTrackerApi.Tests&#xD;
[xUnit.net 00:00:02.79] Finished: JobTrackerApi.Tests&#xD;
</StdOut>
</Output>
</ResultSummary>
</TestRun>
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@@ -43,7 +43,7 @@ const screens = [
{name:'Mail label counts',reference:'correspondence',folder:'mail-labels',state:'label-count',source:'cd116ab8',scope:'Singular text at all nine widths; native singular/plural/live-locale regression passed. No writes or repeated send workflow.'}, {name:'Mail label counts',reference:'correspondence',folder:'mail-labels',state:'label-count',source:'cd116ab8',scope:'Singular text at all nine widths; native singular/plural/live-locale regression passed. No writes or repeated send workflow.'},
{name:'Mail review suggestion',reference:'correspondence',folder:'mail-review-counts',state:'review-counts',source:'e8e7d839',scope:'Fixture suggestion with corrected singular thread, job and message counts. Review actions were not executed.'}, {name:'Mail review suggestion',reference:'correspondence',folder:'mail-review-counts',state:'review-counts',source:'e8e7d839',scope:'Fixture suggestion with corrected singular thread, job and message counts. Review actions were not executed.'},
{name:'Mail draft conflict',reference:'correspondence',folder:'mail-recovery',state:'draft-conflict',source:'c7e8627c',scope:'A controlled 409 preserves unsaved text and shows the conflict warning. No send request.'}, {name:'Mail draft conflict',reference:'correspondence',folder:'mail-recovery',state:'draft-conflict',source:'c7e8627c',scope:'A controlled 409 preserves unsaved text and shows the conflict warning. No send request.'},
{name:'Administration users',reference:'admin',folder:'admin-user-alignment',state:'last-admin',source:'dddec043',scope:'User fields and actions align on desktop. Phone cards retain their stacked layout and final-administrator protections.'}, {name:'Administration users',reference:'admin',folder:'admin-user-2fa',state:'last-admin',source:'e22f2d64',scope:'Confirmation and read-only 2FA status appear on aligned desktop rows and phone cards. Final-administrator safeguards remain visible.'},
{name:'Administration audit',reference:'admin',folder:'audit-date',state:'audit-populated',source:'2dc8fe4c',scope:'Readable timestamp and compact fixture row in the existing scrollable table. No undo or restore executed.'}, {name:'Administration audit',reference:'admin',folder:'audit-date',state:'audit-populated',source:'2dc8fe4c',scope:'Readable timestamp and compact fixture row in the existing scrollable table. No undo or restore executed.'},
{name:'Administration system',reference:'admin',folder:'system-status-refresh',state:'system-normal',source:'4981a242',scope:'A timestamp records when the browser received the successful status response. All readings are synthetic.'}, {name:'Administration system',reference:'admin',folder:'system-status-refresh',state:'system-normal',source:'4981a242',scope:'A timestamp records when the browser received the successful status response. All readings are synthetic.'},
{name:'Administration stale system status',reference:'admin',folder:'system-status-refresh',state:'system-stale',source:'4981a242',scope:'A failed refresh keeps the earlier readings and timestamp with a visible warning and retry.'}, {name:'Administration stale system status',reference:'admin',folder:'system-status-refresh',state:'system-stale',source:'4981a242',scope:'A failed refresh keeps the earlier readings and timestamp with a visible warning and retry.'},
@@ -12,12 +12,14 @@ const adminOnly = Boolean(process.env.A3_ADMIN_FLAT);
const adminHeadings = Boolean(process.env.A3_ADMIN_HEADINGS); const adminHeadings = Boolean(process.env.A3_ADMIN_HEADINGS);
const auditDateOnly = Boolean(process.env.A3_AUDIT_DATE); const auditDateOnly = Boolean(process.env.A3_AUDIT_DATE);
const userAlignmentOnly = Boolean(process.env.A3_ADMIN_USER_ALIGNMENT); const userAlignmentOnly = Boolean(process.env.A3_ADMIN_USER_ALIGNMENT);
const userTwoFactorOnly = Boolean(process.env.A3_ADMIN_USER_2FA);
const usersOnly = userAlignmentOnly || userTwoFactorOnly;
const systemRefreshOnly = Boolean(process.env.A3_SYSTEM_REFRESH); const systemRefreshOnly = Boolean(process.env.A3_SYSTEM_REFRESH);
assert.ok(!adminHeadings || adminOnly, 'Heading qualification uses the Admin fixtures.'); assert.ok(!adminHeadings || adminOnly, 'Heading qualification uses the Admin fixtures.');
assert.ok(!auditDateOnly || (adminOnly && !adminHeadings), 'Audit timestamp qualification uses its own scope.'); assert.ok(!auditDateOnly || (adminOnly && !adminHeadings), 'Audit timestamp qualification uses its own scope.');
assert.ok(!userAlignmentOnly || (adminOnly && !adminHeadings && !auditDateOnly), 'Admin Users alignment uses its own scope.'); assert.ok(!usersOnly || (adminOnly && !adminHeadings && !auditDateOnly && !(userTwoFactorOnly && userAlignmentOnly)), 'Admin Users verification uses a single scope.');
assert.ok(!systemRefreshOnly || (adminOnly && !adminHeadings && !auditDateOnly && !userAlignmentOnly), 'System refresh qualification uses its own scope.'); assert.ok(!systemRefreshOnly || (adminOnly && !adminHeadings && !auditDateOnly && !usersOnly), 'System refresh qualification uses its own scope.');
const out = path.resolve(import.meta.dirname, systemRefreshOnly ? '../../docs/ui-modernisation/visual-correction/evidence/system-status-refresh' : userAlignmentOnly ? '../../docs/ui-modernisation/visual-correction/evidence/admin-user-alignment' : auditDateOnly ? '../../docs/ui-modernisation/visual-correction/evidence/audit-date' : adminHeadings ? '../../docs/ui-modernisation/visual-correction/evidence/admin-headings' : adminOnly ? '../../docs/ui-modernisation/visual-correction/evidence/admin-flat' : accountOnly ? '../../docs/ui-modernisation/visual-correction/evidence/account-flat' : '../../docs/ui-modernisation/evidence/account-admin/browser'); const out = path.resolve(import.meta.dirname, userTwoFactorOnly ? '../../docs/ui-modernisation/visual-correction/evidence/admin-user-2fa' : systemRefreshOnly ? '../../docs/ui-modernisation/visual-correction/evidence/system-status-refresh' : userAlignmentOnly ? '../../docs/ui-modernisation/visual-correction/evidence/admin-user-alignment' : auditDateOnly ? '../../docs/ui-modernisation/visual-correction/evidence/audit-date' : adminHeadings ? '../../docs/ui-modernisation/visual-correction/evidence/admin-headings' : adminOnly ? '../../docs/ui-modernisation/visual-correction/evidence/admin-flat' : accountOnly ? '../../docs/ui-modernisation/visual-correction/evidence/account-flat' : '../../docs/ui-modernisation/evidence/account-admin/browser');
fs.mkdirSync(out, { recursive: true }); fs.mkdirSync(out, { recursive: true });
const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], exportDenials: [], unavailableReads: [], blockedExternal: [] }; const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], exportDenials: [], unavailableReads: [], blockedExternal: [] };
const browser = await chromium.launch(); const browser = await chromium.launch();
@@ -63,7 +65,7 @@ try {
'/api/jobapplications/reminders': [], '/api/notifications/unread-count': { count: 0 }, '/api/notification-settings': { emailFollowUpRemindersEnabled: true }, '/api/jobapplications/reminders': [], '/api/notifications/unread-count': { count: 0 }, '/api/notification-settings': { emailFollowUpRemindersEnabled: true },
'/api/account-lifecycle/status': { deletionEnabled: true, deletionStatus: 'active', requiredConfirmation: 'DELETE alex@example.test', request: null }, '/api/account-lifecycle/status': { deletionEnabled: true, deletionStatus: 'active', requiredConfirmation: 'DELETE alex@example.test', request: null },
'/api/gmail/status': { connected: false, configured: false }, '/api/microsoft-graph/status': { connected: false, configured: false }, '/api/imap/status': { connected: false, configured: false }, '/api/gmail/status': { connected: false, configured: false }, '/api/microsoft-graph/status': { connected: false, configured: false }, '/api/imap/status': { connected: false, configured: false },
'/api/users': [{ id: 'a3-admin-owner', email: 'alex@example.test', userName: 'alex', emailConfirmed: true, roles: ['Admin'], isCurrentUser: true, canRemoveAdmin: false }], '/api/users': [{ id: 'a3-admin-owner', email: 'alex@example.test', userName: 'alex', emailConfirmed: true, twoFactorEnabled: userTwoFactorOnly ? true : undefined, roles: ['Admin'], isCurrentUser: true, canRemoveAdmin: false }],
'/api/admin/audit': populatedAudit ? [{ id: 1, type: 'StatusChanged', oldValue: 'Applied', newValue: 'Interview', note: 'Synthetic audit event', at: '2026-11-04T10:00:00Z', jobApplicationId: 42, jobTitle: operation.job.title, companyName: operation.job.company, ownerUserId: 'a3-admin-owner', ownerEmail: 'alex@example.test', ownerUserName: 'alex' }] : [], '/api/admin/processing': listing, '/api/admin/audit': populatedAudit ? [{ id: 1, type: 'StatusChanged', oldValue: 'Applied', newValue: 'Interview', note: 'Synthetic audit event', at: '2026-11-04T10:00:00Z', jobApplicationId: 42, jobTitle: operation.job.title, companyName: operation.job.company, ownerUserId: 'a3-admin-owner', ownerEmail: 'alex@example.test', ownerUserName: 'alex' }] : [], '/api/admin/processing': listing,
[`/api/admin/processing/${operation.id}`]: operation, [`/api/admin/processing/${operation.id}`]: operation,
[`/api/admin/processing/${operation.id}/events`]: { items: [{ id: '9007199254740993', atUtc: '2026-11-03T10:00:08Z', kind: 'Failed', status: 'failed', stage: 'cv_extracting', attempt: 1, failureCode: 'parser_failed' }], hasMore: false, retentionDays: 30 }, [`/api/admin/processing/${operation.id}/events`]: { items: [{ id: '9007199254740993', atUtc: '2026-11-03T10:00:08Z', kind: 'Failed', status: 'failed', stage: 'cv_extracting', attempt: 1, failureCode: 'parser_failed' }], hasMore: false, retentionDays: 30 },
@@ -123,10 +125,14 @@ try {
await context.close(); await context.close();
continue; continue;
} }
if (userAlignmentOnly) { if (usersOnly) {
await page.goto('http://localhost:4338/admin/users'); await page.goto('http://localhost:4338/admin/users');
await expect(page.getByRole('button', { name: t('adminUsersRemoveAdmin'), exact: true })).toBeDisabled(); await expect(page.getByRole('button', { name: t('adminUsersRemoveAdmin'), exact: true })).toBeDisabled();
await expect(page.getByRole('button', { name: t('adminUsersDelete'), exact: true })).toBeDisabled(); await expect(page.getByRole('button', { name: t('adminUsersDelete'), exact: true })).toBeDisabled();
if (userTwoFactorOnly) {
await expect(page.getByText(t('adminUsers2faEnabled'), { exact: true })).toBeVisible();
await expect(page.getByText(`${t('adminUsersConfirmed')}: ${t('yes')}`, { exact: true })).toBeVisible();
}
await scan('last-admin'); await scan('last-admin');
const grid = page.getByRole('grid'); const grid = page.getByRole('grid');
const cells = width >= 768 ? await grid.getByRole('gridcell').evaluateAll(nodes => nodes.map(cell => { const cells = width >= 768 ? await grid.getByRole('gridcell').evaluateAll(nodes => nodes.map(cell => {
@@ -228,10 +234,10 @@ try {
} }
report.journeys.push({ width, language, mode, draftPreservedAcrossKeyboardSections: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', revocationAndDeletionCancelled: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', recentExportDenial: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', lastAdminActionsDisabled: processingOnly || accountOnly ? 'NOT RUN' : 'PASS', processingKeyboardAndOwnerLink: accountOnly ? 'NOT RUN' : 'PASS', processingFilterLabelsAndDurationGeometry: accountOnly ? 'NOT RUN' : 'PASS', populatedAdminReadStates: adminOnly ? 'PASS' : 'NOT RUN' }); await context.close(); report.journeys.push({ width, language, mode, draftPreservedAcrossKeyboardSections: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', revocationAndDeletionCancelled: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', recentExportDenial: processingOnly || adminOnly ? 'NOT RUN' : 'PASS', lastAdminActionsDisabled: processingOnly || accountOnly ? 'NOT RUN' : 'PASS', processingKeyboardAndOwnerLink: accountOnly ? 'NOT RUN' : 'PASS', processingFilterLabelsAndDurationGeometry: accountOnly ? 'NOT RUN' : 'PASS', populatedAdminReadStates: adminOnly ? 'PASS' : 'NOT RUN' }); await context.close();
} }
assert.equal(report.screens.length, configurations.length * (systemRefreshOnly ? 3 : userAlignmentOnly || auditDateOnly || processingOnly ? 1 : accountOnly ? 7 : adminOnly ? 9 : 8)); assert.equal(report.journeys.length, configurations.length); assert.equal(report.exportDenials.length, processingOnly || adminOnly ? 0 : configurations.length); assert.equal(report.screens.length, configurations.length * (systemRefreshOnly ? 3 : usersOnly || auditDateOnly || processingOnly ? 1 : accountOnly ? 7 : adminOnly ? 9 : 8)); assert.equal(report.journeys.length, configurations.length); assert.equal(report.exportDenials.length, processingOnly || adminOnly ? 0 : configurations.length);
assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []); assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com')); 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} controlled journeys,${report.screens.length} axe scans/captures; no mutation requests.`); console.log(`PASS:${report.journeys.length} controlled journeys,${report.screens.length} axe scans/captures; no mutation requests.`);
} finally { } finally {
fs.writeFileSync(path.join(out, processingOnly ? 'PROCESSING.json' : process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['Every API response is synthetic. In admin-only mode all mutations are blocked and account flows are NOT RUN; otherwise account export is a controlled 403 and confirmations are cancelled.', systemRefreshOnly ? 'Only initial unavailable, successful, stale and recovered System Status reads are exercised. No diagnostic action executed.' : userAlignmentOnly ? 'Only the Users table alignment, mobile layout and last-administrator disabled actions are exercised.' : auditDateOnly ? 'Only the populated audit table, timestamp geometry and keyboard scrolling are exercised.' : adminOnly ? 'System and audit populated states are synthetic read fixtures; no probe, maintenance, settings save, undo, restore or provider action executed.' : 'System monitor is unavailable and audit empty; detailed success/action scenarios are unit tests, not live qualification.', 'No real credentials, provider activation, model, native device, integrated security, production or release acceptance.'] }, null, 2)); fs.writeFileSync(path.join(out, processingOnly ? 'PROCESSING.json' : process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['Every API response is synthetic. In admin-only mode all mutations are blocked and account flows are NOT RUN; otherwise account export is a controlled 403 and confirmations are cancelled.', systemRefreshOnly ? 'Only initial unavailable, successful, stale and recovered System Status reads are exercised. No diagnostic action executed.' : userTwoFactorOnly ? 'Read-only 2FA and confirmation statuses are displayed alongside the existing last-administrator safeguards.' : userAlignmentOnly ? 'Only the Users table alignment, mobile layout and last-administrator disabled actions are exercised.' : auditDateOnly ? 'Only the populated audit table, timestamp geometry and keyboard scrolling are exercised.' : adminOnly ? 'System and audit populated states are synthetic read fixtures; no probe, maintenance, settings save, undo, restore or provider action executed.' : 'System monitor is unavailable and audit empty; detailed success/action scenarios are unit tests, not live qualification.', 'No real credentials, provider activation, model, native device, integrated security, production or release acceptance.'] }, null, 2));
await browser.close(); await browser.close();
} }