PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 9s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 44s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 40s
The location pages were only half-tracked. Umami counts a pageview for each of the ~3,900 URLs automatically, but nothing else: components/ places contained no track() call, and place_viewed was not even a declared event name. The part that mattered was worse than a gap. getNavigationSource mapped a same-origin referrer to a funnel source and had no case for /schools/, so every school view arriving through the location layer fell through to 'direct' — the bucket you read as "typed the URL, no referrer". W2's whole purpose is funnelling search traffic onto school pages, so the one measurement that says whether it worked was reporting the wrong answer, and reporting it confidently. Verified live against staging: expected "place", received "direct". /schools/ is checked before /school/. They differ by one letter and mean different things — the location layer versus a single school — and a prefix test in the wrong order silently merges them. place_viewed carries kind, slug, phase and school_count. kind is the reason it exists: whether to keep investing in these pages turns on which sort earns engagement, and a pageview cannot say, because all four families share the /schools/ prefix and only the registry knows which is which. It is a client component because PlaceView is a server component; one line in PlaceView covers all four families, since they all render through it. Both E2E journeys were verified failing against staging first — one because place_viewed does not exist there, the other on the exact "place" vs "direct" mismatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj
46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
import { render } from '@testing-library/react';
|
|
import { TrackPlaceView } from '@/components/places/TrackPlaceView';
|
|
|
|
const trackMock = jest.fn();
|
|
jest.mock('@/lib/analytics', () => ({
|
|
track: (...args: unknown[]) => trackMock(...args),
|
|
getNavigationSource: () => 'search',
|
|
}));
|
|
|
|
describe('TrackPlaceView', () => {
|
|
beforeEach(() => trackMock.mockClear());
|
|
|
|
it('reports which kind of location page was viewed', () => {
|
|
/*
|
|
* `kind` is the reason this event exists. Whether to keep investing in the
|
|
* location layer turns on which *sort* of page earns engagement — towns,
|
|
* authorities or postcode districts — and a bare pageview cannot say,
|
|
* because all four families share the /schools/ prefix.
|
|
*/
|
|
render(<TrackPlaceView kind="authority" slug="kent" count={412} />);
|
|
expect(trackMock).toHaveBeenCalledWith('place_viewed', {
|
|
kind: 'authority', slug: 'kent', phase: 'all',
|
|
school_count: 412, from: 'search',
|
|
});
|
|
});
|
|
|
|
it('names the phase when the page is a phase variant', () => {
|
|
render(<TrackPlaceView kind="town" slug="brentwood" count={29} phase="primary" />);
|
|
expect(trackMock).toHaveBeenCalledWith('place_viewed',
|
|
expect.objectContaining({ phase: 'primary' }));
|
|
});
|
|
|
|
it('fires once, not once per render', () => {
|
|
const { rerender } = render(
|
|
<TrackPlaceView kind="town" slug="brentwood" count={29} />);
|
|
rerender(<TrackPlaceView kind="town" slug="brentwood" count={29} />);
|
|
expect(trackMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('renders nothing', () => {
|
|
const { container } = render(
|
|
<TrackPlaceView kind="town" slug="brentwood" count={29} />);
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
});
|