| name | filament-testing |
| description | Generate Pest tests for FilamentPHP v4 resources, forms, tables, and authorization |
FilamentPHP Testing Skill
Overview
This skill generates comprehensive Pest tests for FilamentPHP v4 components following official testing documentation patterns.
Documentation Reference
CRITICAL: Before generating tests, read:
/home/mwguerra/projects/mwguerra/claude-code-plugins/filament-specialist/skills/filament-docs/references/general/10-testing/
Test Setup
Base Test Configuration
<?php
declare(strict_types=1);
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void
{
parent::setUp();
$this->actingAs(\App\Models\User::factory()->create([
'is_admin' => true,
]));
}
}
Pest Configuration
uses(Tests\TestCase::class)
->in('Feature');
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)
->in('Feature');
Resource Tests
List Page Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource;
use App\Filament\Resources\PostResource\Pages\ListPosts;
use App\Models\Post;
use App\Models\User;
use Filament\Actions\DeleteAction;
use Filament\Tables\Actions\DeleteBulkAction;
use function Pest\Livewire\livewire;
beforeEach(function () {
$this->user = User::factory()->create(['is_admin' => true]);
$this->actingAs($this->user);
});
it('can render the list page', function () {
livewire(ListPosts::class)
->assertSuccessful();
});
(, function () {
= ::()->()->();
(::)
->();
});
(, function () {
::()->([ => ]);
(::)
->();
});
(, function () {
= ::()->([ => ]);
= ::()->([ => ]);
(::)
->()
->([])
->([]);
});
(, function () {
= ::()->()->();
(::)
->()
->(->(), : )
->(, )
->(->(), : );
});
(, function () {
= ::()->([ => ]);
= ::()->([ => ]);
(::)
->(, )
->([])
->([]);
});
(, function () {
= ::()->()->();
(::)
->(::, );
( ) {
->();
}
});
Create Page Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource;
use App\Filament\Resources\PostResource\Pages\CreatePost;
use App\Models\Category;
use App\Models\Post;
use App\Models\User;
use function Pest\Livewire\livewire;
beforeEach(function () {
$this->user = User::factory()->create(['is_admin' => true]);
$this->actingAs($this->user);
});
it('can render the create page', function () {
livewire(CreatePost::class)
->assertSuccessful();
});
it('can create a post', function () {
$category = Category::()->();
= [
=> ,
=> ,
=> ,
=> ,
=> ->id,
];
(::)
->()
->()
->();
->(::, [
=> ,
=> ,
]);
});
(, function () {
(::)
->([
=> ,
=> ,
])
->()
->([
=> ,
=> ,
]);
});
(, function () {
(::)
->([
=> (, ),
])
->()
->([ => ]);
});
(, function () {
::()->([ => ]);
(::)
->([
=> ,
=> ,
=> ,
])
->()
->([ => ]);
});
Edit Page Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource;
use App\Filament\Resources\PostResource\Pages\EditPost;
use App\Models\Post;
use App\Models\User;
use Filament\Actions\DeleteAction;
use function Pest\Livewire\livewire;
beforeEach(function () {
$this->user = User::factory()->create(['is_admin' => true]);
$this->actingAs($this->user);
});
it('can render the edit page', function () {
$post = Post::factory()->create();
livewire(EditPost::class, ['record' => $post->()])
->();
});
(, function () {
= ::()->();
(::, [ => ->()])
->([
=> ->title,
=> ->slug,
=> ->content,
=> ->status,
]);
});
(, function () {
= ::()->();
= [
=> ,
=> ,
=> ,
=> ,
];
(::, [ => ->()])
->()
->()
->();
(->())
->title->()
->slug->()
->status->();
});
(, function () {
= ::()->();
(::, [ => ->()])
->(::);
->();
});
(, function () {
= ::()->([ => ]);
= ::()->([ => ]);
(::, [ => ->()])
->([ => ])
->()
->([ => ]);
});
View Page Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\Pages\ViewPost;
use App\Models\Post;
use App\Models\User;
use function Pest\Livewire\livewire;
beforeEach(function () {
$this->user = User::factory()->create(['is_admin' => true]);
$this->actingAs($this->user);
});
it('can render the view page', function () {
$post = Post::factory()->create();
livewire(ViewPost::class, ['record' => $post->getRouteKey()])
->assertSuccessful();
});
it('can retrieve post data in infolist', function () {
$post = Post::()->([
=> ,
=> ,
]);
(::, [ => ->()])
->()
->();
});
Form Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\Pages\CreatePost;
use App\Models\Category;
use App\Models\Tag;
use function Pest\Livewire\livewire;
it('can fill form fields', function () {
$category = Category::factory()->create();
livewire(CreatePost::class)
->fillForm([
'title' => 'Test Title',
'category_id' => $category->id,
])
->assertFormSet([
'title' => 'Test Title',
'category_id' => $category->id,
]);
});
it('has required form fields', function () {
livewire(CreatePost::class)
->assertFormFieldExists('title')
->()
->()
->()
->();
});
(, function () {
= ::()->()->();
(::)
->(, function () ($) {
$->() === $->('', '')->();
});
});
(, function () {
(::)
->([
=> [
[ => , => ],
[ => , => ],
],
])
->()
->();
});
Table Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\Pages\ListPosts;
use App\Models\Post;
use function Pest\Livewire\livewire;
it('displays correct columns', function () {
Post::factory()->create([
'title' => 'Test Post',
'status' => 'published',
]);
livewire(ListPosts::class)
->assertCanRenderTableColumn('title')
->assertCanRenderTableColumn('status')
->assertCanRenderTableColumn('author.name')
->assertCanRenderTableColumn('created_at');
});
it('can filter by date range', function () {
$oldPost = Post::factory()->create([
'created_at' => now()->subMonths(),
]);
= ::()->([
=> (),
]);
(::)
->(, [
=> ()->()->(),
=> ()->(),
])
->([])
->([]);
});
(, function () {
(::)
->();
});
Action Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\Pages\EditPost;
use App\Filament\Resources\PostResource\Pages\ListPosts;
use App\Models\Post;
use Filament\Tables\Actions\DeleteAction;
use function Pest\Livewire\livewire;
it('can call publish action', function () {
$post = Post::factory()->create(['status' => 'draft']);
livewire(EditPost::class, ['record' => $post->getRouteKey()])
->callAction('publish');
expect($post->refresh()->status)->toBe('published');
});
it(, function () {
= ::()->([ => ]);
= ::()->([ => ]);
(::, [ => ->()])
->();
(::, [ => ->()])
->();
});
(, function () {
= ::()->();
(::)
->(::, );
->();
});
(, function () {
= ::()->();
(::, [ => ->()])
->(, [
=> ,
=> ,
])
->();
});
(, function () {
= ::()->();
(::, [ => ->()])
->(, [
=> ,
=> ,
])
->([
=> ,
=> ,
]);
});
Authorization Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\Pages\CreatePost;
use App\Filament\Resources\PostResource\Pages\EditPost;
use App\Filament\Resources\PostResource\Pages\ListPosts;
use App\Models\Post;
use App\Models\User;
use function Pest\Livewire\livewire;
it('prevents unauthorized users from viewing list', function () {
$user = User::factory()->create(['is_admin' => false]);
$this->actingAs($user);
livewire(ListPosts::class)
->assertForbidden();
});
it(, function () {
= ::()->([ => ]);
->();
(::)
->();
});
(, function () {
= ::()->();
= ::()->();
= ::()->([ => ->id]);
->();
(::, [ => ->()])
->();
});
(, function () {
= ::()->();
= ::()->([ => ->id]);
->();
(::, [ => ->()])
->();
});
Widget Tests
<?php
declare(strict_types=1);
use App\Filament\Widgets\StatsOverview;
use App\Filament\Widgets\LatestPosts;
use App\Models\Post;
use App\Models\User;
use function Pest\Livewire\livewire;
it('can render stats overview widget', function () {
livewire(StatsOverview::class)
->assertSuccessful();
});
it('displays correct stats', function () {
Post::factory()->count(5)->create(['status' => 'published']);
Post::factory()->count(3)->create(['status' => 'draft']);
livewire(StatsOverview::class)
->()
->();
});
(, function () {
= ::()->()->();
(::)
->()
->();
});
Relation Manager Tests
<?php
declare(strict_types=1);
use App\Filament\Resources\PostResource\RelationManagers\CommentsRelationManager;
use App\Models\Comment;
use App\Models\Post;
use function Pest\Livewire\livewire;
it('can render relation manager', function () {
$post = Post::factory()->create();
livewire(CommentsRelationManager::class, [
'ownerRecord' => $post,
'pageClass' => \App\Filament\Resources\PostResource\Pages\EditPost::class,
])
->assertSuccessful();
});
it('can list related comments', function () {
$post = Post::factory()->create();
$comments = Comment::factory()->count(3)->([ => ->id]);
(::, [
=> ,
=> ::,
])
->();
});
(, function () {
= ::()->();
(::, [
=> ,
=> ::,
])
->(, : [
=> ,
]);
(->comments)->();
});
Output
Generated tests include:
- Page rendering tests
- CRUD operation tests
- Form validation tests
- Table feature tests (search, sort, filter)
- Action tests
- Authorization tests
- Widget tests
- Relation manager tests