import { test, expect, Page } from '@playwright/test'; /** * Journey tests for SchoolCompare, run against the staging environment as the * gate before promotion to production. They assert stable data invariants * (results exist, key UI renders) rather than exact numbers, so routine data * refreshes don't break the pipeline. */ async function searchByName(page: Page, query: string) { await page.goto('/'); const searchInput = page.getByPlaceholder('School name or postcode').first(); await searchInput.fill(query); await searchInput.press('Enter'); await page.waitForURL(/search=|postcode=/); } function schoolLinks(page: Page) { return page.locator('a[href^="/school/"]'); } test('home page loads with hero search', async ({ page }) => { await page.goto('/'); await expect(page.locator('h1').first()).toBeVisible(); await expect(page.getByPlaceholder('School name or postcode').first()).toBeVisible(); }); test('searching by name returns school results', async ({ page }) => { await searchByName(page, 'primary'); await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 }); expect(await schoolLinks(page).count()).toBeGreaterThan(1); }); test('searching by postcode returns nearby schools', async ({ page }) => { await searchByName(page, 'B1 1BB'); await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 }); }); test('school detail page renders name and performance data', async ({ page }) => { await searchByName(page, 'primary'); const firstSchool = schoolLinks(page).first(); await expect(firstSchool).toBeVisible({ timeout: 15_000 }); await firstSchool.click(); await page.waitForURL(/\/school\//); await expect(page.locator('h1').first()).toBeVisible(); // The detail page renders at least one chart canvas (performance history). // Charts live below the fold and only become visible once scrolled to, so // wait for the element, scroll it into view, then assert visibility. const chart = page.locator('canvas').first(); await chart.waitFor({ state: 'attached', timeout: 15_000 }); await chart.scrollIntoViewIfNeeded(); await expect(chart).toBeVisible({ timeout: 15_000 }); }); test('comparing two schools shows both side by side', async ({ page }) => { // Collect two school URNs from search results, then load the share URL await searchByName(page, 'primary'); await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 }); const hrefs = await schoolLinks(page).evaluateAll((links) => links.map((l) => (l as HTMLAnchorElement).getAttribute('href') || '') ); const urns = [...new Set(hrefs.map((h) => h.match(/\/school\/(\d+)/)?.[1]).filter(Boolean))]; expect(urns.length).toBeGreaterThanOrEqual(2); await page.goto(`/compare?urns=${urns[0]},${urns[1]}`); // Both schools' detail links should render in the comparison view await expect(page.locator(`a[href*="${urns[0]}"]`).first()).toBeVisible({ timeout: 15_000 }); await expect(page.locator(`a[href*="${urns[1]}"]`).first()).toBeVisible(); }); test('rankings page loads a populated table', async ({ page }) => { await page.goto('/rankings'); await expect(page.getByRole('heading', { name: /rankings/i }).first()).toBeVisible(); const rows = page.locator('table tbody tr'); await expect(rows.first()).toBeVisible({ timeout: 15_000 }); expect(await rows.count()).toBeGreaterThan(5); });