test(ui): qualify connected discovery to tracker journey
This commit is contained in:
@@ -4,29 +4,47 @@ import AxeBuilder from '@axe-core/playwright';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import ts from 'typescript';
|
||||
const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText;
|
||||
const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`);
|
||||
const visualMatrix = process.env.A3_VISUAL_MATRIX === '1';
|
||||
const widths = process.env.A3_PILOT ? [320] : visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440];
|
||||
const trackingOnly = process.env.A3_DISCOVERY_TRACK === '1';
|
||||
const widths = process.env.A3_PILOT ? (trackingOnly ? [320, 1440] : [320]) : visualMatrix || trackingOnly ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440];
|
||||
const languages = process.env.A3_PILOT ? ['en'] : ['en', 'nb'];
|
||||
const modes = process.env.A3_PILOT ? ['light'] : ['light', 'dark'];
|
||||
const expectedJourneys = widths.length * languages.length * modes.length;
|
||||
const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/discovery' : '../../docs/ui-modernisation/evidence/discovery/browser');
|
||||
const out = path.resolve(import.meta.dirname, trackingOnly ? '../../docs/ui-modernisation/visual-correction/evidence/discovery-track' : visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/discovery' : '../../docs/ui-modernisation/evidence/discovery/browser');
|
||||
fs.mkdirSync(out, { recursive: true });
|
||||
const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], blockedExternal: [] };
|
||||
const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], mockedWrites: [], previewRequests: [], unavailableReads: [], blockedExternal: [] };
|
||||
const browser = await chromium.launch();
|
||||
try {
|
||||
for (const width of widths) for (const language of languages) for (const mode of modes) {
|
||||
console.log(`Checking ${width}/${language}/${mode}`);
|
||||
const t = key => { assert.ok(translations[language][key], key); return translations[language][key]; };
|
||||
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((title, index) => ({ id: `vacancy-${index}`, title, company: 'Synthetic employer', location: 'Oslo', modifiedAt: '2026-11-02T10:00:00Z', deadline: '2026-11-12T12:00:00Z', url: `https://example.test/vacancy-${index}`, source: 'nav', sourceName: 'NAV Arbeidsplassen', acquisitionType: 'searched', retrievedAt: '2026-11-03T10:00:00Z', countryCode: 'NO' }));
|
||||
let saved = null, searches = 0;
|
||||
let tracked = null;
|
||||
const company = { id: 1, name: 'Synthetic employer' };
|
||||
const description = 'Synthetic nurse role: supervised care and recorded handover.';
|
||||
await page.route('**/*', route => {
|
||||
const req = route.request(), url = new URL(req.url());
|
||||
if (trackingOnly && url.origin !== 'http://localhost:4338' && !(url.origin === 'http://localhost:5202' && url.pathname.startsWith('/api/'))) { report.blockedExternal.push(url.origin); return route.abort(); }
|
||||
if (!['localhost', '127.0.0.1'].includes(url.hostname)) { 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) });
|
||||
const note = action => report.mockedWrites.push({ width, language, mode, action });
|
||||
if (trackingOnly && req.method() === 'POST' && url.pathname === '/api/jobimport/preview') {
|
||||
assert.deepEqual(req.postDataJSON(), { url: jobs[0].url }); report.previewRequests.push({ width, language, mode, url: jobs[0].url });
|
||||
return answer({ success: true, title: jobs[0].title, company: company.name, location: 'Oslo', source: 'nav', countryCode: 'NO', sourceUrl: jobs[0].url, description, translatedDescription: null, language: 'en', tags: ['Care'], deadline: jobs[0].deadline });
|
||||
}
|
||||
if (trackingOnly && req.method() === 'POST' && url.pathname === '/api/jobapplications') {
|
||||
const body = req.postDataJSON(); assert.equal(tracked, null); for (const [key, value] of Object.entries({ jobTitle: jobs[0].title, companyId: company.id, status: 'Saved', jobUrl: jobs[0].url, source: 'nav', countryCode: 'NO', description, descriptionLanguage: 'en', deadline: '2026-11-12', followUpAt: null })) assert.equal(body[key], value, key);
|
||||
tracked = { ...body, id: 42, version: 1, company, savedAt: '2026-11-04T10:00:00Z', isDeleted: false, needsFollowUp: false, workflowSignal: null }; note('createTrackedJob'); return answer({ id: 42 });
|
||||
}
|
||||
if (trackingOnly && req.method() !== 'GET') { report.unexpectedWrites.push(url.pathname); return route.abort(); }
|
||||
if (req.method() === 'POST' && url.pathname === '/api/job-discovery/saved-searches') {
|
||||
const data = req.postDataJSON(); assert.deepEqual(data, { name: 'Care roles', query: 'nurse', location: 'Oslo' });
|
||||
saved = { ...data, id: 7, isActive: true, resultCount: 0, dismissedCount: 0 }; note('create'); return answer(saved);
|
||||
@@ -48,11 +66,13 @@ try {
|
||||
'/api/auth/config': { requireAuth: true },
|
||||
'/api/auth/me': { id: 'a3-discovery-owner', sessionContext: 'a3-discovery-session', firstName: 'Alex', displayName: 'Alex Sample', roles: [], uiLanguage: language === 'nb' ? 'nb-NO' : 'en' },
|
||||
'/api/jobapplications/reminders': [], '/api/notifications/unread-count': { count: 0 },
|
||||
...(trackingOnly ? { '/api/companies': [company], '/api/jobapplications': { items: tracked ? [tracked] : [], total: tracked ? 1 : 0, page: 1, pageSize: 15 }, '/api/jobapplications/duplicate-check': { hasDuplicates: false, matches: [] } } : {}),
|
||||
};
|
||||
if (!Object.hasOwn(fixtures, url.pathname)) report.unavailableReads.push(url.pathname);
|
||||
return route.fulfill({ status: Object.hasOwn(fixtures, url.pathname) ? 200 : 404, contentType: 'application/json', body: JSON.stringify(fixtures[url.pathname] ?? {}) });
|
||||
});
|
||||
const scan = async state => {
|
||||
if (width < 768 && state !== 'initial') {
|
||||
if (width < 768 && ['results', 'saved'].includes(state)) {
|
||||
await filters.getByRole('button', { name: language === 'en' ? 'Jump to results' : 'Gå til resultater', exact: true }).click();
|
||||
assert.equal(await results.evaluate(node => node === document.activeElement), true);
|
||||
} else await page.evaluate(() => window.scrollTo(0, 0));
|
||||
@@ -75,6 +95,18 @@ try {
|
||||
const search = page.getByRole('button', { name: language === 'en' ? 'Search NAV' : 'Søk i NAV', exact: true });
|
||||
await search.click(); await results.getByRole('article', { name: 'Electrical engineer', exact: true }).waitFor();
|
||||
assert.equal(await results.getByRole('article').count(), 3); await scan('results');
|
||||
if (trackingOnly) {
|
||||
const article = results.getByRole('article', { name: jobs[0].title, exact: true });
|
||||
await expect(article.getByRole('link', { name: t('jobDiscoveryViewListing'), exact: true })).toHaveAttribute('href', jobs[0].url);
|
||||
await article.getByRole('button', { name: t('jobDiscoverySaveTracker'), exact: true }).click();
|
||||
const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); await expect(dialog).toContainText(t('addJob'));
|
||||
await expect(dialog.getByRole('textbox', { name: t('addJobModalDescriptionOriginal'), exact: true })).toHaveValue(description); assert.equal(report.previewRequests.filter(r => r.width === width && r.language === language && r.mode === mode).length, 1); assert.equal(tracked, null);
|
||||
await scan('review'); await dialog.getByRole('button', { name: t('continue'), exact: true }).click();
|
||||
for (let step = 2; step < 5; step++) await dialog.getByRole('button', { name: t('skipAndContinue'), exact: true }).click();
|
||||
await dialog.getByRole('button', { name: t('createJob'), exact: true }).click(); await expect(dialog).toBeHidden();
|
||||
await expect(page.locator('[data-job-row-id="42"], [data-job-card-id="42"]').first()).toContainText(jobs[0].title); await scan('tracked');
|
||||
assert.ok(tracked); report.journeys.push({ width, language, mode, discoveryPreviewReviewTracked: 'PASS', optionalAttachmentsSkippedWithExistingControls: 'PASS', savedSearchMutations: 'NOT RUN', externalAdvertAndProviderAccess: 'NOT RUN' }); await context.close(); continue;
|
||||
}
|
||||
await role.fill('nurse'); await page.getByRole('textbox', { name: language === 'en' ? 'Municipality' : 'Kommune', exact: true }).fill('Oslo');
|
||||
await page.getByRole('textbox', { name: language === 'en' ? 'Search name' : 'Navn på søket', exact: true }).fill('Care roles');
|
||||
await page.getByRole('button', { name: language === 'en' ? 'Save search' : 'Lagre søk', exact: true }).click();
|
||||
@@ -93,7 +125,7 @@ try {
|
||||
report.journeys.push({ width, language, mode, searchSavePauseRunDismissConfirmedDelete: 'PASS' });
|
||||
await context.close();
|
||||
}
|
||||
assert.equal(report.screens.length, expectedJourneys * 3); assert.equal(report.journeys.length, expectedJourneys); assert.equal(report.mockedWrites.length, expectedJourneys * 5);
|
||||
assert.equal(report.screens.length, expectedJourneys * (trackingOnly ? 4 : 3)); assert.equal(report.journeys.length, expectedJourneys); assert.equal(report.mockedWrites.length, expectedJourneys * (trackingOnly ? 1 : 5)); assert.equal(report.previewRequests.length, trackingOnly ? expectedJourneys : 0);
|
||||
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 discovery journeys,${report.screens.length} axe scans/captures,${report.mockedWrites.length} intercepted fixture mutations.`);
|
||||
|
||||
Reference in New Issue
Block a user