dusk
End-to-end browser testing with Laravel Dusk. Used exclusively for full user flows requiring a real Chrome browser — JavaScript interactions, multi-step forms, and visual assertions.
SKILL.mdresources/boost/skills/dusk/SKILL.md
GitHubDusk (Browser Testing)
When to Use
- End-to-end flows requiring a real browser and JavaScript execution.
- Multi-step forms, modal interactions, and client-side validation states.
- Regression testing for key UI journeys before release.
When Not to Use
- Unit-level business logic validation (use unit tests).
- Controller/API contract validation without browser behavior (use feature/API tests).
- Fast feedback checks where browser startup cost is unnecessary.
Preconditions
- Laravel Dusk is installed and browser tests run from
tests/Browser/. - Chrome/Chromium and compatible driver are available on the test environment.
.env.dusk.localis configured with isolated test DB and app URL.- Test data setup strategy uses
DatabaseTruncationorDatabaseMigrations, neverRefreshDatabase.
Process Checklist
- Choose Dusk only if browser execution is required.
- Add/verify
dusk=""selectors for all interactive elements under test. - Use
loginAs()to bypass repetitive auth UI unless auth flow is under test. - Replace arbitrary waits with
waitFor*assertions. - Call
assertNoJavaScriptErrors()after eachvisit(). - Extract repeated navigation/selectors into Page Objects or Dusk Components.
Rules
- Browser tests live in
tests/Browser/. - Use
DatabaseTruncation(preferred) orDatabaseMigrations; neverRefreshDatabase. - Use only
dusk=""selectors for test targeting. - Do not use
pause(); wait for explicit UI state transitions.
Examples
uses(DatabaseTruncation::class);
it('user can submit an invoice', function () {
// Arrange
$user = User::factory()->create();
// Act + Assert
$this->browse(function (Browser $browser) use ($user) {
$browser->loginAs($user)
->visit('/invoices/create')
->assertNoJavaScriptErrors()
->type('@amount-input', '100')
->press('@submit-button')
->waitForText('Invoice created')
->assertSee('Invoice created');
});
});
<!-- Blade/HTML — use dusk attributes, not CSS classes -->
<button dusk="submit-invoice">Submit</button>
<input dusk="amount-input" type="number" name="amount">
// Page Object
class InvoicePage extends Page
{
public function __construct(private readonly int $invoiceId) {}
public function url(): string { return "/invoices/{$this->invoiceId}"; }
public function assert(Browser $browser): void
{
$browser->assertPathIs($this->url());
}
public function elements(): array
{
return [
'@mark-paid-button' => '[dusk="mark-paid-button"]',
];
}
public function markAsPaid(Browser $browser): void
{
$browser->click('@mark-paid-button')
->waitForText('Invoice marked as paid');
}
public function fillForm(Browser $browser, string $amount): void
{
$browser->type('@amount-input', $amount);
}
public function submitForm(Browser $browser): void
{
$browser->press('@submit-button');
}
public function assertSuccess(Browser $browser): void
{
$browser->waitForText('Invoice created')
->assertSee('Invoice created');
}
}
// Waiting — never use pause()
$browser->waitFor('@invoice-table'); // ❌ pause(3000)
$browser->waitForText('Invoice created'); // ❌ pause(2000)
$browser->waitUntilMissing('@loading-spinner'); // ❌ pause(5000)
Testing Guidance
- Run all browser tests:
php artisan dusk. - Run a specific file:
php artisan dusk tests/Browser/InvoiceFlowTest.php. - Use CI-friendly headless Chrome settings and isolate Dusk database from local dev DB.
Anti-Patterns
- Using
RefreshDatabasein Dusk tests (transactions invisible across HTTP processes) - Using
pause(3000)— arbitrary sleeps make tests slow and brittle - Using CSS classes or IDs as selectors instead of
dusk=""attributes - Testing pure business logic or JSON API responses with Dusk (use feature tests instead)
- Not calling
assertNoJavaScriptErrors()aftervisit() - Repeating navigation and selector logic across test files instead of using Page Objects
References
- Laravel Dusk
- Related:
PestTesting/SKILL.md— Pest syntax used for Dusk tests - Related:
PHPUnit/SKILL.md— feature tests for non-browser scenarios