/** * The section's job is to be honest about what it is showing. These tests pin * the ways it could mislead: rendering below the minimum, claiming a likeness * it does not rank on, showing a missing figure as a number, or hiding a card * behind an arrow where a crawler cannot reach it. */ import { render, screen } from '@testing-library/react'; import { nearbyNoun, NearbySchoolsSection, shouldRenderNearby, } from '@/components/school/NearbySchoolsSection'; import type { NearbySchool } from '@/lib/types'; jest.mock('@/components/school/AddToCompareButton', () => ({ AddToCompareButton: ({ school }: { school: NearbySchool }) => ( ), })); jest.mock('@/components/school/NearbySchoolsCompareBar', () => ({ NearbySchoolsCompareBar: ({ thisUrn }: { thisUrn: number }) => (
bar for {thisUrn}
), })); function school(overrides: Partial = {}): NearbySchool { return { urn: 100002, school_name: 'Willow Lane Primary School', distance_miles: 0.6, school_type: 'Community school', age_range: '4-11', shared: ['Mixed', 'No religious character'], metric_value: 74, metric_key: 'rwm_expected_pct', metric_year: 202425, ...overrides, }; } function renderSection(nearby: NearbySchool[]) { return render( , ); } describe('render gates', () => { it.each([ ['undefined', undefined], ['null', null], ['empty', []], ['a single school', [school()]], ])('renders nothing for %s', (_label, value) => { expect(shouldRenderNearby(value as NearbySchool[] | null | undefined)).toBe(false); }); it('renders for two or more schools', () => { expect(shouldRenderNearby([school(), school({ urn: 100003 })])).toBe(true); }); it('returns null rather than an empty shell below the minimum', () => { const { container } = renderSection([school()]); expect(container).toBeEmptyDOMElement(); }); }); describe('what the section claims', () => { it('never claims a similar intake, because it does not rank on one', () => { renderSection([school(), school({ urn: 100003, shared: [] })]); expect(screen.queryByText(/similar intake/i)).not.toBeInTheDocument(); }); it('is headed "Other schools nearby", not "similar"', () => { renderSection([school(), school({ urn: 100003 })]); expect(screen.getByRole('heading', { name: 'Other schools nearby' })).toBeInTheDocument(); }); it('shows chips for what is shared', () => { renderSection([school({ shared: ['Mixed', 'Roman Catholic'] }), school({ urn: 100003 })]); expect(screen.getAllByText('Roman Catholic').length).toBe(1); }); it('shows no chips at all when nothing is shared, rather than inventing one', () => { const { container } = render( , ); // The card still carries its distance, name, type and figure — just no // claim of likeness. expect(container.querySelectorAll('li ul').length).toBe(0); expect(screen.getAllByText(/miles away/).length).toBe(2); }); }); describe('what the lede calls the set', () => { it.each([ ['Primary', 'primary schools'], ['Middle deemed primary', 'primary schools'], ['Secondary', 'secondary schools'], ['Middle deemed secondary', 'secondary schools'], ['All-through', 'all-through schools'], // GIAS phase 6. Its candidates span the whole secondary group, so no // single noun fits and it takes the honest general one. ['16 plus', 'schools and colleges'], ['', 'schools'], [null, 'schools'], ])('calls a %s school\'s neighbours "%s"', (phase, expected) => { expect(nearbyNoun(phase)).toBe(expected); }); it('never calls a sixth form college\'s neighbours primary schools', () => { render( , ); expect(screen.getByText(/Other schools and colleges near Barnet Sixth Form College/)).toBeInTheDocument(); expect(screen.queryByText(/primary schools/)).not.toBeInTheDocument(); }); }); describe('cards', () => { it('links each school to its canonical slug', () => { renderSection([school(), school({ urn: 100003, school_name: 'Oakfield Primary School' })]); const link = screen.getByRole('link', { name: /Willow Lane Primary School/ }); expect(link).toHaveAttribute('href', '/school/100002-willow-lane-primary-school'); }); it('shows the distance and the shared characteristics', () => { renderSection([school(), school({ urn: 100003 })]); expect(screen.getAllByText('0.6 miles away').length).toBeGreaterThan(0); expect(screen.getAllByText('Mixed').length).toBeGreaterThan(0); }); it('renders a missing figure as "Not published", never as a number', () => { renderSection([school({ metric_value: null }), school({ urn: 100003 })]); expect(screen.getByText('Not published')).toBeInTheDocument(); }); it('anchors each figure against this school', () => { renderSection([school(), school({ urn: 100003 })]); expect(screen.getAllByText('72% at this school').length).toBe(2); }); it('offers the compare bar once, for this school', () => { renderSection([school(), school({ urn: 100003 })]); expect(screen.getByTestId('compare-bar')).toHaveTextContent('bar for 100001'); }); it('keeps every card in the DOM, including the ones scrolled out of view', () => { const six = Array.from({ length: 6 }, (_, n) => school({ urn: 100002 + n, school_name: `Peer ${n} School` }), ); renderSection(six); expect(screen.getAllByRole('link', { name: /Peer \d School/ })).toHaveLength(6); }); it('offers no arrows when three cards fit the row', () => { renderSection([school(), school({ urn: 100003 }), school({ urn: 100004 })]); expect(screen.queryByRole('button', { name: /More schools/ })).not.toBeInTheDocument(); }); it('offers arrows once there is a fourth school', () => { renderSection(Array.from({ length: 4 }, (_, n) => school({ urn: 100002 + n }))); expect(screen.getByRole('button', { name: /More schools/ })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Previous schools/ })).toBeInTheDocument(); }); it('says distances are straight-line, and offers no method panel', () => { const { container } = renderSection([school(), school({ urn: 100003 })]); expect(screen.getByText(/straight-line from this school/i)).toBeInTheDocument(); expect(container.querySelector('details')).toBeNull(); }); });