2026-08-15 22:48:30 +01:00
|
|
|
/**
|
|
|
|
|
* Last distance offered, rendered on both detail templates.
|
|
|
|
|
*
|
|
|
|
|
* The figure is the one number on these pages that a parent may act on — it is
|
|
|
|
|
* easy to read as "we live inside the catchment, we will get a place". These
|
|
|
|
|
* tests pin the things that stop it being read that way: the year is always
|
|
|
|
|
* present, the caveat is always present, and the blanket "not available"
|
|
|
|
|
* sentence appears only when it is actually true.
|
|
|
|
|
*/
|
|
|
|
|
|
2026-08-16 13:42:06 +01:00
|
|
|
import { screen, fireEvent } from '@testing-library/react';
|
2026-08-15 22:48:30 +01:00
|
|
|
import { renderSchoolDetail, renderSecondarySchoolDetail } from '../support/renderSchoolDetail';
|
|
|
|
|
import { primaryFixture, secondaryFixture } from '../support/schoolFixtures';
|
|
|
|
|
import type { SchoolAdmissionDistance } from '@/lib/types';
|
|
|
|
|
|
|
|
|
|
const cutoff = (over: Partial<SchoolAdmissionDistance> = {}): SchoolAdmissionDistance => ({
|
|
|
|
|
year: 2025,
|
|
|
|
|
distance_m: 500,
|
|
|
|
|
route_count: 1,
|
|
|
|
|
la_name: 'Camden',
|
|
|
|
|
distance_unit_raw: 'miles',
|
|
|
|
|
...over,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('primary detail page', () => {
|
|
|
|
|
it('shows the figure with the year it belongs to', () => {
|
|
|
|
|
renderSchoolDetail({ ...primaryFixture, admissionDistance: cutoff({ distance_m: 772.49, year: 2024 }) });
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText('0.48 miles')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByText(/Last distance offered/)).toHaveTextContent('September 2024');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('never shows the figure without saying it is not a catchment', () => {
|
|
|
|
|
renderSchoolDetail({ ...primaryFixture, admissionDistance: cutoff() });
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/not a fixed catchment/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByText(/moves every year/)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('flags that a banded school\'s figure is the widest of several routes', () => {
|
|
|
|
|
renderSchoolDetail({ ...primaryFixture, admissionDistance: cutoff({ route_count: 4 }) });
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/4 admission routes/)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('renders nothing distance-related when the LA publishes none', () => {
|
|
|
|
|
renderSchoolDetail({ ...primaryFixture, admissionDistance: null });
|
|
|
|
|
|
|
|
|
|
expect(screen.queryByText(/Last distance offered/)).not.toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText(/not a fixed catchment/)).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('carries the figure even with no EES admissions row', () => {
|
|
|
|
|
// The two sources are independent; this school has a cut-off and no
|
|
|
|
|
// admissions figures. Before this feature the section did not render at all.
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissions: null,
|
|
|
|
|
admissionsHistory: [],
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 1421.05 }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText('0.88 miles')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('secondary detail page', () => {
|
|
|
|
|
it('shows the figure with the year it belongs to', () => {
|
|
|
|
|
renderSecondarySchoolDetail({ ...secondaryFixture, admissionDistance: cutoff({ distance_m: 3472.96 }) });
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText('2.16 miles')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByText(/Last distance offered/)).toHaveTextContent('September 2025');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('drops the blanket "not available" line once a distance exists', () => {
|
|
|
|
|
renderSecondarySchoolDetail({ ...secondaryFixture, admissionDistance: cutoff() });
|
|
|
|
|
|
|
|
|
|
expect(screen.queryByText(/has not published a cut-off distance/)).not.toBeInTheDocument();
|
|
|
|
|
expect(screen.getByText(/not a fixed catchment/)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('names the authority that would hold the data when there is none', () => {
|
|
|
|
|
// The old copy asserted "Historical distance cut-off data is not available
|
|
|
|
|
// for this school" on every secondary page, including the ones whose
|
|
|
|
|
// council does publish it.
|
|
|
|
|
renderSecondarySchoolDetail({ ...secondaryFixture, admissionDistance: null });
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/has not published a cut-off distance/)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-08-16 13:42:06 +01:00
|
|
|
|
|
|
|
|
// ── The Distance view ──────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
const history = (pts: [number, number][]): SchoolAdmissionDistance[] =>
|
|
|
|
|
pts.map(([year, distance_m]) => ({
|
|
|
|
|
year, distance_m, route_count: 1, la_name: 'Camden', distance_unit_raw: 'miles',
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
describe('primary Distance view', () => {
|
|
|
|
|
it('appears once there are two or more published years', () => {
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 700, year: 2024 }),
|
|
|
|
|
admissionDistanceHistory: history([[2021, 1000], [2022, 900], [2023, 800], [2024, 700]]),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByRole('button', { name: 'Distance' })).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('stays hidden on a single published year', () => {
|
|
|
|
|
// One point is a fact, not a history; a chart of it invites a trend reading
|
|
|
|
|
// that is not there.
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 700, year: 2024 }),
|
|
|
|
|
admissionDistanceHistory: history([[2024, 700]]),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.queryByRole('button', { name: 'Distance' })).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists every year in the span, including the ones with no figure', () => {
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 700, year: 2024 }),
|
|
|
|
|
admissionDistanceHistory: history([[2021, 1000], [2024, 700]]),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// The view is server-rendered but starts hidden behind its tab, and role
|
|
|
|
|
// queries skip hidden subtrees — so open it the way a reader would.
|
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Distance' }));
|
|
|
|
|
|
|
|
|
|
// 2022 and 2023 were never published but must still appear as rows, or the
|
|
|
|
|
// gap in the chart has nothing explaining it.
|
|
|
|
|
for (const year of ['2021', '2022', '2023', '2024']) {
|
|
|
|
|
expect(screen.getByRole('rowheader', { name: year })).toBeInTheDocument();
|
|
|
|
|
}
|
|
|
|
|
expect(screen.getAllByText('Not published').length).toBe(2);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('never claims everyone was offered a place', () => {
|
|
|
|
|
// fact_admissions.oversubscribed is about first preferences only, so the
|
|
|
|
|
// stronger claim is not supported by the data behind it.
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 700, year: 2024 }),
|
|
|
|
|
admissionDistanceHistory: history([[2021, 1000], [2024, 700]]),
|
|
|
|
|
admissionsHistory: [
|
|
|
|
|
...primaryFixture.admissionsHistory,
|
|
|
|
|
{ year: 2022, oversubscribed: false, places_offered: 60 },
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/Places available on first preferences/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText(/every applicant/i)).not.toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText(/all offered/i)).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('withholds a trend reading while the record is thin', () => {
|
|
|
|
|
renderSchoolDetail({
|
|
|
|
|
...primaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 700, year: 2024 }),
|
|
|
|
|
admissionDistanceHistory: history([[2023, 800], [2024, 700]]),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/too few to read as a trend/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText(/Across \d+ published years/)).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('secondary Distance detail', () => {
|
|
|
|
|
it('renders inline, with no extra tab, once there are two published years', () => {
|
|
|
|
|
renderSecondarySchoolDetail({
|
|
|
|
|
...secondaryFixture,
|
|
|
|
|
admissionDistance: cutoff({ distance_m: 3472.96 }),
|
|
|
|
|
admissionDistanceHistory: history([[2023, 3800], [2024, 3472.96]]),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/Last distance offered, by year/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByRole('rowheader', { name: '2023' })).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('explains a selective school by how it admits rather than as missing data', () => {
|
|
|
|
|
renderSecondarySchoolDetail({
|
|
|
|
|
...secondaryFixture,
|
|
|
|
|
schoolInfo: { ...secondaryFixture.schoolInfo, admissions_policy: 'Selective' },
|
|
|
|
|
admissionDistance: null,
|
|
|
|
|
admissionDistanceHistory: [],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/ranked by the entrance test/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText(/has not published a cut-off distance/)).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('reads a consistently undersubscribed school as good news', () => {
|
|
|
|
|
renderSecondarySchoolDetail({
|
|
|
|
|
...secondaryFixture,
|
|
|
|
|
admissionDistance: null,
|
|
|
|
|
admissionDistanceHistory: [],
|
|
|
|
|
admissionsHistory: [
|
|
|
|
|
{ year: 2022, oversubscribed: false },
|
|
|
|
|
{ year: 2023, oversubscribed: false },
|
|
|
|
|
{ year: 2024, oversubscribed: false },
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/has not needed a distance cut-off/)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|