diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index 2d1f881..1586b4f 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -272,6 +272,17 @@ test('searching by postcode returns nearby schools', async ({ page }) => { await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 }); }); +test('the phase filter switches straight from secondary to primary', async ({ page }) => { + // The phase options once came from the result set, which the phase filter + // had already narrowed — so with secondary chosen, primary was not offered. + await page.goto('/?search=school&phase=secondary'); + const phase = page.getByRole('combobox', { name: 'Phase' }); + await expect(phase).toHaveValue('secondary', { timeout: 15_000 }); + await phase.selectOption('primary'); + await expect(page).toHaveURL(/[?&]phase=primary(&|$)/); + await expect(phase).toHaveValue('primary'); +}); + test('a report-card school shows a Report Card badge in search results, not its old grade', async ({ page }) => { // List/map badges keyed off ofsted_grade (the carried-forward legacy grade) // and never reached the report-card branch, so report-card schools were diff --git a/nextjs-app/__tests__/components/FilterBarPhase.test.tsx b/nextjs-app/__tests__/components/FilterBarPhase.test.tsx new file mode 100644 index 0000000..ddbed2e --- /dev/null +++ b/nextjs-app/__tests__/components/FilterBarPhase.test.tsx @@ -0,0 +1,40 @@ +import { render, screen, within } from '@testing-library/react'; +import { FilterBar } from '@/components/FilterBar'; + +let searchParams = new URLSearchParams(); +jest.mock('next/navigation', () => ({ + useRouter: () => ({ push: jest.fn(), replace: jest.fn(), prefetch: jest.fn() }), + usePathname: () => '/', + useSearchParams: () => searchParams, +})); + +const FILTERS = { + local_authorities: [], school_types: [], years: [], + phases: ['Primary', 'Secondary', 'All-through'], + genders: [], admissions_policies: [], +}; + +/** + * The phase options must not come from the result set. The backend scopes its + * result filters to the schools it returns, and it applies the phase filter + * first — so with "secondary" chosen the scoped list holds only secondary-ish + * phases, and switching to primary meant going back to "Any phase" first. + */ +describe('FilterBar phase options', () => { + it('offers every phase while a phase filter narrows the results', () => { + searchParams = new URLSearchParams('search=hampton&phase=secondary'); + render( + , + ); + const phase = screen.getByRole('combobox', { name: 'Phase' }); + expect(within(phase).getByRole('option', { name: 'Primary' })).toBeInTheDocument(); + expect(phase).toHaveValue('secondary'); + }); +}); diff --git a/nextjs-app/components/FilterBar.tsx b/nextjs-app/components/FilterBar.tsx index 20f7dbf..c08211d 100644 --- a/nextjs-app/components/FilterBar.tsx +++ b/nextjs-app/components/FilterBar.tsx @@ -295,7 +295,10 @@ export function FilterBar({ const laOptions = resultFilters?.local_authorities ?? filters.local_authorities; const typeOptions = resultFilters?.school_types ?? filters.school_types; - const phaseOptions = resultFilters?.phases ?? filters.phases ?? []; + // Phase is the exception: always the full list. The result set has already + // been narrowed by the phase filter, so scoping to it would leave only the + // chosen phase on offer and switching phase would need "Any phase" first. + const phaseOptions = filters.phases ?? []; const genderOptions = resultFilters?.genders ?? filters.genders ?? []; const admissionsPolicyOptions = resultFilters?.admissions_policies ?? filters.admissions_policies ?? [];