test(admin): qualify compact user actions
This commit is contained in:
@@ -15,6 +15,7 @@ const auditSearch = Boolean(process.env.A3_AUDIT_SEARCH);
|
||||
const auditFilters = Boolean(process.env.A3_AUDIT_FILTERS);
|
||||
const userAlignmentOnly = Boolean(process.env.A3_ADMIN_USER_ALIGNMENT);
|
||||
const userTwoFactorOnly = Boolean(process.env.A3_ADMIN_USER_2FA);
|
||||
const userActions = Boolean(process.env.A3_ADMIN_ACTIONS);
|
||||
const usersOnly = userAlignmentOnly || userTwoFactorOnly;
|
||||
const systemRefreshOnly = Boolean(process.env.A3_SYSTEM_REFRESH);
|
||||
assert.ok(!adminHeadings || adminOnly, 'Heading qualification uses the Admin fixtures.');
|
||||
@@ -22,8 +23,9 @@ assert.ok(!auditDateOnly || (adminOnly && !adminHeadings), 'Audit timestamp qual
|
||||
assert.ok(!auditSearch || auditDateOnly, 'Audit search uses the populated Audit fixture.');
|
||||
assert.ok(!auditFilters || auditSearch, 'Audit filters extend the recent search journey.');
|
||||
assert.ok(!usersOnly || (adminOnly && !adminHeadings && !auditDateOnly && !(userTwoFactorOnly && userAlignmentOnly)), 'Admin Users verification uses a single scope.');
|
||||
assert.ok(!userActions || userTwoFactorOnly, 'Admin action verification uses the populated Users fixture.');
|
||||
assert.ok(!systemRefreshOnly || (adminOnly && !adminHeadings && !auditDateOnly && !usersOnly), 'System refresh qualification uses its own scope.');
|
||||
const out = path.resolve(import.meta.dirname, auditFilters ? '../../docs/ui-modernisation/visual-correction/evidence/audit-filters' : 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');
|
||||
const out = path.resolve(import.meta.dirname, userActions ? '../../docs/ui-modernisation/visual-correction/evidence/admin-user-actions' : auditFilters ? '../../docs/ui-modernisation/visual-correction/evidence/audit-filters' : 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();
|
||||
@@ -69,7 +71,7 @@ try {
|
||||
'/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/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, twoFactorEnabled: userTwoFactorOnly ? true : undefined, 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 }, ...(userActions ? [{ id: 'a3-member', email: 'taylor@example.test', userName: 'taylor', emailConfirmed: true, twoFactorEnabled: false, roles: [], isCurrentUser: false, canRemoveAdmin: true }] : [])],
|
||||
'/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}/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 },
|
||||
@@ -129,17 +131,45 @@ try {
|
||||
await context.close();
|
||||
continue;
|
||||
}
|
||||
const openUserMenu = async email => {
|
||||
const trigger = page.getByRole('button', { name: t('adminUsersActionsFor').replace('{name}', email), exact: true });
|
||||
await trigger.press('Enter');
|
||||
const menu = page.getByRole('menu');
|
||||
await expect(menu).toBeVisible();
|
||||
return { trigger, menu };
|
||||
};
|
||||
const checkLastAdmin = async () => {
|
||||
const { trigger, menu } = await openUserMenu('alex@example.test');
|
||||
await expect(menu.getByRole('menuitem', { name: t('adminUsersRemoveAdmin'), exact: true })).toBeDisabled();
|
||||
await expect(menu.getByRole('menuitem', { name: t('adminUsersDelete'), exact: true })).toBeDisabled();
|
||||
await expect(menu.getByText(t('adminUsersLastAdminHelp'), { exact: true })).toBeVisible();
|
||||
if (userActions) await scan('last-admin-menu', true);
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(menu).toHaveCount(0);
|
||||
await expect(trigger).toBeFocused();
|
||||
};
|
||||
if (usersOnly) {
|
||||
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('adminUsersDelete'), exact: true })).toBeDisabled();
|
||||
await checkLastAdmin();
|
||||
if (userTwoFactorOnly) {
|
||||
await expect(page.getByText(t('adminUsers2faEnabled'), { exact: true })).toBeVisible();
|
||||
await expect(page.getByText(`${t('adminUsersConfirmed')}: ${t('yes')}`, { exact: true })).toBeVisible();
|
||||
await expect(page.getByText(`${t('adminUsersConfirmed')}: ${t('yes')}`, { exact: true }).first()).toBeVisible();
|
||||
}
|
||||
if (userActions) {
|
||||
const { trigger, menu } = await openUserMenu('taylor@example.test');
|
||||
await menu.getByRole('menuitem', { name: t('adminUsersDelete'), exact: true }).press('Enter');
|
||||
const dialog = page.getByRole('dialog');
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole('button', { name: t('cancel'), exact: true }).press('Enter');
|
||||
await expect(dialog).toHaveCount(0);
|
||||
await expect(trigger.or(page.getByRole('group', { name: t('adminUsersTitle'), exact: true })).and(page.locator(':focus'))).toHaveCount(1).catch(async error => {
|
||||
console.error('Focus after cancellation:', await page.evaluate(() => ({ tag: document.activeElement?.tagName, label: document.activeElement?.getAttribute('aria-label'), text: document.activeElement?.textContent?.slice(0, 100) })));
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
await scan('last-admin');
|
||||
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('row').nth(1).getByRole('gridcell').evaluateAll(nodes => nodes.map(cell => {
|
||||
const cellBounds = cell.getBoundingClientRect();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(cell);
|
||||
@@ -240,7 +270,7 @@ try {
|
||||
await page.goto('http://localhost:4338/settings/connected-accounts'); await expect(page.getByRole('heading', { name: t('connectedAccounts'), exact: true })).toBeVisible(); await scan('connections');
|
||||
}
|
||||
if (!accountOnly) {
|
||||
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('adminUsersDelete'), exact: true })).toBeDisabled(); await checkHeading('users', 'adminUsersSubtitle'); await scan('last-admin');
|
||||
await page.goto('http://localhost:4338/admin/users'); await checkLastAdmin(); await checkHeading('users', 'adminUsersSubtitle'); await scan('last-admin');
|
||||
await page.goto('http://localhost:4338/admin/audit'); await expect(page.getByText(t('adminAuditNoEvents'), { exact: true })).toBeVisible(); await scan('audit-empty');
|
||||
if (adminOnly) { populatedAudit = true; await page.reload(); await expect(page.getByText(t('adminAuditEventDetails'), { exact: true })).toBeVisible(); await checkHeading('auditLog', 'adminAuditSubtitle'); await scan('audit-populated'); }
|
||||
await page.goto('http://localhost:4338/admin/system'); await expect(page.getByText(t('adminSystemLoadFailed'), { exact: true })).toBeVisible(); await scan('system-unavailable');
|
||||
@@ -274,7 +304,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 * (auditFilters || systemRefreshOnly ? 3 : auditSearch ? 2 : 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 * (auditFilters || systemRefreshOnly ? 3 : auditSearch || userActions ? 2 : 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 {
|
||||
|
||||
Reference in New Issue
Block a user