/** * The section's job is to be honest about what it matched. These tests pin the * ways it could lie: rendering below the minimum, claiming a similar intake at * tier 3, 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, SimilarSchoolsSection, shouldRenderSimilar, } from '@/components/school/SimilarSchoolsSection'; import type { SimilarSchool } from '@/lib/types'; jest.mock('@/components/school/AddToCompareButton', () => ({ AddToCompareButton: ({ school }: { school: SimilarSchool }) => ( ), })); jest.mock('@/components/school/SimilarSchoolsCompareBar', () => ({ SimilarSchoolsCompareBar: ({ thisUrn }: { thisUrn: number }) => (
bar for {thisUrn}
), })); function school(overrides: Partial = {}): SimilarSchool { 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'], tier: 1, metric_value: 74, metric_key: 'rwm_expected_pct', metric_year: 202425, ...overrides, }; } function renderSection(similar: SimilarSchool[]) { return render( , ); } describe('render gates', () => { it.each([ ['undefined', undefined], ['null', null], ['empty', []], ['a single school', [school()]], ])('renders nothing for %s', (_label, value) => { expect(shouldRenderSimilar(value as SimilarSchool[] | null | undefined)).toBe(false); }); it('renders for two or more schools', () => { expect(shouldRenderSimilar([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('the claim the lede makes', () => { it('claims a similar intake when every card is tier 1 or 2', () => { renderSection([school({ tier: 1 }), school({ urn: 100003, tier: 2 })]); expect(screen.getByText(/with a similar intake/i)).toBeInTheDocument(); }); it('drops the claim when any card is tier 3', () => { renderSection([school({ tier: 1 }), school({ urn: 100003, tier: 3, shared: ['Primary school'] })]); expect(screen.queryByText(/with a similar intake/i)).not.toBeInTheDocument(); }); }); 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(); }); });