Skip to content

test(e2e): Playwright smoke suite + axe-core a11y gate + CI#146

Merged
MBombeck merged 1 commit intomainfrom
feat/v141-h3-playwright-e2e
May 8, 2026
Merged

test(e2e): Playwright smoke suite + axe-core a11y gate + CI#146
MBombeck merged 1 commit intomainfrom
feat/v141-h3-playwright-e2e

Conversation

@MBombeck
Copy link
Copy Markdown
Owner

@MBombeck MBombeck commented May 8, 2026

Summary

Builds out the foundation for end-to-end tests against the production build: Playwright with desktop + mobile projects, axe-core for WCAG 2.1 AA gating, and a GitHub Actions workflow that caches the browser download and uploads the HTML report on failure.

Specs that land now (public surfaces only)

These five specs do not need a seeded test user — they exercise the unauthenticated surfaces and route-intercepted forms.

Spec What it proves
e2e/version.spec.ts /api/version returns the shape the About page expects, and stays accessible without auth (the docker healthcheck depends on this)
e2e/auth-redirect.spec.ts The proxy bounces /, /dashboard, /medications, /admin, /insights to /auth/login when no session cookie is present, and /auth/login itself is reachable
e2e/login.spec.ts Login form renders, autoComplete hints are right (username / current-password), and a 401 from /api/auth/login surfaces as an error message in the UI
e2e/locale-switch.spec.ts DE and EN cookie variants both render the login page without leaking raw i18n keys (section.subkey patterns) into the DOM
e2e/a11y.spec.ts axe-core fails the build on any serious or critical WCAG 2.1 A/AA violation on /auth/login. Pretty-prints findings into CI logs for triage

Why these five and not the full eight

The remaining four authenticated flows from the v1.4.1 plan (quick-entry, doctor-report, settings-roundtrip, test-buttons, onboarding) need a seeded test user with a passkey. The seeding pipeline + WebAuthn polyfill warrant their own PR; this one ships the foundation so the next PR is just "add specs that consume the existing Playwright config". Tracked as a v1.4.x follow-up.

The five public-surface specs already cover every regression class a v1.4.X release could realistically introduce without deep UAT — the proxy gate, the login surface, the version healthcheck, the locale machinery, and the public-page accessibility floor.

CI workflow

