End-to-end and integration tests for the Languages Learner web app.
Exercises apps/web as a whole — browser-level flows and integration tests — separate from the
unit tests that live inside individual packages and the Playwright component tests in uikit. In
this repo "integration tests" are full-page screenshot (visual regression) tests: each spec
opens a page and calls the expectScreenshot fixture, which captures the page in both light and
dark themes and compares against committed -linux baselines.
Not a library — it defines the test:integration target (and test:integration:update for
refreshing baselines). It does not export anything for other packages to import.
integration/playwright.config.ts— Playwright config.testDirisintegration/tests; aglobal_setupproject logs in once and persists a storage state, on which thechromiumproject depends. Snapshots are stored as…-{theme}-chromium-linux.pngnext to each spec.integration/tests/**— the specs (landing,office,dictionary) and their*-snapshots/.pages/**— Page Object Models; they navigate viapage.goToWithLocale(...)and reuse the web app's route constants from@/shared/routes(→apps/web/src/ui/shared/routes).core/**— shared setup (commonSetup,login) and barrels. The two barrels export differenttestobjects on purpose:core/index.tsre-exports the plaintestfromapp-core-tests-utils(used byglobal.setupfor a real login, no network mocking), whileintegration/core/index.tsre-exports thetestfromapp-integration-tests-utils, which adds the automockNetworkfixture.- Each spec's
*-snapshots/folder also holds committed…-chromium-linux.har(+.datbodies) next to the PNGs: themockNetworkfixture records/replays backend traffic (<baseURL>/api/**) throughrouteFromHARand sanitizes the HAR (har-sanitizer) in anafterAllhook, so screenshots don't depend on live backend data. BecauserouteFromHARmatches by exact URL and the/apiorigin varies between local recording and the dynamic CI stage domain, the committed HAR is host-normalized tohttp://mocked.local(via the fixture'surlsToReplace) and rewritten back to the livebaseURLin a temp*.replay.haron replay.
Path aliases (see tsconfig.json): @/tests/app/* → package root, @/shared/* → the web app's
src/ui/shared, @@/* → repo root.
@languages-learner/app-core-tests-utils— the plaintestfixture (expectScreenshot+goToWithLocale), used for login/global.setup.@languages-learner/app-integration-tests-utils— the integrationtest(core +mockNetwork), used by the specs; records/replays and sanitizes the backend (/api) HAR. Committed HARs are kept secret-free byhar-sanitizer, enforced in CI bypnpm har:check.@languages-learner/playwright-utils— auth-storage helpers and fixture factories.@playwright/test,dotenv.
- Nothing imports it; it is a test app.
- CI: the reusable
.github/workflows/e2e-tests.ymlrunstest:integrationon pull requests, after the staging deploy, gated oncheck-deploy-affected(i.e. whenapp-web/app-backendis affected). It receives the freshly-deployed stage URL asBASE_URLand the test account from theTEST_USER_AUTH_LOGIN/TEST_USER_AUTH_PASSWORDsecrets (mapped toAUTH_LOGIN/AUTH_PASSWORDat run time).
global_setupperforms a real UI login against the staging Supabase, so a seeded, confirmed test user must exist. Locally, provideBASE_URL,AUTH_LOGIN,AUTH_PASSWORDviaintegration/.env(seeintegration/.env.example) and pointBASE_URLat a runningapps/web.- Screenshot baselines are Linux-rendered (
…-linux.png, hardcoded in the config). Regenerate them with the Docker flowtest:integration:update:docker(which mirrors theuikitcomponent-test setup: a CI-matched Ubuntu image viaDockerfile.tests.component), not a nativetest:integration:updateon Windows/macOS, whose font rendering differs from the CI runner. The container reaches the natively-runningapps/webthroughhost.docker.internal— see the README. - API-dependent flows need a real Supabase project or route mocks (e.g.
dictionary.spec.tsstubsrest/v1/wordsviapage.route) — the public tree ships no turnkey backend or DB migrations.
- Scripts and how to run: README
- Test utilities: playwright-utils, app-core-tests-utils