sinnbeck/laravel-dom-assertions
Extra DOM assertion helpers for Laravel HTTP tests. Assert elements, text, forms, selects, and datalists with more precision than assertSee/SeeText. Works with Livewire and Blade views/components; includes quick existence checks and a method reference.
Installation:
composer require sinnbeck/laravel-dom-assertions --dev
(For Laravel 9/PHP 8.0, use ^2.0)
First Use Case:
Replace basic assertSee with granular DOM assertions in a test:
$this->get('/dashboard')
->assertElementExists('#user-profile', fn (AssertElement $el) => $el->is('div'));
Where to Look First:
AssertElement class docs (via IDE autocompletion) for fluent API details.Element Assertions:
// Basic existence
$this->get('/')->assertElementExists('header');
// Fluent assertions
$this->get('/')->assertElementExists('header', fn (AssertElement $el) => [
$el->is('header'),
$el->has('class', 'main-header'),
$el->containsText('Welcome'),
]);
Form-Specific Tests:
$this->get('/login')->assertFormExists('#login-form', fn (AssertForm $form) => [
$form->hasAction('/auth/login')
->hasMethod('post')
->containsInput(['name' => 'email'])
->containsButton(['type' => 'submit']),
]);
Nested Assertions:
$this->get('/products')->assertElementExists('#product-list', fn (AssertElement $list) => [
$list->each('li.product', fn (AssertElement $item) => [
$item->contains('h3.title'),
$item->find('.price', fn (AssertElement $price) => $price->containsText('$')),
]),
]);
assertElementExists() to verify dynamic updates:
$this->livewire(ProfileComponent::class)
->assertElementExists('#livewire-updated', fn ($el) => $el->containsText('Updated!'));
$this->get('/')->assertElementExists('x-app', fn ($el) => $el->contains('x-data'));
$response = $this->postJson('/api/users')->assertCreated();
$this->get('/users')->assertElementExists('#user-1', fn ($el) => $el->containsText($response['name']));
Leverage shortcuts for common patterns:
// Instead of:
$assert->is('div')->has('class', 'foo');
// Use:
$assert->isDiv()->hasClass('foo');
// For forms:
$form->hasEnctype('multipart/form-data'); // Shortcut for has('enctype', '...')
Selector Specificity:
div) may match unintended elements. Prefer IDs or unique classes.each() to validate all matches:
$assert->each('div.card', fn ($el) => $el->has('data-id'));
Whitespace Normalization:
normalize_whitespace) may hide subtle text mismatches.$assert->containsText('Exact Match', normalizeWhitespace: false);
Livewire/Alpine Attributes:
x-data) may change between tests.$assert->has('x-data', '/\{.*\}/');
Form Method Spoofing:
hasMethod('PUT') auto-detects spoofed methods, but may fail if the form lacks a method attribute.$form->hasMethod('PUT')->hasSpoofMethod('PUT');
assertElementExists() with no closure to validate the parsed DOM structure.dd($element->getDOMNode()) inside closures to debug node structures.php artisan optimize:clear) if tests fail intermittently due to stale views.Custom Assertions:
Extend the AssertElement class to add domain-specific methods:
class CustomAssertElement extends AssertElement {
public function hasAlpineData(string $key) {
return $this->has('x-data', fn ($value) => str_contains($value, $key));
}
}
Register via service provider.
Config Overrides: Publish the config to customize defaults:
php artisan vendor:publish --tag=dom-assertions-config
Modify normalize_whitespace or add custom selectors.
Rector Rules:
Use the provided Rector rules to auto-convert legacy assertSee to DOM assertions:
vendor/bin/rector process src --dry-run
find() calls can slow tests. Prefer direct selectors where possible.$response = $this->get('/');
$response->assertElementExists('#header');
$response->assertElementExists('#footer');
How can I help you explore Laravel packages today?