2026-08-01 21:14:35 +01:00
|
|
|
import {
|
|
|
|
|
computeSchoolFlags, buildNavItems,
|
|
|
|
|
computeSecondaryFlags, buildSecondaryNavItems,
|
|
|
|
|
} from '@/lib/schoolSections';
|
|
|
|
|
import {
|
|
|
|
|
primaryFixture, secondaryFixture, allThroughFixture, specialFixture,
|
|
|
|
|
} from '../support/schoolFixtures';
|
|
|
|
|
|
|
|
|
|
describe('computeSchoolFlags', () => {
|
|
|
|
|
it('treats an all-through school as both primary-capable and secondary', () => {
|
|
|
|
|
const f = computeSchoolFlags(allThroughFixture);
|
|
|
|
|
expect(f.isAllThrough).toBe(true);
|
|
|
|
|
expect(f.isSecondary).toBe(true);
|
|
|
|
|
expect(f.hasKS2Results).toBe(true);
|
|
|
|
|
expect(f.hasKS4Results).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('classifies an ordinary primary', () => {
|
|
|
|
|
const f = computeSchoolFlags(primaryFixture);
|
|
|
|
|
expect(f.isPrimary).toBe(true);
|
|
|
|
|
expect(f.isSecondary).toBe(false);
|
|
|
|
|
expect(f.hasKS2Results).toBe(true);
|
|
|
|
|
expect(f.hasKS4Results).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('classifies an ordinary secondary', () => {
|
|
|
|
|
const f = computeSchoolFlags(secondaryFixture);
|
|
|
|
|
expect(f.isSecondary).toBe(true);
|
|
|
|
|
expect(f.isAllThrough).toBe(false);
|
|
|
|
|
expect(f.hasKS4Results).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('suppresses the KS2 comparison for a special school', () => {
|
|
|
|
|
const f = computeSchoolFlags(specialFixture);
|
|
|
|
|
expect(f.isSpecial).toBe(true);
|
|
|
|
|
expect(f.suppressKs2Comparison).toBe(true);
|
|
|
|
|
expect(f.suppressKs4Comparison).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('treats an all-zero KS2 row as a placeholder', () => {
|
|
|
|
|
const f = computeSchoolFlags(specialFixture);
|
|
|
|
|
expect(f.ks2Placeholder).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('does not suppress comparison for an ordinary primary', () => {
|
|
|
|
|
const f = computeSchoolFlags(primaryFixture);
|
|
|
|
|
expect(f.isSpecial).toBe(false);
|
|
|
|
|
expect(f.suppressKs2Comparison).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('detects a gender split only when census counts are present', () => {
|
|
|
|
|
expect(computeSchoolFlags(primaryFixture).hasGenderSplit).toBe(true);
|
|
|
|
|
expect(
|
|
|
|
|
computeSchoolFlags({ ...primaryFixture, census: null }).hasGenderSplit,
|
|
|
|
|
).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('buildNavItems', () => {
|
|
|
|
|
it('omits sections with no data', () => {
|
|
|
|
|
const flags = computeSchoolFlags(specialFixture);
|
|
|
|
|
const ids = buildNavItems(flags, {
|
2026-08-15 22:48:30 +01:00
|
|
|
ofsted: null, admissions: null, admissionDistance: null, yearlyDataLength: 1,
|
2026-08-01 21:14:35 +01:00
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).not.toContain('ofsted');
|
|
|
|
|
expect(ids).not.toContain('admissions');
|
|
|
|
|
expect(ids).toContain('history');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('labels the results section by phase', () => {
|
|
|
|
|
const label = (fixture: any) => {
|
|
|
|
|
const flags = computeSchoolFlags(fixture);
|
|
|
|
|
return buildNavItems(flags, {
|
|
|
|
|
ofsted: fixture.ofsted,
|
|
|
|
|
admissions: fixture.admissions,
|
2026-08-15 22:48:30 +01:00
|
|
|
admissionDistance: null,
|
2026-08-01 21:14:35 +01:00
|
|
|
yearlyDataLength: fixture.yearlyData.length,
|
|
|
|
|
}).find((n) => n.id === 'results')?.label;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
expect(label(primaryFixture)).toBe('SATs');
|
|
|
|
|
expect(label(secondaryFixture)).toBe('GCSEs');
|
|
|
|
|
expect(label(allThroughFixture)).toBe('Results');
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-15 22:48:30 +01:00
|
|
|
it('opens the admissions entry for a cut-off distance with no EES admissions', () => {
|
|
|
|
|
// 3% of the schools that render have one source and not the other. The nav
|
|
|
|
|
// condition and the section's render condition have to agree, or the sticky
|
|
|
|
|
// nav links to an anchor that was never rendered.
|
|
|
|
|
const flags = computeSchoolFlags(specialFixture);
|
|
|
|
|
const ids = buildNavItems(flags, {
|
|
|
|
|
ofsted: null,
|
|
|
|
|
admissions: null,
|
|
|
|
|
admissionDistance: { year: 2025, distance_m: 500, route_count: 1, la_name: 'Camden', distance_unit_raw: 'miles' },
|
|
|
|
|
yearlyDataLength: 1,
|
|
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).toContain('admissions');
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-01 21:14:35 +01:00
|
|
|
it('keeps the engagement-led ordering', () => {
|
|
|
|
|
const flags = computeSchoolFlags(primaryFixture);
|
|
|
|
|
const ids = buildNavItems(flags, {
|
|
|
|
|
ofsted: primaryFixture.ofsted,
|
|
|
|
|
admissions: primaryFixture.admissions,
|
2026-08-15 22:48:30 +01:00
|
|
|
admissionDistance: null,
|
2026-08-01 21:14:35 +01:00
|
|
|
yearlyDataLength: primaryFixture.yearlyData.length,
|
|
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).toEqual([
|
|
|
|
|
'ofsted', 'results', 'admissions', 'inclusion',
|
|
|
|
|
'history', 'school-life', 'local-area', 'finances',
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('computeSecondaryFlags', () => {
|
|
|
|
|
it('reads results and sixth form from the secondary fixture', () => {
|
|
|
|
|
const f = computeSecondaryFlags(secondaryFixture);
|
|
|
|
|
expect(f.hasResults).toBe(true);
|
|
|
|
|
expect(f.hasSixthForm).toBe(false);
|
|
|
|
|
expect(f.hasWellbeing).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('reports sixth form when GIAS flags it', () => {
|
|
|
|
|
expect(computeSecondaryFlags(allThroughFixture).hasSixthForm).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('suppresses the comparison for a special school', () => {
|
|
|
|
|
const f = computeSecondaryFlags(specialFixture);
|
|
|
|
|
expect(f.isSpecial).toBe(true);
|
|
|
|
|
expect(f.suppressComparison).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('does not flag Progress 8 as suspended for pre-2024/25 cohorts', () => {
|
|
|
|
|
expect(computeSecondaryFlags(secondaryFixture).p8Suspended).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('buildSecondaryNavItems', () => {
|
|
|
|
|
it('uses the secondary section ids', () => {
|
|
|
|
|
const flags = computeSecondaryFlags(secondaryFixture);
|
|
|
|
|
const ids = buildSecondaryNavItems(flags, {
|
|
|
|
|
ofsted: secondaryFixture.ofsted,
|
|
|
|
|
admissions: secondaryFixture.admissions,
|
2026-08-15 22:48:30 +01:00
|
|
|
admissionDistance: null,
|
2026-08-01 21:14:35 +01:00
|
|
|
yearlyDataLength: secondaryFixture.yearlyData.length,
|
|
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).toEqual(['ofsted', 'gcse', 'admissions', 'history', 'wellbeing', 'finances']);
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-15 22:48:30 +01:00
|
|
|
it('opens the admissions entry for a cut-off distance alone', () => {
|
|
|
|
|
const flags = computeSecondaryFlags(secondaryFixture);
|
|
|
|
|
const ids = buildSecondaryNavItems(flags, {
|
|
|
|
|
ofsted: null,
|
|
|
|
|
admissions: null,
|
|
|
|
|
admissionDistance: { year: 2025, distance_m: 2400, route_count: 4, la_name: 'Islington', distance_unit_raw: 'miles' },
|
|
|
|
|
yearlyDataLength: 1,
|
|
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).toContain('admissions');
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-01 21:14:35 +01:00
|
|
|
it('gates History on more than one year, unlike the primary page', () => {
|
|
|
|
|
const flags = computeSecondaryFlags(secondaryFixture);
|
|
|
|
|
const ids = buildSecondaryNavItems(flags, {
|
2026-08-15 22:48:30 +01:00
|
|
|
ofsted: null, admissions: null, admissionDistance: null, yearlyDataLength: 1,
|
2026-08-01 21:14:35 +01:00
|
|
|
}).map((n) => n.id);
|
|
|
|
|
|
|
|
|
|
expect(ids).not.toContain('history');
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-09-21 22:42:59 +01:00
|
|
|
|
2026-09-22 11:58:36 +01:00
|
|
|
describe('the nearby-schools nav item', () => {
|
2026-09-21 22:42:59 +01:00
|
|
|
const navInput = {
|
|
|
|
|
ofsted: null, admissions: null, admissionDistance: null,
|
|
|
|
|
hasLocation: true, yearlyDataLength: 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
it('appears on both templates when the section renders', () => {
|
|
|
|
|
const primary = computeSchoolFlags(primaryFixture);
|
|
|
|
|
const secondary = computeSecondaryFlags(secondaryFixture);
|
2026-09-22 11:58:36 +01:00
|
|
|
const input = { ...navInput, hasNearbySchools: true };
|
2026-09-21 22:42:59 +01:00
|
|
|
|
2026-09-22 11:58:36 +01:00
|
|
|
expect(buildNavItems(primary, input).map((i) => i.id)).toContain('nearby');
|
|
|
|
|
expect(buildSecondaryNavItems(secondary, input).map((i) => i.id)).toContain('nearby');
|
2026-09-21 22:42:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('is absent when the section does not render', () => {
|
|
|
|
|
const primary = computeSchoolFlags(primaryFixture);
|
|
|
|
|
const secondary = computeSecondaryFlags(secondaryFixture);
|
2026-09-22 11:58:36 +01:00
|
|
|
const input = { ...navInput, hasNearbySchools: false };
|
2026-09-21 22:42:59 +01:00
|
|
|
|
2026-09-22 11:58:36 +01:00
|
|
|
expect(buildNavItems(primary, input).map((i) => i.id)).not.toContain('nearby');
|
|
|
|
|
expect(buildSecondaryNavItems(secondary, input).map((i) => i.id)).not.toContain('nearby');
|
2026-09-21 22:42:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('is absent when nothing says either way', () => {
|
|
|
|
|
const primary = computeSchoolFlags(primaryFixture);
|
2026-09-22 11:58:36 +01:00
|
|
|
expect(buildNavItems(primary, navInput).map((i) => i.id)).not.toContain('nearby');
|
2026-09-21 22:42:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('comes last, because the section renders last', () => {
|
|
|
|
|
const primary = computeSchoolFlags(primaryFixture);
|
2026-09-22 11:58:36 +01:00
|
|
|
const ids = buildNavItems(primary, { ...navInput, hasNearbySchools: true }).map((i) => i.id);
|
|
|
|
|
expect(ids[ids.length - 1]).toBe('nearby');
|
2026-09-21 22:42:59 +01:00
|
|
|
});
|
|
|
|
|
});
|