import { test, expect } from './fixtures'; // The ?boom= trigger only exists on the dev server, which is what Playwright // runs against. A production build drops it entirely — verified separately by // grepping dist for the marker. test.describe('Error boundaries', () => { test('a throw below the root shows a message rather than a blank page', async ({ page }) => { await page.goto('/?boom=page'); await expect(page.getByRole('heading', { name: 'Something went wrong' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Reload' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Back to the shop' })).toBeVisible(); }); test('a throw in the item grid leaves the header and theme switch usable', async ({ page }) => { await page.goto('/?boom=catalogue'); await expect(page.getByRole('heading', { name: "The item list didn't load" })).toBeVisible(); // The claim this boundary exists to make: a bad item no longer takes // navigation down with it. await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeVisible(); await expect(page.getByRole('switch')).toBeVisible(); // And the root boundary did not also fire — only the nearest one should. await expect(page.getByRole('heading', { name: 'Something went wrong' })).toHaveCount(0); }); test('a throw in the modal block leaves the storefront behind it intact', async ({ page }) => { await page.goto('/?boom=modal'); await expect(page.getByRole('heading', { name: "Couldn't open that" })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeVisible(); }); test('a caught error is reported to the server', async ({ page }) => { const reports: string[] = []; page.on('request', (request) => { if (request.url().includes('/api/client-errors')) { reports.push(request.postData() ?? ''); } }); await page.goto('/?boom=catalogue'); await expect(page.getByRole('heading', { name: "The item list didn't load" })).toBeVisible(); // Observed on the wire rather than trusting that the reporter was called. // // Greater-than-zero, not exactly one: StrictMode double-invokes render in // development, so the dev server this runs against may report twice where // production reports once. Asserting an exact count would make the test // fail for a reason that has nothing to do with the boundary. await expect.poll(() => reports.length).toBeGreaterThan(0); expect(reports[0]).toContain('"context":"catalogue"'); }); });