import { render, screen } from '@testing-library/react'; import { DestinationsSection } from '@/components/school/DestinationsSection'; import type { DestinationPhase } from '@/lib/types'; import type { DestinationCategory, DestinationStatus } from '@/lib/destinations'; const cell = ( category: DestinationCategory, pupils: number | null, status: DestinationStatus = 'published', ) => ({ category, pupils, percentage: pupils === null ? null : (pupils / 180) * 100, status, }); const ALL_PUBLISHED = [ cell('school_sixth_form', 75), cell('sixth_form_college', 21), cell('further_education', 55), cell('other_education', 6), cell('apprenticeship', 8), cell('employment', 6), cell('not_sustained', 5), cell('not_captured', 4), ]; const fullPhase: DestinationPhase = { cohort_year: '2022/23', groups: { all: { cohort: 180, categories: ALL_PUBLISHED } }, }; const suppressedPhase: DestinationPhase = { cohort_year: '2022/23', groups: { all: { cohort: 180, categories: [ cell('school_sixth_form', 75), cell('sixth_form_college', null, 'suppressed'), cell('further_education', 55), cell('other_education', 6), cell('apprenticeship', 8), cell('employment', 6), cell('not_sustained', 5), cell('not_captured', 4), ], }, }, }; describe('DestinationsSection', () => { it('dates its own cohort so it is not read as stale next to the GCSE section', () => { render(); expect(screen.getByText(/2022\/23/)).toBeInTheDocument(); }); it('renders one bar segment per published category', () => { const { container } = render(); expect(container.querySelectorAll('[data-destination-segment]')).toHaveLength(8); }); it('renders NO bar at all when a category is withheld', () => { const { container } = render(); // R1: a bar with a gap in it publishes the withheld figure by its width. expect(container.querySelectorAll('[data-destination-segment]')).toHaveLength(0); expect(screen.getAllByText(/withheld/i).length).toBeGreaterThan(0); }); it('never states the remainder for a partially suppressed group', () => { const { container } = render(); // 180 cohort - 159 published = 21, the withheld figure. It must appear nowhere. expect(container.textContent).not.toMatch(/\b21\b/); }); it('shows a card value for a group whose components are all published', () => { render(); // academic route = 75 + 21 = 96 of 180 = 53% expect(screen.getByText('53%')).toBeInTheDocument(); }); it('refuses a card value when one of its components is withheld', () => { render(); // academic route needs sixth_form_college, which is suppressed. expect(screen.getByText(/not published/i)).toBeInTheDocument(); expect(screen.queryByText('53%')).not.toBeInTheDocument(); }); it('never claims a pupil stayed at this school', () => { const { container } = render(); // The published file reports destination TYPE, never destination institution. expect(container.textContent).not.toMatch(/stayed on (here|at this school)/i); }); it('renders nothing when no group carries categories', () => { const empty: DestinationPhase = { cohort_year: '2022/23', groups: {} }; const { container } = render(); expect(container.firstChild).toBeNull(); }); }); describe('the detail table keeps the three statuses apart', () => { // 'suppressed' and 'not_applicable' are different claims, and the mart, the // SQLAlchemy model and the serialiser all preserve the difference. The table // used to key its Share column off `percentage === null`, which is true for // both, so a category that simply does not apply was labelled "withheld" — // while the Pupils column beside it rendered blank. const mixedPhase: DestinationPhase = { cohort_year: '2022/23', groups: { all: { cohort: 180, categories: [ cell('school_sixth_form', 75), cell('sixth_form_college', null, 'suppressed'), cell('further_education', null, 'suppressed'), cell('apprenticeship', null, 'not_applicable'), ], }, }, }; const rowFor = (container: HTMLElement, category: string) => Array.from(container.querySelectorAll('tbody tr')) .find(tr => tr.textContent?.includes(category)); it('never labels a not-applicable category as withheld', () => { const { container } = render(); const row = rowFor(container, 'Apprenticeship'); expect(row).toBeTruthy(); expect(row!.textContent).not.toMatch(/withheld/i); }); it('labels a genuinely suppressed category as withheld in both columns', () => { const { container } = render(); const row = rowFor(container, 'Sixth-form college'); expect(row).toBeTruthy(); expect(row!.querySelectorAll('td')).toHaveLength(2); Array.from(row!.querySelectorAll('td')).forEach(td => expect(td.textContent).toMatch(/withheld/i)); }); it('the two columns of a row never disagree about what the row is', () => { const { container } = render(); Array.from(container.querySelectorAll('tbody tr')).forEach(tr => { const cells = Array.from(tr.querySelectorAll('td')) .map(td => /withheld/i.test(td.textContent ?? '')); expect(new Set(cells).size).toBe(1); }); }); it('shows a published category its real figures', () => { const { container } = render(); const row = rowFor(container, 'State-funded school sixth form'); expect(row!.textContent).toMatch(/75/); expect(row!.textContent).toMatch(/42%/); }); });