Replace Dusk with Pest 4 browser testing (Playwright) #54

Closed
opened 2026-08-18 23:51:16 +02:00 by myrmidex · 0 comments
Owner

Why

Laravel's own documentation now steers new browser testing away from Dusk:

"Pest 4 now includes automated browser testing which offers significant performance and usability improvements compared to Laravel Dusk. For new projects, we recommend using Pest for browser testing."
https://laravel.com/docs/13.x/dusk

Dusk is not deprecated and stays supported, but the direction has moved.

Three reasons this matters here specifically:

  1. Auto-waiting. Dusk makes you hand-tune waits — DuskTestCase currently carries TIMEOUT_SHORT = 2, TIMEOUT_MEDIUM = 3, PAUSE_SHORT = 500, PAUSE_MEDIUM = 1000. Playwright auto-waits on every interaction, which removes most of that class of flakiness. This app is Blade + Livewire + Alpine, so nearly every assertion follows a wire: round-trip — precisely where fixed pauses are fragile.
  2. Visible verification. pest --headed opens a real browser window so a fix can be watched end-to-end, and pest --debug pauses on failure with the browser open. This is wanted for verifying bug fixes rather than trusting a green assertion.
  3. Speed. Playwright drives over CDP rather than WebDriver, and Pest supports --parallel and --shard.

Migration size

11 browser tests across four areas:

area tests
tests/Browser/Auth/ 1
tests/Browser/Dishes/ 6
tests/Browser/Schedule/ 2
tests/Browser/Users/ 1
tests/Browser/RedirectTest.php 1

None use RefreshDatabase. This is a real port, not a trivial one — but still small enough to be worth doing before browser coverage grows further.

Scope

Install

composer require pestphp/pest-plugin-browser --dev
npm install playwright@latest
npx playwright install

Pest is a wrapper around PHPUnit, not a replacement — existing PHPUnit test cases are reused as-is, so the existing PHPUnit suite keeps working. Installation does involve composer remove phpunit/phpunit (Pest pulls its own PHPUnit 12), so the dependency swap needs care.

Port all 11 tests in tests/Browser/ to Pest browser syntax, including the LoginHelpers.php shared helper.

Remove Dusk once the ports pass: laravel/dusk from composer, tests/DuskTestCase.php, phpunit.dusk.xml, .env.dusk.local, and the selenium service from the compose file. Also clean up the tests/Browser/console, screenshots, and source artifact directories Dusk generates.

CI.forgejo/workflows/ci.yml builds a PHP-only image with no Node. Playwright needs Node plus browser binaries, so the CI image or workflow needs extending. Scope this before starting; it may be the largest part of the work.

shell.nix — add a helper for headed runs.

Verify phpunit.xml survives

phpunit.xml was just hardened in #53 with <server> + force="true" on every value, which is what stops tests from wiping the dev database. Confirm those still apply under Pest, and that a run with APP_ENV=local in the environment still resolves to sqlite :memory:.

Note that Dusk's .env.dusk.local uses real MySQL (dishplanner_test), so it was never covered by #53's fix — removing Dusk also removes that exposure.

Known limitations, accepted

Pest's plugin is a thin wrapper over Playwright and does not expose everything raw Playwright does:

  • No network interception / request mocking (page.route()). Not needed here — server-rendered Livewire (Blade + 4 Livewire components + Alpine, no React/Vue), and Http::fake() covers third-party calls at the PHP layer.
  • No video recording or trace viewer. Screenshots (full-page and per-element) are supported.

What it does cover: click, type, select, check, radio, file upload, keyboard with modifiers, hover, drag, iframes, resize; Chrome/Firefox/Safari; mobile viewports and device presets; dark mode; geolocation; accessibility scanning; console-error assertions; ARIA assertions.

Out of scope

  • Converting the existing PHPUnit suite to Pest syntax. pest-plugin-drift can do it later if wanted; nothing forces it.
  • Expanding browser coverage to untested flows — tracked separately.

Acceptance

  • pest-plugin-browser and Playwright installed
  • All 11 browser tests ported and passing
  • pest --headed opens a visible browser locally
  • Dusk, Selenium service, phpunit.dusk.xml, .env.dusk.local, and Dusk artifact dirs removed
  • CI runs browser tests green
  • Full suite still passes
  • A run with APP_ENV=local set still uses sqlite :memory: and leaves the dishplanner database untouched
