Merge pull request 'fix(admissions): flag-off pages must not speak for the council' (#135) from fix/distance-flag-off-absence-copy into main
Stage (build -> staging -> E2E gate) / Build Backend (FastAPI) (push) Successful in 13s
Stage (build -> staging -> E2E gate) / Build Frontend (Next.js) (push) Successful in 52s
Stage (build -> staging -> E2E gate) / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Stage (build -> staging -> E2E gate) / Deploy to Staging (push) Successful in 1s
Stage (build -> staging -> E2E gate) / E2E Journeys against Staging (push) Successful in 1m43s
Stage (build -> staging -> E2E gate) / Build Backend (FastAPI) (push) Successful in 13s
Stage (build -> staging -> E2E gate) / Build Frontend (Next.js) (push) Successful in 52s
Stage (build -> staging -> E2E gate) / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Stage (build -> staging -> E2E gate) / Deploy to Staging (push) Successful in 1s
Stage (build -> staging -> E2E gate) / E2E Journeys against Staging (push) Successful in 1m43s
Reviewed-on: #135
This commit was merged in pull request #135.
This commit is contained in:
commit
1ed4470fc2
6 files changed
+72
-6
No files matched your search
@@ -1304,6 +1304,52 @@ test('with the distance feature off, the section is absent rather than empty', a
|
|||||||
.toHaveCount(0);
|
.toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A secondary school carrying an EES admissions row, which is what makes its
|
||||||
|
* Admissions section render while the distance feature is dark.
|
||||||
|
*/
|
||||||
|
async function secondarySchoolWithAdmissions(page: Page) {
|
||||||
|
const list = await page.request.get('/api/schools?phase=secondary&page_size=40');
|
||||||
|
if (!list.ok()) return null;
|
||||||
|
const body = await list.json();
|
||||||
|
for (const s of (body?.schools ?? []).slice(0, 25)) {
|
||||||
|
const res = await page.request.get(`/api/schools/${s.urn}`);
|
||||||
|
if (!res.ok()) continue;
|
||||||
|
const detail = await res.json();
|
||||||
|
if (detail?.admissions == null) continue;
|
||||||
|
return { urn: s.urn as number };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('with the distance feature off, a secondary page makes no claim about publication', async ({ page }) => {
|
||||||
|
/*
|
||||||
|
* Shipping dark must not put words in the council's mouth. The secondary
|
||||||
|
* template is the only one that words the absence, and "X has not published
|
||||||
|
* a cut-off distance for this school" is false wherever X does publish and
|
||||||
|
* we are simply withholding it.
|
||||||
|
*
|
||||||
|
* This is why the API omits the key rather than sending null: absent means
|
||||||
|
* "cut-offs are not published at all", null means "this school has none".
|
||||||
|
* Only the second is a fact about the school, and only the second is sayable.
|
||||||
|
*/
|
||||||
|
test.skip(await distanceFeatureIsOn(page),
|
||||||
|
'the admission_distance flag is on in this environment');
|
||||||
|
|
||||||
|
const found = await secondarySchoolWithAdmissions(page);
|
||||||
|
test.skip(found === null, 'no secondary school in the sample has an admissions row');
|
||||||
|
|
||||||
|
await page.goto(`/school/${found!.urn}`);
|
||||||
|
await expect(page.locator('h1').first()).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
// The Admissions section is still there — this is not a test that the whole
|
||||||
|
// section vanished, which would pass for the wrong reason.
|
||||||
|
await expect(page.locator('#admissions')).toHaveCount(1);
|
||||||
|
|
||||||
|
await expect(page.getByText(/has not published a cut-off distance/)).toHaveCount(0);
|
||||||
|
await expect(page.getByText(/Contact the admissions authority/)).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('/api/flags is not reachable from the public internet', async ({ page }) => {
|
test('/api/flags is not reachable from the public internet', async ({ page }) => {
|
||||||
// It names every unreleased feature and whether it is on. Next reads it
|
// It names every unreleased feature and whether it is on. Next reads it
|
||||||
// server-side over the Docker network; the public proxy must deny it.
|
// server-side over the Docker network; the public proxy must deny it.
|
||||||
|
|||||||
@@ -98,6 +98,17 @@ describe('secondary detail page', () => {
|
|||||||
|
|
||||||
expect(screen.getByText(/has not published a cut-off distance/)).toBeInTheDocument();
|
expect(screen.getByText(/has not published a cut-off distance/)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('makes no claim about publication when the feature is switched off', () => {
|
||||||
|
// Absent, not null. The API omits the key entirely while the
|
||||||
|
// admission_distance flag is off, and "Islington has not published a
|
||||||
|
// cut-off distance" is then a statement about us, not about Islington —
|
||||||
|
// false wherever the authority does publish one.
|
||||||
|
renderSecondarySchoolDetail({ ...secondaryFixture, admissionDistance: undefined });
|
||||||
|
|
||||||
|
expect(screen.queryByText(/has not published a cut-off distance/)).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText(/Contact the admissions authority/)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── The Distance section ───────────────────────────────────────────────
|
// ── The Distance section ───────────────────────────────────────────────
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export default async function SchoolPage({ params }: SchoolPageProps) {
|
|||||||
census={census ?? null}
|
census={census ?? null}
|
||||||
admissions={admissions ?? null}
|
admissions={admissions ?? null}
|
||||||
admissionsHistory={admissions_history ?? []}
|
admissionsHistory={admissions_history ?? []}
|
||||||
admissionDistance={admission_distance ?? null}
|
admissionDistance={admission_distance}
|
||||||
deprivation={deprivation ?? null}
|
deprivation={deprivation ?? null}
|
||||||
finance={finance ?? null}
|
finance={finance ?? null}
|
||||||
nationalAvg={nationalAvg}
|
nationalAvg={nationalAvg}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function DistanceSection({
|
|||||||
admissionDistance,
|
admissionDistance,
|
||||||
schoolInfo,
|
schoolInfo,
|
||||||
}: {
|
}: {
|
||||||
admissionDistance: SchoolAdmissionDistance | null;
|
admissionDistance: SchoolAdmissionDistance | null | undefined;
|
||||||
schoolInfo: School;
|
schoolInfo: School;
|
||||||
}) {
|
}) {
|
||||||
// Without a figure there is nothing to compare against, and without
|
// Without a figure there is nothing to compare against, and without
|
||||||
|
|||||||
@@ -21,11 +21,17 @@ export function SecondaryAdmissionsSection({
|
|||||||
published cut-off and no EES admissions row. */
|
published cut-off and no EES admissions row. */
|
||||||
admissions: SchoolAdmissions | null;
|
admissions: SchoolAdmissions | null;
|
||||||
admissionsHistory: SchoolAdmissions[];
|
admissionsHistory: SchoolAdmissions[];
|
||||||
admissionDistance: SchoolAdmissionDistance | null;
|
admissionDistance: SchoolAdmissionDistance | null | undefined;
|
||||||
schoolInfo: School;
|
schoolInfo: School;
|
||||||
hasSixthForm: boolean;
|
hasSixthForm: boolean;
|
||||||
}) {
|
}) {
|
||||||
const cutoff = describeCutoff(admissionDistance);
|
const cutoff = describeCutoff(admissionDistance);
|
||||||
|
/* Absent means cut-offs are not being published at all; null means this
|
||||||
|
school has no published cut-off. Only the second is a fact about the
|
||||||
|
school, and only the second can be stated. Saying "X has not published a
|
||||||
|
cut-off" while the feature is dark describes us, and is false wherever the
|
||||||
|
authority does publish one. */
|
||||||
|
const featureOn = admissionDistance !== undefined;
|
||||||
// Moved with this section from SecondarySchoolDetailView, its only consumer.
|
// Moved with this section from SecondarySchoolDetailView, its only consumer.
|
||||||
const admissionsTag = (() => {
|
const admissionsTag = (() => {
|
||||||
const policy = schoolInfo.admissions_policy?.toLowerCase() ?? '';
|
const policy = schoolInfo.admissions_policy?.toLowerCase() ?? '';
|
||||||
@@ -102,7 +108,7 @@ export function SecondaryAdmissionsSection({
|
|||||||
{CUTOFF_NOTE} {CUTOFF_MEASUREMENT_NOTE}
|
{CUTOFF_NOTE} {CUTOFF_MEASUREMENT_NOTE}
|
||||||
{cutoff.routeNote && <> {cutoff.routeNote}</>}
|
{cutoff.routeNote && <> {cutoff.routeNote}</>}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : featureOn ? (
|
||||||
<p className={styles.sectionSubtitle} style={{ marginTop: '1rem' }}>
|
<p className={styles.sectionSubtitle} style={{ marginTop: '1rem' }}>
|
||||||
{describeCutoffAbsence({
|
{describeCutoffAbsence({
|
||||||
localAuthority: schoolInfo.local_authority,
|
localAuthority: schoolInfo.local_authority,
|
||||||
@@ -110,7 +116,7 @@ export function SecondaryAdmissionsSection({
|
|||||||
admissionsHistory,
|
admissionsHistory,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
{hasSixthForm && (
|
{hasSixthForm && (
|
||||||
<div className={styles.sixthFormNote}>
|
<div className={styles.sixthFormNote}>
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ export interface SecondarySchoolSectionsProps {
|
|||||||
/** Needed to tell a year with no published cut-off apart from a year the
|
/** Needed to tell a year with no published cut-off apart from a year the
|
||||||
* school simply was not oversubscribed. */
|
* school simply was not oversubscribed. */
|
||||||
admissionsHistory: SchoolAdmissions[];
|
admissionsHistory: SchoolAdmissions[];
|
||||||
admissionDistance: SchoolAdmissionDistance | null;
|
/** Absent — not null — while the admission_distance flag is off. The two
|
||||||
|
* mean different things to the reader and must stay distinguishable:
|
||||||
|
* see SecondaryAdmissionsSection, which words the absence. */
|
||||||
|
admissionDistance: SchoolAdmissionDistance | null | undefined;
|
||||||
deprivation: SchoolDeprivation | null;
|
deprivation: SchoolDeprivation | null;
|
||||||
finance: SchoolFinance | null;
|
finance: SchoolFinance | null;
|
||||||
nationalAvg: NationalAverages | null;
|
nationalAvg: NationalAverages | null;
|
||||||
|
|||||||
Reference in new issue
Block a user