🐛 fix(frontend): make Playwright detect health endpoint failures
Some checks failed
CI/CD Pipeline / Build Docker Cache (push) Successful in 15s
CI/CD Pipeline / CI Pipeline (push) Failing after 6m32s
CI/CD Pipeline / Trigger Docker Push (push) Has been skipped

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 16:46:27 +02:00
parent 17130082c6
commit 923b98168c
2 changed files with 47 additions and 2 deletions

View File

@@ -6,11 +6,28 @@ const meta: Meta<typeof HealthDashboard> = {
component: HealthDashboard,
tags: ['autodocs'],
}
export default meta
type Story = StoryObj<typeof meta>
// 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.',
},
},
},
}

View File

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