PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 9s
PR Checks / Build Backend (no push) (pull_request) Successful in 18s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m18s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 3m48s
Review caught _places_payload documenting a `phase_url` the function never returned. The docstring was not stray prose: the approved design included the phase variant — "Primary schools in Beccles" was one of its four example links — and it was dropped during implementation without being mentioned. Deleting the sentence would have closed the report while losing the feature, so the links are built instead. These are the pages that most needed them. ~950 phase variants were once reachable by nothing at all: absent from every sitemap and unlinked from the place page. "Primary schools in brentwood" is the query they exist to answer. Membership is read from the registry's own `phase_urns` rather than re-derived from the school's phase string. The registry already decides which phases a place publishes and which schools are listed on each, so asking it is both shorter and the only way the link cannot disagree with the page it points at. It also means outcodes need no special case: they carry empty `phase_urns` by design, because nobody searches "primary schools in SW11", so they report no phase links on their own. `phases` is a list rather than a single url. An all-through school is listed on both the primary and secondary pages, so there is no tie to break and no reason to invent one. Each entry renders directly after its own place, so "22 primary schools in Brentwood" reads as part of Brentwood rather than as an unrelated link further along the row. The e2e journey now follows a phase link where the town publishes one and asserts it resolves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXnXQKnPpZBBP61fBQiFkq
93 lines
4.1 KiB
TypeScript
93 lines
4.1 KiB
TypeScript
/**
|
|
* The module that ends the stranding: before it, a school page's only anchor
|
|
* pointed at the school's own website, so ~27k pages sent authority off-site
|
|
* and none of it reached the location layer.
|
|
*/
|
|
import { render, screen } from '@testing-library/react';
|
|
import { NearbyPlaces } from '@/components/school/NearbyPlaces';
|
|
|
|
const essex = { kind: 'authority', slug: 'essex', name: 'Essex', count: 480, url: '/schools/authority/essex', phases: [] };
|
|
const brentwood = { kind: 'town', slug: 'brentwood', name: 'Brentwood', count: 37, url: '/schools/brentwood', phases: [] };
|
|
const cm15 = { kind: 'outcode', slug: 'cm15', name: 'CM15', count: 12, url: '/schools/near/cm15', phases: [] };
|
|
|
|
describe('NearbyPlaces', () => {
|
|
it('links to every place the school belongs to', () => {
|
|
render(<NearbyPlaces places={[essex, brentwood, cm15]} />);
|
|
|
|
expect(screen.getByRole('link', { name: /Brentwood/ }))
|
|
.toHaveAttribute('href', '/schools/brentwood');
|
|
expect(screen.getByRole('link', { name: /Essex/ }))
|
|
.toHaveAttribute('href', '/schools/authority/essex');
|
|
expect(screen.getByRole('link', { name: /CM15/ }))
|
|
.toHaveAttribute('href', '/schools/near/cm15');
|
|
});
|
|
|
|
it('says how many schools each link leads to', () => {
|
|
// An anchor that states its destination's size is worth more to a reader
|
|
// and to a crawler than "see more".
|
|
render(<NearbyPlaces places={[brentwood]} />);
|
|
expect(screen.getByRole('link', { name: /37 schools in Brentwood/ }))
|
|
.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders nothing at all when the school has no published places', () => {
|
|
// Not an empty heading. A school whose town and authority both fall below
|
|
// the threshold has nowhere to point, and the page should look as it did
|
|
// before the module existed.
|
|
const { container } = render(<NearbyPlaces places={[]} />);
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
|
|
it('puts the narrowest place first, which is the most useful link', () => {
|
|
// The API orders widest-first for the breadcrumb; a reader on a school
|
|
// page wants its town before its county.
|
|
render(<NearbyPlaces places={[essex, brentwood, cm15]} />);
|
|
const hrefs = screen.getAllByRole('link').map((a) => a.getAttribute('href'));
|
|
expect(hrefs.indexOf('/schools/brentwood'))
|
|
.toBeLessThan(hrefs.indexOf('/schools/authority/essex'));
|
|
});
|
|
|
|
it('handles a singular count without saying "1 schools"', () => {
|
|
render(<NearbyPlaces places={[{ ...brentwood, count: 1 }]} />);
|
|
expect(screen.getByRole('link', { name: /1 school in Brentwood/ }))
|
|
.toBeInTheDocument();
|
|
});
|
|
|
|
it('links the phase page the school appears on', () => {
|
|
// "primary schools in brentwood" is the query these pages exist for.
|
|
render(<NearbyPlaces places={[{
|
|
...brentwood,
|
|
phases: [{ phase: 'primary', count: 22, url: '/schools/brentwood/primary' }],
|
|
}]} />);
|
|
|
|
expect(screen.getByRole('link', { name: /22 primary schools in Brentwood/ }))
|
|
.toHaveAttribute('href', '/schools/brentwood/primary');
|
|
});
|
|
|
|
it('links both phase pages for an all-through school', () => {
|
|
render(<NearbyPlaces places={[{
|
|
...brentwood,
|
|
phases: [
|
|
{ phase: 'primary', count: 22, url: '/schools/brentwood/primary' },
|
|
{ phase: 'secondary', count: 9, url: '/schools/brentwood/secondary' },
|
|
],
|
|
}]} />);
|
|
|
|
expect(screen.getByRole('link', { name: /22 primary schools/ })).toBeInTheDocument();
|
|
expect(screen.getByRole('link', { name: /9 secondary schools/ })).toBeInTheDocument();
|
|
});
|
|
|
|
it('keeps a phase link next to the place it belongs to', () => {
|
|
// Grouping matters: "22 primary schools in Brentwood" directly after
|
|
// "37 schools in Brentwood" reads as one place, not two unrelated links.
|
|
render(<NearbyPlaces places={[essex, {
|
|
...brentwood,
|
|
phases: [{ phase: 'primary', count: 22, url: '/schools/brentwood/primary' }],
|
|
}]} />);
|
|
|
|
const hrefs = screen.getAllByRole('link').map((a) => a.getAttribute('href'));
|
|
expect(hrefs.indexOf('/schools/brentwood/primary'))
|
|
.toBe(hrefs.indexOf('/schools/brentwood') + 1);
|
|
});
|
|
});
|