fix(compare): render all-secondary comparisons — re-run phase detection after basket hydration
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Regression: an all-secondary comparison must render the secondary sections.
|
||||
*
|
||||
* The basket hydrates from the URL a beat after mount, so the auto-phase
|
||||
* effect must re-run once selectedSchools arrives — with deps of only
|
||||
* [comparisonData] it fired once against an empty basket, bailed, and the
|
||||
* page stayed on an empty "primary" tab ("No primary schools in your
|
||||
* comparison") even though all schools were secondary.
|
||||
*/
|
||||
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
|
||||
import { ComparisonView } from '@/components/ComparisonView';
|
||||
import { ComparisonProvider } from '@/context/ComparisonProvider';
|
||||
import type { ComparisonData, School } from '@/lib/types';
|
||||
|
||||
const fetchComparison = jest.fn();
|
||||
jest.mock('@/lib/api', () => ({
|
||||
fetchComparison: (...args: unknown[]) => fetchComparison(...args),
|
||||
}));
|
||||
jest.mock('@/lib/analytics', () => ({ track: jest.fn() }));
|
||||
|
||||
function secondarySchool(urn: number, name: string): School {
|
||||
return {
|
||||
urn,
|
||||
school_name: name,
|
||||
local_authority: 'Testshire',
|
||||
school_type: 'Academy converter',
|
||||
attainment_8_score: 55,
|
||||
phase: 'Secondary',
|
||||
} as School;
|
||||
}
|
||||
|
||||
function data(urn: number, name: string): ComparisonData {
|
||||
return {
|
||||
school_info: secondarySchool(urn, name),
|
||||
yearly_data: [{ year: 202425, attainment_8_score: 55 }] as ComparisonData['yearly_data'],
|
||||
ofsted: null,
|
||||
census: null,
|
||||
admissions: null,
|
||||
admissions_history: [],
|
||||
deprivation: null,
|
||||
};
|
||||
}
|
||||
|
||||
const INITIAL_DATA = {
|
||||
'300': data(300, 'Gamma High'),
|
||||
'400': data(400, 'Delta Academy'),
|
||||
};
|
||||
|
||||
test('an all-secondary comparison renders the sections, not an empty primary tab', async () => {
|
||||
render(
|
||||
<ComparisonProvider>
|
||||
<ComparisonView
|
||||
initialData={INITIAL_DATA}
|
||||
initialNationalAverages={{
|
||||
year: 202425,
|
||||
primary: {},
|
||||
secondary: { attainment_8_score: 46 },
|
||||
by_year: [],
|
||||
}}
|
||||
initialBenchmarks={undefined}
|
||||
initialUrns={[300, 400]}
|
||||
metrics={[]}
|
||||
selectedMetric="attainment_8_score"
|
||||
/>
|
||||
</ComparisonProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'At a glance' })).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getAllByText('Gamma High').length).toBeGreaterThan(0);
|
||||
expect(screen.queryByText(/No primary schools in your comparison/)).toBeNull();
|
||||
expect(fetchComparison).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -173,7 +173,11 @@ export function ComparisonView({
|
||||
if (!metricFitsPhase) {
|
||||
setSelectedMetric(newPhase === 'secondary' ? 'attainment_8_score' : 'rwm_expected_pct');
|
||||
}
|
||||
}, [comparisonData]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// selectedSchools is a dep because the basket hydrates after mount: the
|
||||
// first run sees an empty basket and bails, so it must re-fire when the
|
||||
// schools arrive. primarySchools/secondarySchools/metrics/selectedMetric
|
||||
// are intentionally omitted (derived or would cause loops).
|
||||
}, [comparisonData, selectedSchools]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const handlePhaseChange = (phase: 'primary' | 'secondary') => {
|
||||
phaseLockedByUser.current = true;
|
||||
|
||||
Reference in New Issue
Block a user