Files
pl_mail/package.json
T
Paul Lützner 56606f2c78 ci(e2e): the suite runs against the FrankenPHP the app ships on
CI served the browser suite with `php -S` on the runner, behind a router
script written for the occasion, with PHP and its extensions installed
beside it. Every accommodation in that workflow existed because of that
server and not because of the app: a php.ini copied into the scan dir with
sudo, PHP_CLI_SERVER_WORKERS, a router so index.php did not answer 56 asset
requests with 56 kernel boots, a copy of the environment into $_SERVER
because the cli-server SAPI provides none, and finally a restart loop
because the thing segfaulted mid-suite. A green run proved something about
a server no user will ever meet, while PHPUnit ran bare on the runner in
the same job that every developer ran in a container.

It now boots compose.test.yaml — the app's own image, from this
repository's Dockerfile — and runs `npm run test:unit:docker` and the
Playwright suite against it. Local and CI are one invocation.

Three things had to be true first.

app-init could not bootstrap a checkout that had never seen composer. It
overrides the image entrypoint to get the ordering right around the
database, and that entrypoint is also what installs dependencies into the
bind mount, so a fresh clone — or a worktree, or a CI runner — died on
"Dependencies are missing" before the stack came up at all. It installs
them itself now when vendor/ is absent.

admin-queue.spec.ts and mercure.spec.ts each carried a private copy of
`docker compose -f compose.test.yaml` with no -p, so both silently
addressed the DEFAULT project. Point the suite at a stack under any other
project name and the queue seed populated a database the browser was not
looking at, while the hub stop that mercure.spec.ts uses to prove the
stream recovers stopped a project that did not exist — which compose
reports as success. That is the whole of why admin-queue was believed to
need a running worker: it never did. `delivered_at` is what "a worker is
holding it" means to the transport, and app:test:seed-queue writes it.
Both now go through E2E_COMPOSE, resolved once in playwright.config.ts.

The image needed a stable tag so CI could build it ahead of time with a
layer cache, which is why compose.test.yaml names it rather than leaving
it to `<project>-<service>`.

Deleted: the webServer block, tests/e2e/support/router.php, the Postgres
service container, setup-php and its extension cache, the php.ini scan-dir
step, and the separate migrate/seed/asset steps the stack already does.
The server log still goes up with the report — from `docker compose logs`
now, and covering app-init and Postgres too, because when the failure is
that the stack never came up that is the only place the reason exists.

Verified against a compose stack from this worktree: PHPStan clean,
PHPUnit 2820 tests / 6979 assertions green, Playwright 253 passed and
0 failed — admin-queue and both mercure recovery tests among them.
2026-08-10 16:17:28 +02:00

33 lines
1.7 KiB
JSON

{
"name": "plmail-e2e",
"private": true,
"description": "End-to-end tests for plMail (Playwright). Separate from the app's AssetMapper/importmap frontend.",
"scripts": {
"test:env:up": "docker compose -f compose.test.yaml up -d --build --wait app",
"test:env:down": "docker compose -f compose.test.yaml down",
"test:env:reset": "docker compose -f compose.test.yaml down -v",
"test:env:logs": "docker compose -f compose.test.yaml logs -f app",
"test:unit:docker": "docker compose -f compose.test.yaml run --rm --no-deps --entrypoint docker-php-entrypoint app php vendor/bin/phpunit",
"test:e2e:docker": "npm run test:env:up && playwright test",
"test:e2e:docker:ui": "npm run test:env:up && playwright test --ui",
"test:e2e:docker:headed": "npm run test:env:up && playwright test --headed",
"test:e2e:docker:trace": "npm run test:env:up && playwright test --trace on --video on",
"test:e2e": "playwright test",
"test:e2e:site": "php bin/build-site.php && E2E_SITE=1 playwright test site.spec.ts",
"test:e2e:screenshots": "E2E_SCREENSHOTS=1 playwright test screenshots.spec.ts",
"test:e2e:coverage": "rm -rf var/js-coverage && E2E_JS_COVERAGE=1 playwright test && node bin/js-coverage.mjs",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed",
"test:e2e:report": "playwright show-report",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"engines": {
"node": ">=18"
},
"devDependencies": {
"@playwright/test": "^1.55.0",
"@types/node": "^22.10.0",
"typescript": "^5.6.0"
}
}