Continuous integration
Run Symfony browser tests in a dedicated CI job with explicit browser installation, selection, and failure artifacts.
The in-process bundle removes the application-server step, but CI still needs PHP dependencies, the Playwright driver, browser binaries, test data, and failure evidence.
Keep the browser suite in a dedicated job. That makes its runtime visible and prevents a normal unit-test command from launching a browser unexpectedly.
A minimal GitHub Actions job
name: Browser tests
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
- uses: playwright-php/setup-playwright@v1
with:
playwright-version: '1.62.1'
browsers: chromium
- run: composer install --prefer-dist --no-interaction --no-progress
- run: vendor/bin/phpunit tests/E2E
The setup action installs the selected browser. For a shell-only Chromium setup, run vendor/bin/playwright-install --with-deps chromium after Composer installation.
Keep configuration explicit
Use a stable test base URL such as http://localhost, and include its host in intercepted_hosts. A network port does not need to be opened for in-process kernel routing.
Run one engine first. Add Firefox or WebKit jobs only for risks that need cross-engine coverage. A full application suite multiplied across three engines can be expensive without adding proportional confidence.
Prepare and clean application state
Create the test database and run migrations before the browser suite when the application needs them. The bundle does not reset database state. Use deterministic factories or the project's normal test reset tool.
External services still need CI-safe configuration because non-intercepted hosts use real browser networking and server-side HTTP calls remain server-side.
Preserve failure evidence
Configure traces, screenshots, or video where they help, then upload the artifact directory when the test step fails. Also retain application logs when the failure came from the kernel.
See the core Continuous integration guide for runner strategy and Profiler and debugging for Symfony-specific evidence.