test(qa): qualify route matrix and document stale-write failure

This commit is contained in:
cesnimda
2026-10-22 22:27:51 +02:00
parent 322b025350
commit 82be698dcf
69 changed files with 41364 additions and 1 deletions
@@ -0,0 +1,74 @@
import { test, expect } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
import AxeBuilder from '../../../../../job-tracker-ui/node_modules/@axe-core/playwright';
import { translations } from '../../../../../job-tracker-ui/src/i18n/translations';
const api = 'http://localhost:5302/api';
for (const language of ['en', 'nb'] as const) for (const theme of ['light', 'dark'] as const) {
test(`P9 settled badge drawer and editor triage ${language} ${theme}`, async ({ page, context, playwright }, info) => {
test.setTimeout(120000);
const t = translations[language];
await page.setViewportSize({ width: 320, height: 900 });
await page.emulateMedia({ reducedMotion: 'reduce' });
await context.addInitScript(({ language, theme }) => {
localStorage.setItem('uiLanguage', language); localStorage.setItem('jobtracker.themeMode', theme);
}, { language, theme });
await context.route('**/*', route => {
const url = new URL(route.request().url());
return ['localhost', '127.0.0.1'].includes(url.hostname) || ['data:', 'blob:', 'about:'].includes(url.protocol) ? route.continue() : route.abort();
});
const admin = await playwright.request.newContext();
try {
const password = 'E2ePassword123!';
expect((await admin.post(`${api}/auth/login`, { data: { email: 'e2e@example.test', password } })).status()).toBe(200);
const adminMe = await (await admin.get(`${api}/auth/me`)).json();
const csrf = (await admin.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value;
const email = `p9-axe-${language}-${theme}-${Date.now()}@example.test`;
expect((await admin.post(`${api}/users`, { headers: { 'X-CSRF-TOKEN': csrf, 'X-Session-Context': adminMe.sessionContext }, data: { email, password, roles: [] } })).status()).toBe(200);
expect((await page.request.post(`${api}/auth/login`, { data: { email, password } })).status()).toBe(200);
const me = await (await page.request.get(`${api}/auth/me`)).json();
const ownerCsrf = (await page.request.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value;
expect((await page.request.put(`${api}/auth/preferences/language`, { headers: { 'X-CSRF-TOKEN': ownerCsrf, 'X-Session-Context': me.sessionContext }, data: { language: language === 'nb' ? 'nb-NO' : 'en' } })).status()).toBe(204);
await page.goto('/dashboard');
await expect(page.getByRole('button', { name: t.dashboardCustomize, exact: true })).toBeVisible();
const badge = await page.locator('.MuiBadge-invisible').evaluateAll(nodes => nodes.map(node => ({
ariaHidden: node.getAttribute('aria-hidden'), text: node.textContent, width: node.getBoundingClientRect().width,
height: node.getBoundingClientRect().height, visibility: getComputedStyle(node).visibility, transform: getComputedStyle(node).transform })));
expect(badge.length).toBeGreaterThan(0);
expect(badge.every(node => node.ariaHidden === 'true' && node.width * node.height === 0)).toBe(true);
const scans: object[] = [];
const scan = async (state: string) => {
const result = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze();
scans.push({ state, violations: result.violations, incomplete: result.incomplete });
expect.soft(result.violations, state).toEqual([]);
};
await scan('dashboard-settled');
const trigger = page.getByRole('button', { name: t.openNavigation, exact: true });
await trigger.focus(); await trigger.press('Enter');
const drawer = page.getByRole('dialog', { name: t.navigation, exact: true });
await expect(drawer.getByRole('button', { name: t.dashboard, exact: true })).toBeFocused();
await scan('drawer-open');
await page.keyboard.press('Escape');
await expect(drawer).not.toBeVisible(); await expect(trigger).toBeFocused();
// Backdrop/root exit can precede Slide exit; await the complete settled predicate.
const inspectDrawer = () => page.locator('.MuiDrawer-modal').evaluateAll(nodes => nodes.map(node => ({
ariaHidden: node.getAttribute('aria-hidden'), visibility: getComputedStyle(node).visibility,
containsActive: node.contains(document.activeElement),
visibleTabbables: [...node.querySelectorAll('[tabindex="0"],button,a[href],input,select')].filter(element => getComputedStyle(element).visibility !== 'hidden' && element.getClientRects().length > 0).length })));
const drawerAfterEscape = await inspectDrawer();
await expect.poll(async () => (await inspectDrawer()).every(node => node.ariaHidden === 'true' && node.visibility === 'hidden' && !node.containsActive && node.visibleTabbables === 0)).toBe(true);
const hiddenDrawer = await inspectDrawer();
expect(hiddenDrawer.every(node => node.ariaHidden === 'true' && !node.containsActive && node.visibleTabbables === 0)).toBe(true);
for (let index = 0; index < 24; index++) {
await page.keyboard.press('Tab');
expect(await page.evaluate(() => !!document.activeElement?.closest('.MuiDrawer-modal[aria-hidden="true"]'))).toBe(false);
}
await scan('drawer-closed-settled');
await page.getByRole('button', { name: t.dashboardCustomize, exact: true }).click();
const editor = page.getByRole('region', { name: t.dashboardEditing, exact: true });
await expect(editor.getByRole('combobox')).toHaveCount(9);
await scan('editor-settled');
await info.attach('phase9-settled-axe', { body: JSON.stringify({ language, theme, badge, drawerAfterEscape, hiddenDrawer, scans,
limits: 'Finite native DOM/keyboard evidence; remaining overlapping background contrast and actual screen-reader perception still require human assessment.' }, null, 2), contentType: 'application/json' });
} finally { await admin.dispose(); }
});
}