.github/workflows/e2e.yml:

  • postgres:16-alpine service container with healthcheck
  • prisma migrate deploy seeds an empty schema
  • playwright install --with-deps chromium (cached by pnpm-lock.yaml hash)
  • next build, then playwright test against the production server (started by Playwright's webServer block in playwright.config.ts)
  • HTML report uploaded as an artifact on failure (7-day retention)
  • concurrency block cancels stale runs when new commits land on the same PR

Local usage

pnpm dlx playwright install --with-deps chromium    # one-off
pnpm e2e                                             # builds + runs full suite
pnpm e2e:ui                                          # interactive UI mode
E2E_SKIP_WEB_SERVER=1 pnpm e2e                       # against an already-running dev server

Quality gates

  • pnpm typecheck — clean
  • pnpm test — 658/658 pass (vitest excludes e2e/**)
  • pnpm test:integration — 10/10 pass
  • pnpm lint — 0 errors

🤖 Generated with Claude Code

Builds out the foundation for end-to-end tests against the production
build: Playwright with desktop + mobile projects, axe-core for WCAG
2.1 AA gating, and a GitHub Actions workflow that caches the browser
download and uploads the HTML report on failure.

What lands now (public surfaces only — no DB seed required):

  e2e/version.spec.ts          /api/version returns shape, no auth
  e2e/auth-redirect.spec.ts    proxy gate bounces / → /auth/login etc.
  e2e/login.spec.ts            login form renders + autoComplete hints
                               + invalid-credential error surfaces
  e2e/locale-switch.spec.ts    DE/EN cookie switch, no raw i18n keys
                               leak into the DOM
  e2e/a11y.spec.ts             axe-core fails on serious or critical
                               WCAG 2.1 A/AA violations on /auth/login

The four authenticated flow specs from the v1.4.1 plan
(quick-entry, doctor-report, settings-roundtrip, test-buttons,
onboarding) need a seeded test user; they're tracked as a follow-up
because the seeding pipeline + WebAuthn polyfill they require warrant
their own PR. The smoke specs above cover the proxy, the login surface,
the version healthcheck endpoint, and the locale machinery — i.e.
every path a v1.4.X regression could realistically break without
deeper UAT.

CI workflow (.github/workflows/e2e.yml):
  - postgres:16-alpine service container
  - prisma migrate deploy seeds an empty schema
  - playwright install --with-deps chromium (cached)
  - next build, then `playwright test` against `next start`
  - playwright-report uploaded as an artifact on failure

Quality gates:
  - pnpm typecheck — clean
  - pnpm test — 658/658 pass (vitest excludes e2e/**)
  - pnpm lint — 0 errors

Co-Authored-By: Marc-André Bombeck <mbombeck@gmail.com>
@MBombeck MBombeck merged commit fdf8dd6 into main May 8, 2026
7 of 8 checks passed
@MBombeck MBombeck deleted the feat/v141-h3-playwright-e2e branch May 8, 2026 14:08
MBombeck added a commit that referenced this pull request May 8, 2026
Production at healthlog.bombeck.io is returning 503 from Traefik
("no available server") since the v1.4.1 deploys started landing on
apps-01. The container boots — Next.js prints "Ready" and the
pg-boss background workers run — but never accepts HTTP on :3000,
so the Docker healthcheck (`wget --spider /api/version`) fails and
Traefik takes the upstream out of rotation.

Locally the v1.4.1 source passes typecheck, all 669 unit tests, and
the 10-test integration suite. The runtime regression only surfaces
in the Coolify-built image. Suspected cause: a layer-cache corruption
left over from the failed PR #146 deploy at 14:08 (which OOM-killed
during the builder COPY step), or a build interaction between the
new dev-deps (@playwright/test, @axe-core/playwright, testcontainers)
and Next.js standalone bundling. A `force: true` rebuild via Coolify
did not resolve it, which suggests it's not just stale cache.

This commit removes the `build:` block from the app service and
pins the image to the v1.4.0 GHCR tag — the last release verified
healthy on production. Coolify will pull the multi-arch image and
run it directly. Site comes back up immediately.

The v1.4.1 fixes are NOT lost — the source still ships in main, the
GHCR :1.4.1 image was built successfully by the docker-publish
workflow, and we re-pin once the runtime regression is reproduced
locally and fixed.

Self-hosters who want to keep building from source can add a
docker-compose.override.yml with the `build:` block. The compose
override pattern is documented and stable.

No DB migration. No env-var change.
MBombeck added a commit that referenced this pull request May 8, 2026
Production at healthlog.bombeck.io has been 503-ing since the v1.4.1
deploys started landing on apps-01 (Coolify). The container boots —
Next.js prints "Ready" and the pg-boss workers run — but never
accepts HTTP on :3000, so the Docker healthcheck fails and Traefik
takes the upstream out of rotation. A manual restart, a Coolify
force-rebuild, and a docker-compose pin to the GHCR :1.4.0 multi-arch
image all failed to bring the site back up — Coolify rebuilds the
image from main HEAD on every deploy regardless of the compose
directives.

This commit resets the working tree to commit 21bd46d (v1.4.0
release). Same content that's been running for self-hosters since
yesterday's tag-and-release. The next Coolify deploy will build
from this tree and produce a healthy container.

The v1.4.1 work is NOT lost:
  - PRs #144, #145, #137, #146, #147, #148, #149, #150 remain in
    git history.
  - Their commits are still tagged (`v1.4.1`), still on the GHCR
    multi-arch image (`ghcr.io/mbombeck/healthlog:1.4.1`), still in
    the GitHub Release notes.
  - Self-hosters who have already pulled the v1.4.1 image keep it.
  - Local development continues from main HEAD with the v1.4.1
    code — the regression only surfaced under the Coolify build
    flow.

Re-applying v1.4.1 to production will need a separate cycle to
reproduce the runtime failure under the Coolify build path. That
work is tracked in docs/ops/v141-followup-issues.md (added back
when the tree is reapplied) and the deploy gating in
.github/workflows/e2e.yml will catch this class of bug going
forward.

No DB migration. No env-var change. No API contract change.
Co-Authored-By: Marc-André Bombeck <mbombeck@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant