From 923b98168c9213124451596aefbba6aa49f0f6f1 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 3 May 2026 16:46:27 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(frontend):=20make=20Playwrig?= =?UTF-8?q?ht=20detect=20health=20endpoint=20failures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by user 2026-05-03: the existing test `home page loads and shows server health info` only asserted visibility of the dashboard component, which is ALSO visible when it shows the error state ("Error loading health: [GET] /api/healthz: 404"). So when the Go backend is down or the dev proxy misroutes /api/*, the page shows a visible error UI but the e2e test PASSES — silent regression risk. This commit: 1. health.spec.ts now asserts: - health-info element visible (only present in healthy state) - health-status text equals "healthy" - "Error loading health" text NOT visible 2. Adds a regression test "home page surfaces health endpoint errors visibly" that mocks /api/healthz to 502 and asserts the error UI appears (this is reproducible regardless of backend state). 3. HealthDashboard.stories.ts: adds a documentation note about the useFetch-internal-state limitation for now (proper state-based stories require a future component prop refactor). 🤖 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/HealthDashboard.stories.ts | 19 +++++++++++- frontend/tests/e2e/health.spec.ts | 30 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/frontend/components/HealthDashboard.stories.ts b/frontend/components/HealthDashboard.stories.ts index fe3a54e..6980774 100644 --- a/frontend/components/HealthDashboard.stories.ts +++ b/frontend/components/HealthDashboard.stories.ts @@ -6,11 +6,28 @@ const meta: Meta = { component: HealthDashboard, tags: ['autodocs'], } - export default meta type Story = StoryObj +// Default story - calls real /api/healthz (works in browser if dev proxy + backend are up) export const Default: Story = { args: {}, } + +// Documents the loading/error visuals so reviewers see them in Storybook +// without needing a backend down. The component currently doesn't accept overrides +// because it uses useFetch internally - this story is a placeholder for a future +// refactor that exposes data/pending/error as props for testability. +export const DocumentationStub: Story = { + args: {}, + parameters: { + docs: { + description: { + story: + 'Renders the same as Default. The HealthDashboard fetches /api/healthz internally, so loading/error states only appear when the backend is down or slow. ' + + 'Future improvement: accept healthData/pending/error as props for easy state-based stories. Until then, see frontend/docs/e2e/ for screenshots of both healthy and error states.', + }, + }, + }, +} diff --git a/frontend/tests/e2e/health.spec.ts b/frontend/tests/e2e/health.spec.ts index cbff6c8..5ed5c5f 100644 --- a/frontend/tests/e2e/health.spec.ts +++ b/frontend/tests/e2e/health.spec.ts @@ -1,9 +1,37 @@ import { test, expect } from '@playwright/test' -test('home page loads and shows server health info', async ({ page }) => { +test('home page loads and shows healthy server state', async ({ page }) => { await page.goto('/') await expect(page.getByTestId('health-dashboard')).toBeVisible() const heading = page.getByRole('heading', { name: /dance-lessons-coach/i }) await expect(heading).toBeVisible() + + // Assert the dashboard is in HEALTHY state, not an error state. + // The dashboard renders an "Error loading health: ..." paragraph when /api/healthz + // is unreachable (Go backend not running, proxy misconfigured, endpoint removed, + // etc.). Without these assertions the test would falsely PASS even when the + // dashboard shows the error UI - regression observed 2026-05-03 (Go backend + // not running locally → page renders the error, Playwright PASSES). + await expect(page.getByTestId('health-info')).toBeVisible() + await expect(page.getByTestId('health-status')).toHaveText('healthy') + await expect(page.getByText(/Error loading health/i)).not.toBeVisible() + await page.screenshot({ path: 'tests/e2e/screenshots/home-page-loads-and-shows-server-health-info.png', fullPage: true }) }) + +// Regression spec: documents the expected error UX so we don't ship a silent failure. +// Routes /api/healthz to a 502 mock so the test is reproducible regardless of backend. +test('home page surfaces health endpoint errors visibly', async ({ page }) => { + await page.route('**/api/healthz', (route) => { + route.fulfill({ + status: 502, + contentType: 'application/json', + body: JSON.stringify({ error: 'simulated_backend_down' }), + }) + }) + await page.goto('/') + await expect(page.getByTestId('health-dashboard')).toBeVisible() + await expect(page.getByText(/Error loading health/i)).toBeVisible() + await expect(page.getByTestId('health-info')).not.toBeVisible() + await page.screenshot({ path: 'tests/e2e/screenshots/home-page-surfaces-health-endpoint-errors-visibly.png', fullPage: true }) +}) -- 2.49.1