Add comprehensive test coverage for core site features (content, navigation, language, FAQ, news, petition, newsletter) using Pest browser tests and unit tests for custom plugins. Includes test infrastructure (Containerfile.test, compose.test.yaml), test documentation, and test files covering petition form logic, CSV handling, translation, date formatting, rate limiting, and map data building.
98 lines
3.4 KiB
Markdown
98 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.
|