innhold/tests/README.md
Ruben b8e6d2537d Add browser and unit test suite with Pest + Playwright
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.
2026-03-17 16:43:39 +01:00

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.