Payload's admin panel ships its own root layout rendering html/body. Next allows multiple root layouts only when no app/layout.tsx exists, so the site's routes move into their own group. Route groups are invisible to routing: every public URL is unchanged, verified against the build's route table. The metadata file conventions deliberately stay at the app/ root. Moving them into the group renamed /icon.png to /icon-4usi79.png (likewise apple-icon and opengraph-image) and dropped /robots.txt altogether, which would have broken the /icon.png cache-control rule, the outputFileTracingIncludes entry for the share card, and robots.txt. darkThemeSafety reads app/globals.css off disk rather than importing it, so it needed its own path fix — a grep for import specifiers misses it, and it fails as an unrunnable suite rather than a failed assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
import { generateMetadata as placeMeta } from '@/app/(frontend)/schools/[place]/page';
|
|
|
|
jest.mock('@/lib/places', () => ({
|
|
...jest.requireActual('@/lib/places'),
|
|
fetchPlace: jest.fn(async (kind: string, slug: string) =>
|
|
slug === 'atlantis' ? null : ({
|
|
place: { kind, slug, name: 'Brentwood', count: 29,
|
|
parent_authority: 'Essex' },
|
|
schools: [], averages: { rwm_expected_pct: 63, attainment_8_score: null },
|
|
})),
|
|
fetchPlaces: jest.fn(async () => []),
|
|
}));
|
|
|
|
describe('place page metadata', () => {
|
|
it('titles the page the way the place is searched', async () => {
|
|
const m = await placeMeta({ params: Promise.resolve({ place: 'brentwood' }) });
|
|
expect((m.title as { absolute: string }).absolute).toMatch(/schools in brentwood/i);
|
|
});
|
|
|
|
it('canonicalises to its own path on the www host', async () => {
|
|
const m = await placeMeta({ params: Promise.resolve({ place: 'brentwood' }) });
|
|
expect(m.alternates?.canonical)
|
|
.toBe('https://www.schoolcompare.co.uk/schools/brentwood');
|
|
});
|
|
|
|
it('opts out of the layout template, which would double the brand', () => {
|
|
// The root layout appends '| schoolcompare' to a plain string title, and
|
|
// these titles already carry it — every place page shipped reading
|
|
// '... | schoolcompare | schoolcompare' until this was made absolute.
|
|
return placeMeta({ params: Promise.resolve({ place: 'brentwood' }) })
|
|
.then((m) => {
|
|
expect(typeof m.title).toBe('object');
|
|
expect((m.title as { absolute: string }).absolute)
|
|
.not.toMatch(/schoolcompare.*schoolcompare/);
|
|
});
|
|
});
|
|
|
|
it('an unknown place gets a not-found title rather than inventing one', async () => {
|
|
const m = await placeMeta({ params: Promise.resolve({ place: 'atlantis' }) });
|
|
expect(m.title).toMatch(/not found/i);
|
|
});
|
|
});
|