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 }) +})