Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

web-e2e

End-to-end and integration tests for the Languages Learner web app.

In this repo, "integration tests" are full-page screenshot (visual regression) tests: each spec opens a page and compares it against committed baselines in both light and dark themes.

Links

Layout

  • integration/playwright.config.ts — Playwright config (testDir = integration/tests).
  • integration/tests/** — specs and their *-snapshots/ baselines (landing, office, dictionary).
  • pages/** — Page Object Models.
  • core/** — shared login/setup helpers.
  • integration/.env.example — template for the local env.

Scripts

Script What it does
test:integration Runs the Playwright screenshot suite against BASE_URL (native).
test:integration:update Same, but regenerates the screenshot baselines (--update-snapshots).
test:integration:docker Runs the suite inside the CI-matched Linux Docker image.
test:integration:update:docker Regenerates the baselines inside that Docker image (see below).
typecheck tsgo --noEmit.
lint / lint:fix ESLint (check / autofix).

Run a single script with a filter, e.g. pnpm --filter web-e2e test:integration.

Running locally

  1. Copy integration/.env.exampleintegration/.env and fill in:
    • BASE_URL — a running server, e.g. http://localhost:5173 (start apps/web first with pnpm --filter app-web dev) or a staging URL.
    • AUTH_LOGIN / AUTH_PASSWORD — a seeded, confirmed test user. global_setup performs a real UI login and persists the session as storage state.
  2. pnpm --filter web-e2e test:integration.

Baselines are Linux-rendered (…-chromium-linux.png, hardcoded in the config). On Windows/macOS a native :update would overwrite them with locally-rendered images that then mismatch on the CI Linux runner — so never update baselines natively. Use the Docker flow below instead.

Updating screenshot baselines (from any OS)

This mirrors how uikit component tests are updated: baselines are (re)generated inside a Docker image that matches the CI runner (Ubuntu + the same fonts + pinned Playwright, see Dockerfile.tests.component), while CI itself runs the comparison directly on ubuntu-latest without Docker.

  1. Make sure Docker Desktop is running.
  2. Put AUTH_LOGIN / AUTH_PASSWORD (a seeded, confirmed test user) in integration/.env. BASE_URL there is only for native runs — the Docker flow overrides it.
  3. Start the app natively on the host (it must not be containerised): pnpm --filter app-web dev (serves http://localhost:5173).
  4. Regenerate the baselines in the container: pnpm --filter web-e2e test:integration:update:docker.

The container reaches the host dev server via host.docker.internal:5173 (set as BASE_URL by the playwright-ct service in docker-compose.yml; override with the E2E_BASE_URL env var to point at a different URL, e.g. staging). The updated *-snapshots/*.png are written back to the working tree — review and commit them. Run test:integration:docker (without :update) to verify against the committed baselines the same way.

Network mocks (HAR)

Backend traffic (<baseURL>/api/**) is mocked via committed …-linux.har files next to each snapshot (the mockNetwork fixture from @languages-learner/app-integration-tests-utils, which also sanitizes the HAR). The :update scripts refresh the HAR together with the screenshots — test:integration:update sets UPDATE=1 (via cross-env), so test:integration:update:docker re-records and sanitizes the HAR while regenerating the PNGs. Commit the refreshed *.har/*.dat alongside the screenshots. Plain test:integration / :docker (and CI) only replay the committed HAR.

Because Playwright matches HAR entries by exact request URL (host included) and the backend is served under the app's own origin — which differs between local recording and the dynamic CI stage domain — the committed HAR is host-normalized: the recording origin is rewritten to the stable placeholder http://mocked.local, and on replay a throwaway *.replay.har copy is rewritten back to the current baseURL. Login itself is not mocked (it goes to Supabase auth directly).

After this change the previously committed HARs are stale (they targeted the old direct-Supabase URLs). Re-run test:integration:update:docker once to record the /api traffic, then commit the regenerated *.har/*.dat — until then CI has nothing to replay.

Committed HARs must carry no secrets (session cookies, bearer tokens, API keys). The mockNetwork fixture sanitizes them automatically on record; pnpm sanitize-har re-sanitizes every HAR in the repo on demand, and CI enforces it via pnpm har:check (re-runs the sanitizer and scans for tokens — see scripts/check-har-sanitized.ts). Extend @languages-learner/har-sanitizer if a new field ever leaks.

CI

The reusable workflow .github/workflows/e2e-tests.yml runs test:integration on pull requests, after the staging deploy, only when the web project is affected (gated on the check-deploy-affected job). It passes the freshly-deployed stage URL as BASE_URL and feeds the test account from the TEST_USER_AUTH_LOGIN / TEST_USER_AUTH_PASSWORD repository secrets (mapped to AUTH_LOGIN / AUTH_PASSWORD). On failure the Playwright HTML report is uploaded as an artifact for inspecting screenshot diffs.

When the UI changes, regenerate the baselines with the Docker flow above (test:integration:update:docker) and commit the updated *-snapshots/*.png in the same PR.