Files
pl_mail/.github/workflows/e2e.yml
T
Paul LütznerandClaude Opus 5 71d5474b5d Set the Node version in one place
.nvmrc said 22, the CI workflow pinned 20, and the machine the browser
suite was actually developed and run on had 24. Three numbers, none of
them agreeing, which means "passes locally, fails in CI" had three
plausible explanations before anyone looked at the code.

.nvmrc is now the single source — CI reads it with node-version-file —
and it says 24, which is what the suite has been passing on.
package.json's engines floor stays at >=18: that is a genuine minimum
rather than a choice, and there is no reason to raise it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 15:45:26 +02:00

170 lines
6.9 KiB
YAML

name: E2E
on:
push:
pull_request:
jobs:
playwright:
name: Playwright
runs-on: ubuntu-latest
env:
APP_ENV: test
APP_DEBUG: "1"
DATABASE_URL: "postgresql://app:app@127.0.0.1:5432/app?serverVersion=18&charset=utf8"
MESSENGER_TRANSPORT_DSN: "in-memory://"
MAILER_DSN: "null://null"
APP_DEV_USER_EMAIL: "e2e@plmail.test"
APP_DEV_USER_PASSWORD: "e2e-password-change-me"
services:
postgres:
image: postgres:18
env:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U app"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup PHP extension cache
id: php-extension-cache
uses: shivammathur/cache-extensions@v1
with:
php-version: "8.4"
extensions: ctype, iconv, pcntl, pdo_pgsql, pgsql, intl, sodium, mbstring, xml, curl, zip
key: php-extensions-v1
- name: Cache PHP extensions
uses: actions/cache@v4
with:
path: ${{ steps.php-extension-cache.outputs.dir }}
key: ${{ steps.php-extension-cache.outputs.key }}
restore-keys: ${{ steps.php-extension-cache.outputs.key }}
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: ctype, iconv, pcntl, pdo_pgsql, pgsql, intl, sodium, mbstring, xml, curl, zip
coverage: none
tools: composer:v2
- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-${{ hashFiles('**/composer.lock') }}
restore-keys: composer-
- name: Install PHP dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: Install Symfony CLI
run: curl -sS https://get.symfony.com/cli/installer | bash && sudo mv ~/.symfony*/bin/symfony /usr/local/bin/symfony
- name: Setup Node
uses: actions/setup-node@v4
with:
# From .nvmrc rather than pinned here. The two drifted —
# .nvmrc said 22, this said 20, and the machine the suite was
# actually developed on ran 24 — so "works locally, fails in
# CI" had three plausible causes instead of none.
node-version-file: .nvmrc
cache: npm
- name: Install Node dependencies
run: npm ci
- name: Resolve Playwright version
id: playwright-version
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
# Browsers came from cache; the apt libs they link against did not.
- name: Install Playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
# Tailwind scans the templates, so they belong in the key alongside
# the importmap and the sources themselves.
- name: Cache frontend build
id: frontend-cache
uses: actions/cache@v4
with:
path: |
public/assets
assets/vendor
key: frontend-${{ runner.os }}-${{ hashFiles('importmap.php', 'package-lock.json', 'assets/**', 'templates/**') }}
- name: Build frontend assets
if: steps.frontend-cache.outputs.cache-hit != 'true'
run: |
php bin/console importmap:install
php bin/console tailwind:build
# Compiled rather than served through PHP. Without this every
# asset request falls through to a full Symfony kernel boot —
# /login alone pulls 56 of them, and Playwright gives every
# test a cold cache. It was about 40% of the whole suite.
php bin/console asset-map:compile
- name: Prepare database
run: |
php bin/console doctrine:database:create --if-not-exists --no-interaction
php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
# The Playwright suite seeds its own per-worker users, so this is
# only what PHPUnit needs: the default user, and the admin the
# controller tests look for. That admin used to be created by a
# browser spec, which meant PHPUnit silently skipped ~50 tests
# whenever it ran first — which is exactly the order used here.
- name: Seed fixture users
run: |
php bin/console app:test:seed-user
php bin/console app:test:seed-user --admin --email=e2e-admin@plmail.test --password=e2e-admin-password
# Needs the compiled test container for phpstan-symfony to resolve
# service ids, and a booted kernel for the Doctrine metadata — both
# of which the seeding above has already produced.
- name: Run PHPStan
run: composer stan
# Fast and dependency-free, so it runs before the browser suite:
# a failure here is far quicker to read than a Playwright trace.
- name: Run PHPUnit tests
run: composer test
- name: Run Playwright tests
run: npx playwright test
- name: Upload Playwright report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 14