diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index 9c18dc8..b3fdbc0 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -1252,7 +1252,11 @@ test('a published cut-off distance is shown with the year it belongs to', async await expect(label).toContainText(`September ${found!.distance.year}`); // And the figure itself, in the unit councils publish in. - await expect(page.getByText(/\d+(\.\d+)? (miles|m)\b/).first()).toBeVisible(); + // No trailing \b: the tile's support figure is an adjacent text node, so + // the element reads "0.88 miles 1.4 km" and a word boundary after "miles" + // is not guaranteed. The leading shape is what matters โ a decimal figure + // in miles, never a metric one. + await expect(page.getByText(/\d+\.\d+ miles/).first()).toBeVisible(); }); test('a cut-off distance is never shown without saying it is not a catchment', async ({ page }) => { @@ -1340,7 +1344,27 @@ test('a school page shows no year-by-year cut-off record', async ({ page }) => { // history back without the API. await expect(page.getByText(/Last distance offered, by year/)).toHaveCount(0); await expect(page.getByText(/too few to read as a trend/)).toHaveCount(0); - await expect(page.getByRole('button', { name: 'Distance' })).toHaveCount(0); + + /* + * No "Distance" tab in the admissions segmented control. + * + * Scoped to the control, and exact, because getByRole matches accessible + * names by case-insensitive SUBSTRING: an unscoped { name: 'Distance' } + * matched "Show this distance on a map" โ the map toggle added by this same + * feature โ and failed a page that was entirely correct. Naming the group + * this assertion is about also means unrelated copy elsewhere on the page + * can never break it again. + */ + const viewToggle = page.getByRole('group', { name: 'Admissions view' }); + const tabs = await viewToggle.getByRole('button').allTextContents(); + + // Read the tabs positively rather than asserting an absence against a + // locator that might resolve to nothing: if the group selector ever stops + // matching, an absence check passes for the wrong reason, which is how the + // bug this test is guarding would slip back in unnoticed. + expect(tabs, 'admissions view toggle did not resolve').toContain('This year'); + expect(tabs.map((s) => s.trim()), `admissions tabs: ${tabs.join(', ')}`) + .not.toContain('Distance'); }); test('the postcode check answers for the published year, and names it', async ({ page }) => { @@ -1354,7 +1378,7 @@ test('the postcode check answers for the published year, and names it', async ({ await expect(section).toBeVisible({ timeout: 15_000 }); await page.getByLabel('Your postcode').fill('SW1A 1AA'); - await page.getByRole('button', { name: 'Check' }).click(); + await page.getByRole('button', { name: 'Check', exact: true }).click(); const result = page.getByRole('status'); const error = page.getByRole('alert'); diff --git a/nextjs-app/__tests__/components/lastDistanceOffered.test.tsx b/nextjs-app/__tests__/components/lastDistanceOffered.test.tsx index 0c30e16..9f16df5 100644 --- a/nextjs-app/__tests__/components/lastDistanceOffered.test.tsx +++ b/nextjs-app/__tests__/components/lastDistanceOffered.test.tsx @@ -30,6 +30,17 @@ describe('primary detail page', () => { expect(screen.getByText(/Last distance offered/)).toHaveTextContent('September 2024'); }); + it('keeps the figure and its metric support readable as two numbers', () => { + // They are flex children with a CSS gap and nothing between them in the + // text layer, which read as "0.48 miles770 m" to a screen reader and to any + // text matcher. Cheap to lose again, so pinned. + renderSchoolDetail({ ...primaryFixture, admissionDistance: cutoff({ distance_m: 772.49 }) }); + + const tile = document.querySelector('[class*="admissionsTileDistance"]')!; + expect(tile.textContent).toMatch(/0\.48 miles\s+770 m/); + expect(tile.textContent).not.toMatch(/miles\d/); + }); + it('never shows the figure without saying it is not a catchment', () => { renderSchoolDetail({ ...primaryFixture, admissionDistance: cutoff() }); diff --git a/nextjs-app/components/school/AdmissionsSection.tsx b/nextjs-app/components/school/AdmissionsSection.tsx index 6b423b7..fcac056 100644 --- a/nextjs-app/components/school/AdmissionsSection.tsx +++ b/nextjs-app/components/school/AdmissionsSection.tsx @@ -70,10 +70,16 @@ export function AdmissionsSection({ {/* Spans both columns rather than taking a half-width cell. This is the figure parents come to the page for, and at tile width the two-line "0.31 miles / September 2025" pairing wraps badly. */} + {/* The {' '} between the figure and its metric support is not decoration. + The two are flex children, so the gap is drawn by CSS and the text layer + had nothing between them: textContent read "0.88 miles1.4 km", which is + what a screen reader announces and what any text matcher sees. Whitespace + text nodes are not rendered as flex items, so this changes the reading + without changing the layout. */} const distanceTile = cutoff && (