## Why Laravel's own documentation now steers new browser testing away from Dusk: > "Pest 4 now includes automated browser testing which offers significant performance and usability improvements compared to Laravel Dusk. **For new projects, we recommend using Pest for browser testing.**" > — https://laravel.com/docs/13.x/dusk Dusk is not deprecated and stays supported, but the direction has moved. Three reasons this matters here specifically: 1. **Auto-waiting.** Dusk makes you hand-tune waits — `DuskTestCase` currently carries `TIMEOUT_SHORT = 2`, `TIMEOUT_MEDIUM = 3`, `PAUSE_SHORT = 500`, `PAUSE_MEDIUM = 1000`. Playwright auto-waits on every interaction, which removes most of that class of flakiness. This app is Blade + Livewire + Alpine, so nearly every assertion follows a `wire:` round-trip — precisely where fixed pauses are fragile. 2. **Visible verification.** `pest --headed` opens a real browser window so a fix can be watched end-to-end, and `pest --debug` pauses on failure with the browser open. This is wanted for verifying bug fixes rather than trusting a green assertion. 3. **Speed.** Playwright drives over CDP rather than WebDriver, and Pest supports `--parallel` and `--shard`. ## Migration size **11 browser tests** across four areas: | area | tests | |---|---| | `tests/Browser/Auth/` | 1 | | `tests/Browser/Dishes/` | 6 | | `tests/Browser/Schedule/` | 2 | | `tests/Browser/Users/` | 1 | | `tests/Browser/RedirectTest.php` | 1 | None use `RefreshDatabase`. This is a real port, not a trivial one — but still small enough to be worth doing before browser coverage grows further. ## Scope **Install** ``` composer require pestphp/pest-plugin-browser --dev npm install playwright@latest npx playwright install ``` Pest is a wrapper around PHPUnit, not a replacement — existing PHPUnit test cases are reused as-is, so the existing PHPUnit suite keeps working. Installation does involve `composer remove phpunit/phpunit` (Pest pulls its own PHPUnit 12), so the dependency swap needs care. **Port** all 11 tests in `tests/Browser/` to Pest browser syntax, including the `LoginHelpers.php` shared helper. **Remove** Dusk once the ports pass: `laravel/dusk` from composer, `tests/DuskTestCase.php`, `phpunit.dusk.xml`, `.env.dusk.local`, and the `selenium` service from the compose file. Also clean up the `tests/Browser/console`, `screenshots`, and `source` artifact directories Dusk generates. **CI** — `.forgejo/workflows/ci.yml` builds a PHP-only image with no Node. Playwright needs Node plus browser binaries, so the CI image or workflow needs extending. **Scope this before starting; it may be the largest part of the work.** **`shell.nix`** — add a helper for headed runs. ## Verify `phpunit.xml` survives `phpunit.xml` was just hardened in #53 with `<server>` + `force="true"` on every value, which is what stops tests from wiping the dev database. Confirm those still apply under Pest, and that a run with `APP_ENV=local` in the environment still resolves to sqlite `:memory:`. Note that Dusk's `.env.dusk.local` uses real MySQL (`dishplanner_test`), so it was never covered by #53's fix — removing Dusk also removes that exposure. ## Known limitations, accepted Pest's plugin is a thin wrapper over Playwright and does not expose everything raw Playwright does: - **No network interception / request mocking** (`page.route()`). Not needed here — server-rendered Livewire (Blade + 4 Livewire components + Alpine, no React/Vue), and `Http::fake()` covers third-party calls at the PHP layer. - **No video recording or trace viewer.** Screenshots (full-page and per-element) are supported. What it does cover: click, type, select, check, radio, file upload, keyboard with modifiers, hover, drag, iframes, resize; Chrome/Firefox/Safari; mobile viewports and device presets; dark mode; geolocation; accessibility scanning; console-error assertions; ARIA assertions. ## Out of scope - Converting the existing PHPUnit suite to Pest syntax. `pest-plugin-drift` can do it later if wanted; nothing forces it. - Expanding browser coverage to untested flows — tracked separately. ## Acceptance - [ ] `pest-plugin-browser` and Playwright installed - [ ] All 11 browser tests ported and passing - [ ] `pest --headed` opens a visible browser locally - [ ] Dusk, Selenium service, `phpunit.dusk.xml`, `.env.dusk.local`, and Dusk artifact dirs removed - [ ] CI runs browser tests green - [ ] Full suite still passes - [ ] A run with `APP_ENV=local` set still uses sqlite `:memory:` and leaves the `dishplanner` database untouched
myrmidex added this to the v0.9.0 milestone 2026-08-18 23:51:16 +02:00
myrmidex added the
enhancement
testing
labels 2026-08-18 23:51:16 +02:00
myrmidex added a new dependency 2026-08-18 23:54:04 +02:00
myrmidex added a new dependency 2026-08-18 23:54:07 +02:00
myrmidex added a new dependency 2026-08-18 23:54:12 +02:00
myrmidex added a new dependency 2026-08-18 23:54:14 +02:00
myrmidex added a new dependency 2026-08-18 23:54:21 +02:00
myrmidex self-assigned this 2026-08-19 00:45:19 +02:00
Sign in to join this conversation.
No description provided.