99 lines
3.4 KiB
Markdown
99 lines
3.4 KiB
Markdown
|
|
# Test Suite
|
||
|
|
|
||
|
|
Browser tests using [Pest](https://pestphp.com/) + [pest-plugin-browser](https://github.com/pestphp/pest-plugin-browser) (Playwright/Chromium).
|
||
|
|
|
||
|
|
## Run tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
podman compose -f compose.test.yaml run --rm test
|
||
|
|
```
|
||
|
|
|
||
|
|
The `app` service (the site) starts automatically via `depends_on`. Tests run against `http://app.dns.podman` inside the Podman network.
|
||
|
|
|
||
|
|
## Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
tests/
|
||
|
|
composer.json # pestphp/pest ^4, pestphp/pest-plugin-browser ^4
|
||
|
|
Pest.php # defines BASE_URL and CUSTOM_DIR constants; groups unit tests
|
||
|
|
Browser/ # browser tests (require running app + Playwright)
|
||
|
|
HomepageTest.php
|
||
|
|
NavigationTest.php
|
||
|
|
LanguageTest.php
|
||
|
|
PetitionTest.php
|
||
|
|
Screenshots/ # auto-saved on failure (gitignored)
|
||
|
|
Unit/ # unit tests (pure PHP, no browser needed)
|
||
|
|
PetitionFormTest.php
|
||
|
|
```
|
||
|
|
|
||
|
|
## Writing unit tests
|
||
|
|
|
||
|
|
Place tests in `tests/Unit/`. The constant `CUSTOM_DIR` points to `custom/` on the host.
|
||
|
|
|
||
|
|
Since `custom/` plugins are written as framework plugins (they call `Hooks::add()` and reference `Context`), stub those classes before requiring the file:
|
||
|
|
|
||
|
|
```php
|
||
|
|
<?php
|
||
|
|
class Context {}
|
||
|
|
class Hook { const TEMPLATE_VARS = 'template_vars'; }
|
||
|
|
class Hooks { public static function add(string $hook, callable $fn): void {} }
|
||
|
|
|
||
|
|
require_once CUSTOM_DIR . '/plugins/page/my-plugin.php';
|
||
|
|
|
||
|
|
it('does something', function () {
|
||
|
|
expect(myFunction('input'))->toBe('expected');
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Only test functions from `custom/` — `app/` has its own test suite.
|
||
|
|
|
||
|
|
## Writing browser tests
|
||
|
|
|
||
|
|
All browser tests use `$this` for the Playwright page object via pest-plugin-browser.
|
||
|
|
|
||
|
|
```php
|
||
|
|
it('describes behavior', function () {
|
||
|
|
$this->visit(BASE_URL . '/path')
|
||
|
|
->assertSee('text') // visible text
|
||
|
|
->assertSourceHas('html snippet') // raw HTML/source
|
||
|
|
->assertPresent('css selector') // element exists in DOM
|
||
|
|
->click('css selector')
|
||
|
|
->press('input[type="submit"]') // click button/submit
|
||
|
|
->assertSee('expected result');
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
Place new test files in `tests/Browser/`. No registration needed — Pest autodiscovers `*Test.php` files.
|
||
|
|
|
||
|
|
**Reference:** [pestphp.com/docs/browser-testing](https://pestphp.com/docs/browser-testing) — full list of interactions (`type()`, `select()`, `check()`, `drag()`, …) and assertions (`assertUrlIs()`, `assertAttribute()`, `assertNoJavaScriptErrors()`, …).
|
||
|
|
|
||
|
|
## Infrastructure
|
||
|
|
|
||
|
|
- **Containerfile.test**: `php:8.4-cli-bookworm` + Node 22 + Composer + Playwright Chromium at `/opt/playwright`
|
||
|
|
- **compose.test.yaml**: `app` service = the site; `test` service = runner; mounts `./tests` as volume so edits apply without rebuild
|
||
|
|
- **Pest.php**: `BASE_URL` defaults to `http://app` if `APP_URL` not set
|
||
|
|
|
||
|
|
## Rebuild image
|
||
|
|
|
||
|
|
Only needed after changing `Containerfile.test` or `composer.json`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
podman compose -f compose.test.yaml build test
|
||
|
|
```
|
||
|
|
|
||
|
|
## Agent workflow
|
||
|
|
|
||
|
|
When modifying or adding code in `custom/` or `content/`:
|
||
|
|
|
||
|
|
1. Run the existing test suite first to establish a baseline.
|
||
|
|
2. Make your changes.
|
||
|
|
3. Run tests again. All previously passing tests must still pass.
|
||
|
|
4. If your change adds new behavior, add a test for it in `tests/Browser/`.
|
||
|
|
5. Do not mark work done until the test suite passes (excluding pre-existing known failures listed below).
|
||
|
|
|
||
|
|
Failed tests save screenshots to `tests/Browser/Screenshots/` — read them to diagnose rendering issues.
|
||
|
|
|
||
|
|
## Known failures
|
||
|
|
|
||
|
|
None.
|