Skip to content

Architecture

Your test (unchanged Dusk body)


Dawn\Browser ─ Dusk's fluent API, one class per concern trait
        │        (elements, mouse, waits, assertions, URLs, auth, JS)

Dawn\ElementResolver ─ Dusk selector semantics → Playwright locators


playwright-php/playwright ─ PHP ↔ Node JSON-RPC over stdio


Playwright (Node) ─ Chromium / Firefox / WebKit

The pieces

Dawn\Browser holds a Playwright Page and an ElementResolver carrying the current scope prefix. Every fluent method translates to locator/page calls; unknown methods hit __call, try registered macros (Dawn's Browser is Macroable, like Dusk's), then throw UnsupportedDuskMethod.

Dawn\ElementResolver is pure translation - it never touches the DOM. @name[dusk="name"], page-element aliases, scope prefixes, and Dusk's field/button candidate orders compiled into CSS selector lists. Locators resolve at action time, so Playwright's auto-wait covers the find as well as the act.

Dawn\Engine\Engine owns the lifecycle: one Node sidecar and one launched browser per PHP process, a browser context per Browser instance, everything shut down at process exit.

Dawn\TestCase / ProvidesBrowser mirror Dusk's lifecycle: browse() with as many Browser arguments as your closure declares, the primary browser persisted across tests in a class, failure screenshots and console logs captured automatically.

Dawn\DawnServiceProvider registers the /_dawn/* auth routes (non-production only).

src/compat.php aliases Laravel\Dusk\BrowserDawn\Browser eagerly (PHP skips autoloading for parameter type checks) and the base classes lazily, only when laravel/dusk is absent.

Design rules

  1. No WebDriver. No Facebook\WebDriver types, no W3C endpoint.
  2. No PHP-side waiting - see How waiting works. CI greps the codebase to enforce it; Dawn\Support\Waiter (backing waitUsing()) is the single sanctioned exception.
  3. Dusk is the spec. Resolution orders, assertion messages, timeout message formats and route mechanics are copied from laravel/dusk source; divergences are documented, never silent.
  4. Point-in-time assertions. Reads (innerText, values, checked state) execute immediately via evaluate(), exactly like Dusk's getText() - waiting stays in the explicit waitFor* calls where your suite already puts it.

Released under the MIT License. Not affiliated with Laravel.