🐛 fix(frontend): Playwright now detects health endpoint failures (was silently passing) (#32)
User caught silent regression: existing test only asserted dashboard visibility, which is also true on the error branch. New tests assert healthy state + new regression test mocks /api/healthz to 502. Co-authored-by: Gabriel Radureau <arcodange@gmail.com> Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
This commit was merged in pull request #32.
This commit is contained in:
@@ -6,11 +6,28 @@ const meta: Meta<typeof HealthDashboard> = {
|
|||||||
component: HealthDashboard,
|
component: HealthDashboard,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
}
|
}
|
||||||
|
|
||||||
export default meta
|
export default meta
|
||||||
|
|
||||||
type Story = StoryObj<typeof 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 = {
|
export const Default: Story = {
|
||||||
args: {},
|
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.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,37 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
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 page.goto('/')
|
||||||
await expect(page.getByTestId('health-dashboard')).toBeVisible()
|
await expect(page.getByTestId('health-dashboard')).toBeVisible()
|
||||||
const heading = page.getByRole('heading', { name: /dance-lessons-coach/i })
|
const heading = page.getByRole('heading', { name: /dance-lessons-coach/i })
|
||||||
await expect(heading).toBeVisible()
|
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 })
|
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 })
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user