test(admin): qualify audit timestamps and table density
This commit is contained in:
@@ -10,8 +10,10 @@ const { translations } = await import(`data:text/javascript;base64,${Buffer.from
|
||||
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);
|
||||
assert.ok(!adminHeadings || adminOnly, 'Heading qualification uses the Admin fixtures.');
|
||||
const out = path.resolve(import.meta.dirname, 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');
|
||||
assert.ok(!auditDateOnly || (adminOnly && !adminHeadings), 'Audit timestamp qualification uses its own scope.');
|
||||
const out = path.resolve(import.meta.dirname, 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();
|
||||
@@ -19,7 +21,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 ? (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 ? (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]; };
|
||||
@@ -85,6 +87,39 @@ try {
|
||||
await expect(page.getByRole('heading', { name: t(title), exact: true, level: 1 })).toBeVisible();
|
||||
await expect(page.getByText(t(subtitle), { exact: true })).toBeVisible();
|
||||
};
|
||||
if (auditDateOnly) {
|
||||
populatedAudit = true;
|
||||
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();
|
||||
const heading = table.getByRole('columnheader', { name: t('adminAuditAt'), exact: true });
|
||||
const dateCell = table.getByRole('row').nth(1).getByRole('cell').first();
|
||||
const wordsStayOnOneLine = element => {
|
||||
const text = element.firstChild;
|
||||
if (!text || text.nodeType !== Node.TEXT_NODE) return false;
|
||||
return [...text.textContent.matchAll(/\S+/g)].every(word => {
|
||||
const range = document.createRange();
|
||||
range.setStart(text, word.index);
|
||||
range.setEnd(text, word.index + word[0].length);
|
||||
return range.getClientRects().length === 1;
|
||||
});
|
||||
};
|
||||
assert.ok(await heading.evaluate(wordsStayOnOneLine), 'Audit column heading breaks within a word');
|
||||
assert.ok(await dateCell.evaluate(wordsStayOnOneLine), 'Audit date or time breaks within its formatted value');
|
||||
const headerBounds = await table.getByRole('row').first().boundingBox();
|
||||
const eventBounds = await table.getByRole('row').nth(1).boundingBox();
|
||||
assert.ok(headerBounds.height <= 72 && eventBounds.height <= 128, `A short audit event should stay compact (header ${headerBounds.height}px, row ${eventBounds.height}px)`);
|
||||
await scan('audit-populated');
|
||||
const region = page.getByRole('region', { name: t('adminAuditTitle'), exact: true });
|
||||
const requiresScroll = await region.evaluate(node => node.scrollWidth > node.clientWidth);
|
||||
if (requiresScroll) {
|
||||
await region.press('ArrowRight');
|
||||
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' });
|
||||
await context.close(); continue;
|
||||
}
|
||||
if (!processingOnly) {
|
||||
if (!adminOnly) {
|
||||
await page.goto('http://localhost:4338/profile');
|
||||
@@ -137,10 +172,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();
|
||||
}
|
||||
assert.equal(report.screens.length, configurations.length * (accountOnly ? 7 : processingOnly ? 1 : 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 * (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 {
|
||||
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.', 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.', 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user