# 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.
