diff --git a/job-tracker-ui/e2e/job-discovery.spec.ts b/job-tracker-ui/e2e/job-discovery.spec.ts index 905fb822..fa518626 100644 --- a/job-tracker-ui/e2e/job-discovery.spec.ts +++ b/job-tracker-ui/e2e/job-discovery.spec.ts @@ -12,6 +12,9 @@ async function login(page: Page) { } test.beforeEach(async ({ page }) => { + await page.route("**/*", route => ["localhost", "127.0.0.1", "[::1]"].includes(new URL(route.request().url()).hostname) + ? route.continue() + : route.abort()); await page.addInitScript(() => window.localStorage.setItem("uiLanguage", "en")); }); @@ -55,9 +58,14 @@ test("discovery remains usable across widths and opens the reviewed import", asy await page.getByLabel("Role or company").fill("utvikler"); await page.keyboard.press("Tab"); - await expect(page.getByLabel("Municipality")).toBeFocused(); - await page.keyboard.press("Tab"); await expect(page.getByRole("button", { name: "Search NAV" })).toBeFocused(); + await page.keyboard.press("Tab"); + await expect(page.getByRole("button", { name: "Search filters", exact: true })).toBeFocused(); + await page.keyboard.press("Enter"); + await expect(page.getByRole("dialog", { name: "Search filters" }).getByRole("button", { name: "Close", exact: true })).toBeFocused(); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await expect(page.getByLabel("Municipality")).toBeFocused(); await page.keyboard.press("Enter"); await expect(page.getByText("2 vacancies found")).toBeVisible(); diff --git a/job-tracker-ui/scripts/verify-a3-discovery.mjs b/job-tracker-ui/scripts/verify-a3-discovery.mjs index ce8e46dc..db2d59dc 100644 --- a/job-tracker-ui/scripts/verify-a3-discovery.mjs +++ b/job-tracker-ui/scripts/verify-a3-discovery.mjs @@ -9,13 +9,14 @@ const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/transla const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`); const visualMatrix = process.env.A3_VISUAL_MATRIX === '1'; const trackingOnly = process.env.A3_DISCOVERY_TRACK === '1'; -const searchFirst = process.env.A3_DISCOVERY_SEARCH_FIRST === '1'; +const mobileFilters = process.env.A3_DISCOVERY_MOBILE_FILTERS === '1'; +const searchFirst = process.env.A3_DISCOVERY_SEARCH_FIRST === '1' || mobileFilters; assert.ok(!(trackingOnly && searchFirst), 'Search and tracker qualification are separate scopes.'); const widths = process.env.A3_PILOT ? (trackingOnly || searchFirst ? [320, 1440] : [320]) : visualMatrix || trackingOnly || searchFirst ? [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, searchFirst ? '../../docs/ui-modernisation/visual-correction/evidence/discovery-search-first' : trackingOnly ? '../../docs/ui-modernisation/visual-correction/evidence/discovery-track' : visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/discovery' : '../../docs/ui-modernisation/evidence/discovery/browser'); +const out = path.resolve(import.meta.dirname, mobileFilters ? '../../docs/ui-modernisation/visual-correction/evidence/discovery-mobile-filters' : searchFirst ? '../../docs/ui-modernisation/visual-correction/evidence/discovery-search-first' : 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: [], previewRequests: [], unavailableReads: [], blockedExternal: [] }; const browser = await chromium.launch(); @@ -78,7 +79,7 @@ try { 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 && ['results', 'saved'].includes(state)) { + if (!mobileFilters && 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)); @@ -88,6 +89,48 @@ try { await page.screenshot({ path: path.join(out, `${state}-${language}-${mode}-${width}.png`), fullPage: width >= 768 }); }; await page.goto('http://localhost:4338/discover'); + if (mobileFilters) { + const narrow = width <= 1000; + const results = page.getByRole('region', { name: t('jobDiscoveryResults'), exact: true }); + const role = page.getByRole('textbox', { name: t('jobDiscoveryRoleCompany'), exact: true }); + const trigger = page.getByRole('button', { name: t('jobDiscoveryFilters'), exact: true }); + const filters = page.getByRole('complementary', { name: t('jobDiscoveryFilters'), exact: true }); + const dialog = page.getByRole('dialog', { name: t('jobDiscoveryFilters'), exact: true }); + await expect(results).toBeVisible(); await role.fill('nurse'); await scan('initial'); + if (narrow) { + await trigger.press('Enter'); await expect(dialog).toBeVisible(); + await expect(dialog.getByRole('button', { name: t('close'), exact: true })).toBeFocused(); + await page.keyboard.press('Escape'); await expect(dialog).toBeHidden(); await expect(trigger).toBeFocused(); + await trigger.press('Enter'); + } + const municipality = filters.getByRole('textbox', { name: t('jobDiscoveryMunicipality'), exact: true }); + await municipality.fill('Oslo'); + await filters.getByRole('textbox', { name: t('jobDiscoverySavedName'), exact: true }).fill('Care roles draft'); + if (narrow) { + await page.keyboard.press('Escape'); await expect(dialog).toBeHidden(); await expect(trigger).toBeFocused(); + await trigger.press('Enter'); await expect(municipality).toHaveValue('Oslo'); + await expect(filters.getByRole('textbox', { name: t('jobDiscoverySavedName'), exact: true })).toHaveValue('Care roles draft'); + } + const source = filters.getByText(t('jobDiscoverySourceCoverage'), { exact: true }); + await source.press('Enter'); await expect(filters.getByText(t('jobDiscoverySourceHelp'), { exact: true })).toBeVisible(); + await scan('filters'); + await municipality.press('Enter'); + if (narrow) { await expect(dialog).toBeHidden(); await expect(results).toBeFocused(); } + await expect(results.getByRole('article', { name: jobs[0].title, exact: true })).toBeVisible(); + assert.equal(searches, 1); + await scan('results'); + if (narrow && width === 320) { + await trigger.press('Enter'); await expect(dialog).toBeVisible(); + await page.setViewportSize({ width: 1024, height: 1000 }); await expect(dialog).toHaveCount(0); await expect(role).toBeFocused(); + await expect(municipality).toHaveValue('Oslo'); + await page.setViewportSize({ width, height: 1000 }); await expect(trigger).toHaveAttribute('aria-expanded', 'false'); + await trigger.press('Enter'); await expect(municipality).toHaveValue('Oslo'); + await filters.getByRole('button', { name: t('jobDiscoveryJumpResults'), exact: true }).click(); + await expect(dialog).toBeHidden(); await expect(results).toBeFocused(); + } + report.journeys.push({ width, language, mode, mobileFilterDraftSubmitAndFocus: 'PASS', breakpoint: width === 320 ? 'PASS' : 'NOT RUN', providerWrites: 'NOT RUN' }); + await context.close(); continue; + } const filters = page.getByRole('complementary', { name: language === 'en' ? 'Search filters' : 'Søkefiltre' }); await filters.waitFor(); const results = page.getByRole('region', { name: language === 'en' ? 'Search results' : 'Søkeresultater', exact: true }); await results.waitFor(); const role = page.getByRole('textbox', { name: language === 'en' ? 'Role or company' : 'Rolle eller bedrift', exact: true });