test(admin): qualify recent audit search and responsive feedback
@@ -7,7 +7,7 @@ Owner addendum received 9 November 2026. Source `E:/Storage/Projects/Active/temp
|
||||
| Admin information isolation | The original redaction patch passed 62 tests on the security branch. [A3 integration](ADMIN_AUDIT_ISOLATION_VALIDATION.md) matched the exact source/test patch and passed 57 selected tests. The runs overlap and remain separate evidence. | 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. |
|
||||
| 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 | [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. |
|
||||
| Admin Users and Audit | [Aligned rows](visual-correction/ADMIN_USER_ALIGNMENT_REVIEW.md), [read-only 2FA status](visual-correction/ADMIN_USER_2FA_REVIEW.md) and [recent audit search](visual-correction/AUDIT_SEARCH_REVIEW.md) passed their own 36-configuration checks. Compact user actions, structured filters and safe expansion 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. |
|
||||
| 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. |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# A3-IM10 Cross-application qualification
|
||||
|
||||
Audit runtime `1a8a97a5` adds local metadata search for the loaded recent-event window. Ten frontend tests and 72 controlled captures and scans passed. See [audit search evidence](visual-correction/AUDIT_SEARCH_REVIEW.md).
|
||||
|
||||
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).
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Recent audit event search
|
||||
|
||||
Runtime `1a8a97a57894bde4029ca0d878df6d4a815424a4`. The EN/NB search filters the existing administrator response of up to 200 recent events. It matches event type, user identifiers, application title/company and exact numeric application ID. It does not match notes or old/new values. The query stays in component state and produces no network request. Undo and Restore retain their existing paths, admission and focus handling.
|
||||
|
||||
The focused test was red before the search existed. The first browser matrix passed its functional assertions but the 320 px Bokmål dark capture showed clipped no-match copy in the table's scroll area. The retained before capture records the problem. The final implementation displays no-match feedback at normal page width and verifies that it fits. Clearing the field restores the loaded rows.
|
||||
|
||||
**PASSED:** 10 selected Jest tests, full lint, export build with TypeScript, and 36 configurations with 72 scoped axe scans and captures. The pilot overlaps the matrix. Reviewed the narrow Bokmål no-match and English desktop populated captures. No writes or page errors. Browser data was synthetic, with 36 unavailable company reads outside Audit.
|
||||
|
||||
Full historical search, date/time and other structured filters, safe event expansion, integrated authorisation and native review remain open.
|
||||
@@ -39,6 +39,7 @@ Owner correction: 3 November 2026. This register extends IM10 and the original 3
|
||||
| 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. |
|
||||
| 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. |
|
||||
| V32 | Admin Audit search | The first no-match state clipped its message inside the wide table on phones | RESOLVED in `1a8a97a5` | [Audit search review](AUDIT_SEARCH_REVIEW.md) records the narrow screenshot and fitting feedback. |
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -219,9 +219,9 @@
|
||||
"AdminAuditPage"
|
||||
],
|
||||
"visualStatus": "OPEN_COLLECTION_RENDERED_SCOPED_RENDERED_REVIEW",
|
||||
"afterStatus": "PASSED:36 focused timestamp and keyboard scroll configurations on 2dc8fe4c; earlier empty and populated states retained; no undo/restore",
|
||||
"afterStatus": "PASSED:36 recent-search, no-match, timestamp and keyboard scroll configurations; browser mutations not run",
|
||||
"functionalCriteria": "Original preservation register retained; no capability acceptance promotion",
|
||||
"evidence": "AUDIT_DATE_REVIEW.md; evidence/audit-date/BROWSER.json; earlier ADMIN_HEADINGS_REVIEW.md"
|
||||
"evidence": "AUDIT_SEARCH_REVIEW.md; evidence/audit-search/BROWSER.json; earlier AUDIT_DATE_REVIEW.md and ADMIN_HEADINGS_REVIEW.md"
|
||||
},
|
||||
{
|
||||
"route": "/admin/users",
|
||||
|
||||
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"screens": [
|
||||
{
|
||||
"width": 320,
|
||||
"language": "nb",
|
||||
"mode": "dark",
|
||||
"state": "audit-no-matches",
|
||||
"capture": "viewport at top",
|
||||
"violations": []
|
||||
},
|
||||
{
|
||||
"width": 320,
|
||||
"language": "nb",
|
||||
"mode": "dark",
|
||||
"state": "audit-populated",
|
||||
"capture": "viewport at top",
|
||||
"violations": []
|
||||
}
|
||||
],
|
||||
"journeys": [
|
||||
{
|
||||
"width": 320,
|
||||
"language": "nb",
|
||||
"mode": "dark",
|
||||
"auditSearch": "PASS",
|
||||
"auditTimestampReadable": "PASS",
|
||||
"headerHeight": 44.5,
|
||||
"eventHeight": 79.5,
|
||||
"keyboardScroll": "PASS",
|
||||
"auditMutations": "NOT RUN"
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"unexpectedWrites": [],
|
||||
"exportDenials": [],
|
||||
"unavailableReads": [
|
||||
"/api/companies"
|
||||
],
|
||||
"blockedExternal": [
|
||||
"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.",
|
||||
"Only the populated audit table, timestamp geometry and keyboard scrolling are exercised.",
|
||||
"No real credentials, provider activation, model, native device, integrated security, production or release acceptance."
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 55 KiB |
@@ -44,7 +44,8 @@ const screens = [
|
||||
{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:'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-search',state:'audit-populated',source:'1a8a97a5',scope:'Recent event search works with the existing read response and retains keyboard access to the table.'},
|
||||
{name:'Administration audit no matches',reference:'admin',folder:'audit-search',state:'audit-no-matches',source:'1a8a97a5',scope:'The empty result is readable without horizontal scrolling. Private note text is outside search.'},
|
||||
{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 benchmark',reference:'admin',folder:'admin-headings',state:'system-findings',source:'c7e8627c',scope:'Divided synthetic findings/weak files without nested panels/shadows. Unapproved fixture; no benchmark execution or engine acceptance.'},
|
||||
|
||||
@@ -11,15 +11,17 @@ const accountOnly = Boolean(process.env.A3_ACCOUNT_FLAT);
|
||||
const adminOnly = Boolean(process.env.A3_ADMIN_FLAT);
|
||||
const adminHeadings = Boolean(process.env.A3_ADMIN_HEADINGS);
|
||||
const auditDateOnly = Boolean(process.env.A3_AUDIT_DATE);
|
||||
const auditSearch = Boolean(process.env.A3_AUDIT_SEARCH);
|
||||
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);
|
||||
assert.ok(!adminHeadings || adminOnly, 'Heading qualification uses the Admin fixtures.');
|
||||
assert.ok(!auditDateOnly || (adminOnly && !adminHeadings), 'Audit timestamp qualification uses its own scope.');
|
||||
assert.ok(!auditSearch || auditDateOnly, 'Audit search uses the populated Audit fixture.');
|
||||
assert.ok(!usersOnly || (adminOnly && !adminHeadings && !auditDateOnly && !(userTwoFactorOnly && userAlignmentOnly)), 'Admin Users verification uses a single scope.');
|
||||
assert.ok(!systemRefreshOnly || (adminOnly && !adminHeadings && !auditDateOnly && !usersOnly), 'System refresh qualification uses its own scope.');
|
||||
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');
|
||||
const out = path.resolve(import.meta.dirname, auditSearch ? '../../docs/ui-modernisation/visual-correction/evidence/audit-search' : 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 });
|
||||
const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], exportDenials: [], unavailableReads: [], blockedExternal: [] };
|
||||
const browser = await chromium.launch();
|
||||
@@ -27,7 +29,7 @@ const processingOnly = Boolean(process.env.A3_PROCESSING_ONLY);
|
||||
assert.ok(!(processingOnly && accountOnly), 'Account and processing qualification are distinct scopes.');
|
||||
assert.ok(!adminOnly || !(processingOnly || accountOnly), 'Admin qualification is a distinct scope.');
|
||||
try {
|
||||
const configurations = process.env.A3_PILOT ? (userAlignmentOnly ? [[1440, 'en', 'light']] : auditDateOnly ? [[768, 'nb', 'dark']] : adminOnly ? [[320, 'en', 'light'], [1440, 'en', 'light']] : [[320, 'en', 'light']]) : (accountOnly || adminOnly ? [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])));
|
||||
const configurations = process.env.A3_PILOT ? (userAlignmentOnly ? [[1440, 'en', 'light']] : auditSearch ? [[320, 'nb', 'dark']] : auditDateOnly ? [[768, 'nb', 'dark']] : adminOnly ? [[320, 'en', 'light'], [1440, 'en', 'light']] : [[320, 'en', 'light']]) : (accountOnly || adminOnly ? [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]; };
|
||||
@@ -154,6 +156,20 @@ try {
|
||||
await page.goto('http://localhost:4338/admin/audit');
|
||||
const table = page.getByRole('table', { name: t('adminAuditTitle'), exact: true });
|
||||
await expect(table.getByRole('cell', { name: 'Applied -> Interview Synthetic audit event', exact: true })).toBeVisible();
|
||||
if (auditSearch) {
|
||||
const input = page.getByRole('searchbox', { name: t('adminAuditSearch'), exact: true });
|
||||
await expect(input).toHaveAccessibleDescription(t('adminAuditSearchScope'));
|
||||
await input.pressSequentially('42');
|
||||
await expect(table.getByText('StatusChanged', { exact: true })).toBeVisible();
|
||||
await input.fill('Synthetic audit event');
|
||||
const empty = page.getByText(t('adminAuditNoMatches'), { exact: true });
|
||||
await expect(empty).toBeVisible();
|
||||
assert.ok(await empty.evaluate(node => node.scrollWidth <= node.clientWidth && node.getBoundingClientRect().right <= innerWidth), 'Empty search feedback fits the viewport.');
|
||||
await scan('audit-no-matches');
|
||||
await input.press('ControlOrMeta+a');
|
||||
await input.press('Backspace');
|
||||
await expect(table.getByText('StatusChanged', { exact: true })).toBeVisible();
|
||||
}
|
||||
const heading = table.getByRole('columnheader', { name: t('adminAuditAt'), exact: true });
|
||||
const dateCell = table.getByRole('row').nth(1).getByRole('cell').first();
|
||||
const wordsStayOnOneLine = element => {
|
||||
@@ -179,7 +195,7 @@ try {
|
||||
await expect(region).toBeFocused();
|
||||
await expect.poll(() => region.evaluate(node => node.scrollLeft)).toBeGreaterThan(0);
|
||||
}
|
||||
report.journeys.push({ width, language, mode, auditTimestampReadable: 'PASS', headerHeight: headerBounds.height, eventHeight: eventBounds.height, keyboardScroll: requiresScroll ? 'PASS' : 'NOT REQUIRED', auditMutations: 'NOT RUN' });
|
||||
report.journeys.push({ width, language, mode, auditSearch: auditSearch ? 'PASS' : 'NOT RUN', auditTimestampReadable: 'PASS', headerHeight: headerBounds.height, eventHeight: eventBounds.height, keyboardScroll: requiresScroll ? 'PASS' : 'NOT REQUIRED', auditMutations: 'NOT RUN' });
|
||||
await context.close(); continue;
|
||||
}
|
||||
if (!processingOnly) {
|
||||
@@ -234,7 +250,7 @@ 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();
|
||||
}
|
||||
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.equal(report.screens.length, configurations.length * (auditSearch ? 2 : 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'));
|
||||
console.log(`PASS:${report.journeys.length} controlled journeys,${report.screens.length} axe scans/captures; no mutation requests.`);
|
||||
} finally {
|
||||
|
||||