fix(search): tag only what the register records

The secondary row tested the admissions policy with includes('selective'),
which "Non-selective" passes, so every comprehensive (Burntwood, Graveney)
was tagged Selective. It now needs an exact "Selective".

Both rows excluded only "Does not apply" from the religious character, so a
school recorded as "None" got "Faith priority" (Putney High) or a bare
"None" chip (Abacus Belsize Primary). hasReligiousCharacter() treats both of
the register's no-faith values as no faith.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5.5 committed 2026-10-02 22:50:44 +01:00
1 parent dd5b48e612
commit e65c93b68e
5 files changed
+87 -9

No files matched your search

@@ -0,0 +1,36 @@
/**
* SchoolRow (primary search results): line 2 prints the religious character
* only when the school has one. The register's "None" was printed as a chip.
*/
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { SchoolRow } from '@/components/SchoolRow';
import type { School } from '@/lib/types';
const base = {
urn: 100001,
school_name: 'Alpha Primary School',
local_authority: 'Testshire',
school_type: 'Free schools',
phase: 'Primary',
gender: 'Mixed',
age_range: '4-11',
rwm_expected_pct: 70,
} as unknown as School;
describe('SchoolRow religious character', () => {
it.each(['None', 'Does not apply', ''])(
'prints nothing when the register says %p',
(religious_denomination) => {
render(<SchoolRow school={{ ...base, religious_denomination }} />);
expect(screen.queryByText('None')).not.toBeInTheDocument();
expect(screen.queryByText('Does not apply')).not.toBeInTheDocument();
},
);
it('prints a religious character the school has', () => {
render(<SchoolRow school={{ ...base, religious_denomination: 'Church of England' }} />);
expect(screen.getByText('Church of England')).toBeInTheDocument();
});
});
@@ -65,3 +65,38 @@ describe('SecondarySchoolRow proposed-to-close tag', () => {
expect(screen.queryByText(/Proposed to close/)).not.toBeInTheDocument();
});
});
describe('SecondarySchoolRow admissions tag', () => {
it('tags a selective school', () => {
render(<SecondarySchoolRow school={{ ...base, admissions_policy: 'Selective' }} />);
expect(screen.getByText('Selective')).toBeInTheDocument();
});
it('does not tag a non-selective school as selective', () => {
// "Non-selective" contains "selective": a substring test tagged every
// comprehensive (Burntwood, Graveney) as Selective.
render(<SecondarySchoolRow school={{ ...base, admissions_policy: 'Non-selective' }} />);
expect(screen.queryByText('Selective')).not.toBeInTheDocument();
});
it.each(['None', 'Does not apply', '', null])(
'gives no faith tag when the religious character is %p',
(religious_denomination) => {
render(
<SecondarySchoolRow
school={{ ...base, admissions_policy: 'Not applicable', religious_denomination }}
/>,
);
expect(screen.queryByText('Faith priority')).not.toBeInTheDocument();
},
);
it('tags a school with a religious character', () => {
render(
<SecondarySchoolRow
school={{ ...base, admissions_policy: 'Not applicable', religious_denomination: 'Church of England' }}
/>,
);
expect(screen.getByText('Faith priority')).toBeInTheDocument();
});
});
+2 -4
View File
@@ -9,7 +9,7 @@
*/
import type { School } from '@/lib/types';
import { formatPercentage, calculateTrend, getPhaseStyle, schoolUrl, buildOfstedListBadge, formatAgeRange, isProposedToClose, isSpecialSchool, listRwmValue } from '@/lib/utils';
import { formatPercentage, calculateTrend, getPhaseStyle, schoolUrl, buildOfstedListBadge, formatAgeRange, hasReligiousCharacter, isProposedToClose, isSpecialSchool, listRwmValue } from '@/lib/utils';
import styles from './SchoolRow.module.css';
interface SchoolRowProps {
@@ -34,9 +34,7 @@ export function SchoolRow({
const ofstedBadge = buildOfstedListBadge(school);
const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
const showDenomination =
school.religious_denomination &&
school.religious_denomination !== 'Does not apply';
const showDenomination = hasReligiousCharacter(school.religious_denomination);
// The school's OWN figure and its year-over-year trend are same-school
// measures — shown whenever there's a real value (not the all-zero
+5 -5
View File
@@ -11,14 +11,14 @@
'use client';
import type { School } from '@/lib/types';
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, isProposedToClose, isSpecialSchool } from '@/lib/utils';
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, hasReligiousCharacter, isProposedToClose, isSpecialSchool } from '@/lib/utils';
import styles from './SecondarySchoolRow.module.css';
function detectAdmissionsTag(school: School): string | null {
const policy = school.admissions_policy?.toLowerCase() ?? '';
if (policy.includes('selective')) return 'Selective';
const denom = school.religious_denomination ?? '';
if (denom && denom !== 'Does not apply') return 'Faith priority';
// Exact match: "Non-selective" contains "selective", so a substring test
// tagged every comprehensive as Selective.
if (school.admissions_policy?.trim().toLowerCase() === 'selective') return 'Selective';
if (hasReligiousCharacter(school.religious_denomination)) return 'Faith priority';
return null;
}
+9
View File
@@ -902,6 +902,15 @@ export function isSpecialSchool(school: { school_type?: string | null }): boolea
return /\bspecial\b/.test(t) || /pupil referral/.test(t) || /alternative provision/.test(t);
}
/**
* Whether GIAS records a religious character. "None" and "Does not apply" are
* the register's two ways of saying it has none, and neither is a faith.
*/
export function hasReligiousCharacter(value: string | null | undefined): boolean {
const v = value?.trim().toLowerCase() ?? '';
return v !== '' && v !== 'none' && v !== 'does not apply';
}
/**
* The school's combined Reading, Writing & Maths figure, or null when there is
* no real one to show.