contao / e2e-test-bundle
End-to-end testing support for isolated Contao Managed Editions
Fund package maintenance!
Requires
- php: ^8.2
- contao/installation-recipe: dev-main
- doctrine/dbal: ^3.6 || ^4.0
- phpunit/phpunit: ^10.5 || ^11.5 || ^12.0 || ^13.0
- symfony/browser-kit: ^6.4 || ^7.0 || ^8.0
- symfony/console: ^6.4 || ^7.0 || ^8.0
- symfony/dom-crawler: ^6.4 || ^7.0 || ^8.0
- symfony/filesystem: ^6.4 || ^7.0 || ^8.0
- symfony/http-client: ^6.4 || ^7.0 || ^8.0
- symfony/panther: ^2.1
- symfony/process: ^6.4 || ^7.0 || ^8.0
This package is auto-updated.
Last update: 2026-08-05 12:58:20 UTC
README
contao/e2e-test-bundle prepares a real Contao Managed Edition, migrates an isolated MySQL/MariaDB database, loads installation recipes, and exposes raw HTTP and Panther clients. It does not require a Contao bundle itself, so the test suite selects the Contao version in its recipe.
If Docker is available, no database setup is needed. The first test starts a reusable mariadb:11.4 container on a random loopback port; subsequent runs reuse it. Its /var/lib/mysql directory is bind-mounted to .contao-e2e/database/data, so all generated database files remain inside the project-local E2E workspace.
Select a database explicitly in the PHPUnit configuration when an extension supports a particular database range:
use Contao\E2eTestBundle\Database\DockerDatabaseConfig; $mariaDb = $config->withDatabase(DockerDatabaseConfig::mariaDb('mariadb:10.11')); $mysql = $config->withDatabase(DockerDatabaseConfig::mysql('mysql:8.0'));
Different types and image versions use independent reusable containers and storage directories. This makes those configurations suitable for a PHPUnit data provider or separate CI jobs. A CI matrix can configure the same tests without changing PHP code:
CONTAO_E2E_DATABASE_TYPE=mysql CONTAO_E2E_DATABASE_IMAGE=mysql:8.0 composer e2e-tests CONTAO_E2E_DATABASE_TYPE=mariadb CONTAO_E2E_DATABASE_IMAGE=mariadb:10.11 composer e2e-tests
An administrative database URL that may create test databases overrides Docker:
export CONTAO_E2E_DATABASE_URL='mysql://root:password@127.0.0.1:3306'
Use the trait with PHPUnit 10 through 13; no test base class is imposed:
use Contao\E2eTestBundle\ManagedEdition\ManagedEditionConfig; use Contao\E2eTestBundle\ManagedEdition\ManagedEditionTestTrait; use Contao\InstallationRecipe\Composer\ComposerConfig; use Contao\InstallationRecipe\Recipe\InstallationRecipe; use PHPUnit\Framework\TestCase; final class LoginTest extends TestCase { use ManagedEditionTestTrait; protected static function createManagedEditionConfig(): ManagedEditionConfig { $composer = ComposerConfig::managedEdition('^5.7') ->withPathPackage('acme/example-bundle', dirname(__DIR__), '1.0.x-dev'); return ManagedEditionConfig::create( InstallationRecipe::create($composer)->withFixtureFile(__DIR__.'/fixtures.yaml'), dirname(__DIR__), ); } public function testLoginPage(): void { $client = self::managedEdition()->createFirefoxClient(); $client->request('GET', '/contao/login'); self::assertSelectorTextContains('body', 'Contao'); } }
Without an origin, Panther uses the local E2E server URI directly so that absolute redirects and cookies stay on the same browser origin. Pass Origin::http('example.test') or Origin::https('example.test') when a test must emulate a page DNS entry or HTTPS; the server maps that origin without requiring a real domain or certificate.
Each consumer project gets one ignored .contao-e2e/ workspace. Dependency, application, and fixture fingerprints are separate: unchanged Composer input reuses vendor/; source or configuration changes rerun setup and migrations; fixture-only changes only reset and reload the database. Parallel processes acquire separate installation and database slots.
ManagedEdition::resetDatabase() returns a FixtureResult. Call $result->value('page_home') to obtain the generated primary key of a named fixture, or pass a second column name to read another resolved value. $result->interpolate('/pages/{page_home}') substitutes generated values in paths or other strings.
For monorepos, MonorepoProject discovers an explicit root package version or the dev-main branch alias and falls back
to dev-main when neither exists. It also reads the package names from local composer.json files:
use Contao\E2eTestBundle\Composer\MonorepoProject; $monorepo = MonorepoProject::discover(dirname(__DIR__)); $composer = $monorepo->configureComposer( ComposerConfig::managedEdition('^5.7'), 'packages/example-bundle', );
For HTTP tests without JavaScript, use Symfony's BrowserKit client. It returns a DomCrawler instance and supports links, forms, cookies, history, and access to the last response:
$browser = self::managedEdition()->createHttpBrowser(Origin::https('example.test')); $crawler = $browser->request('GET', '/'); self::assertSame(200, $browser->getInternalResponse()->getStatusCode()); self::assertSame('Example', trim($crawler->filterXPath('//head/title')->text()));
Full Managed Editions are stored below .contao-e2e/cache/installations/<fingerprint>/<slot>/project. The matching
MySQL or MariaDB database runs in the configured server or a reusable Docker container. The default database files are stored below .contao-e2e/database/data; additional image variants use .contao-e2e/database/<fingerprint>/data. The runtime/ directory only contains
the lightweight webserver router and origin mapping.
CONTAO_E2E_DIRECTORY overrides the workspace, and CONTAO_E2E_NO_CACHE=1 forces a fresh dependency installation. The contao-e2e executable is a Symfony Console application; run vendor/bin/contao-e2e list for all commands. cache:clear safely clears reusable installations, while database:stop stops every database variant belonging to the current project.