test(ui): qualify responsive secondary and application routes
This commit is contained in:
@@ -1,22 +1,26 @@
|
||||
// Actual exported React UI; all API responses and status writes are synthetic.
|
||||
import { chromium } from '@playwright/test';
|
||||
import { chromium, expect } from '@playwright/test';
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/applications/browser');
|
||||
const visualMatrix = Boolean(process.env.A3_VISUAL_MATRIX);
|
||||
const widths = visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440];
|
||||
const configurations = process.env.A3_PILOT ? [[320, 'en', 'light']] : widths.flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode])));
|
||||
const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/applications' : '../../docs/ui-modernisation/evidence/applications/browser');
|
||||
fs.mkdirSync(out, { recursive: true });
|
||||
const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], unavailableReads: [], blockedExternal: [] };
|
||||
const report = { screens: [], journeys: [], aliases: [], errors: [], unexpectedWrites: [], mockedWrites: [], unavailableReads: [], blockedExternal: [] };
|
||||
const browser = await chromium.launch();
|
||||
try {
|
||||
for (const width of [320, 390, 768, 1440]) for (const language of ['en', 'nb']) for (const mode of ['light', 'dark']) {
|
||||
for (const [width, language, mode] of configurations) {
|
||||
console.log(`Checking ${width}/${language}/${mode}`);
|
||||
const context = await browser.newContext({ viewport: { width, height: 1000 }, reducedMotion: 'reduce' });
|
||||
const page = await context.newPage(); page.on('pageerror', error => report.errors.push(error.message));
|
||||
await page.addInitScript(({ language, mode }) => { localStorage.setItem('uiLanguage', language); localStorage.setItem('jobtracker.themeMode', mode); }, { language, mode });
|
||||
const jobs = ['Community nurse', 'Primary teacher', 'Electrical engineer'].map((jobTitle, index) => ({ id: 42 + index, version: 7, jobTitle, company: { id: 1, name: 'Fjord Care' }, companyId: 1, status: 'Saved', location: 'Oslo', countryCode: 'NO', source: 'nav', salary: null, dateApplied: null, savedAt: '2026-10-01T09:00:00Z', deadline: '2026-11-12', followUpAt: null, tags: 'Care coordination', description: 'Recorded source advertisement.', isDeleted: false, needsFollowUp: false, workflowSignal: null }));
|
||||
await page.route('**/*', route => {
|
||||
const req = route.request(), url = new URL(req.url());
|
||||
if (!['localhost', '127.0.0.1'].includes(url.hostname)) { report.blockedExternal.push(url.origin); return route.abort(); }
|
||||
if (url.origin !== 'http://localhost:4338' && !(url.origin === 'http://localhost:5202' && url.pathname.startsWith('/api/'))) { report.blockedExternal.push(url.origin); return route.abort(); }
|
||||
if (!url.pathname.startsWith('/api/')) return route.continue();
|
||||
const answer = data => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(data) });
|
||||
if (req.method() === 'PATCH' && url.pathname === '/api/jobapplications/42/status') {
|
||||
@@ -51,6 +55,12 @@ try {
|
||||
const search = page.getByRole('textbox', { name: language === 'en' ? 'Search' : 'Søk', exact: true }); await search.waitFor();
|
||||
await page.locator('[data-job-row-id="42"], [data-job-card-id="42"]').first().waitFor();
|
||||
await scan('table');
|
||||
if (visualMatrix) {
|
||||
await page.getByRole('button', { name: language === 'en' ? 'Add Job' : 'Legg til jobb', exact: true }).click();
|
||||
await expect(page.getByRole('dialog')).toBeVisible(); await scan('add-job');
|
||||
await page.keyboard.press('Escape'); await expect(page.getByRole('dialog')).toHaveCount(0);
|
||||
assert.equal(report.unexpectedWrites.length, 0);
|
||||
}
|
||||
await search.fill('nurse'); await page.waitForURL(/q=nurse/);
|
||||
await page.getByRole('link', { name: language === 'en' ? 'Board' : 'Tavle', exact: true }).click();
|
||||
const card = page.locator('[data-kanban-card-id="42"]'); await card.waitFor();
|
||||
@@ -72,13 +82,22 @@ try {
|
||||
await page.waitForURL(/view=board/); assert.equal(new URL(page.url()).searchParams.get('q'), 'nurse');
|
||||
await page.locator('[data-kanban-card-id="42"] [role="button"]').waitFor();
|
||||
report.journeys.push({ width, language, mode, searchBoardPreciseStatusDetailContextReturn: 'PASS' });
|
||||
if (visualMatrix && width === 320 && language === 'en' && mode === 'light') {
|
||||
for (const [legacy, destination, parameter] of [['/kanban?q=nurse#retained', '/jobs', ['view', 'board']], ['/applications/42?section=overview', '/jobs/42', ['section', 'overview']], ['/correspondence/review?q=recorded#retained', '/correspondence', ['view', 'review']]]) {
|
||||
await page.goto(`http://localhost:4338${legacy}`);
|
||||
await expect.poll(() => new URL(page.url()).pathname).toBe(destination);
|
||||
assert.equal(new URL(page.url()).searchParams.get(parameter[0]), parameter[1]);
|
||||
if (!legacy.startsWith('/applications/')) { assert.equal(new URL(page.url()).hash, '#retained'); assert.equal(new URL(page.url()).searchParams.get('q'), new URL(legacy, 'http://localhost:4338').searchParams.get('q')); }
|
||||
report.aliases.push({ legacy, destination, queryPreserved: 'PASS' });
|
||||
}
|
||||
}
|
||||
await context.close();
|
||||
}
|
||||
assert.equal(report.screens.length, 48); assert.equal(report.journeys.length, 16); assert.equal(report.mockedWrites.length, 16);
|
||||
assert.equal(report.screens.length, configurations.length * (visualMatrix ? 4 : 3)); assert.equal(report.journeys.length, configurations.length); assert.equal(report.mockedWrites.length, configurations.length); assert.equal(report.aliases.length, visualMatrix ? 3 : 0);
|
||||
assert.deepEqual(report.errors, []); assert.deepEqual(report.unexpectedWrites, []);
|
||||
assert.ok(report.blockedExternal.every(origin => origin === 'https://fonts.googleapis.com'));
|
||||
console.log('PASS: 16 controlled table/board/detail journeys,48 axe scans/captures;16 mocked status writes.');
|
||||
console.log(`PASS:${report.journeys.length} controlled table/board/detail journeys,${report.screens.length} axe scans/captures;${report.mockedWrites.length} mocked status writes.`);
|
||||
} finally {
|
||||
fs.writeFileSync(path.join(out, 'BROWSER.json'), JSON.stringify({ ...report, limits: ['controlled fixtures, not real backend integration', 'secondary service reads may return honest unavailable states', 'no native device, provider, model, security or release qualification'] }, null, 2));
|
||||
fs.writeFileSync(path.join(out, process.env.A3_PILOT ? 'PILOT.json' : 'BROWSER.json'), JSON.stringify({ ...report, limits: ['controlled fixtures, not real backend integration', 'secondary service reads may return honest unavailable states', 'no native device, provider, model, security or release qualification'] }, null, 2));
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user