Testing
This guide covers testing strategies for CMS-backed components using Pest.
Ensure you have the testing dependencies:
composer require --dev pestphp/pest pestphp/pest-plugin-laravelThe package provides test mixins:
uses( Tests\TestCase::class, Illuminate\Foundation\Testing\RefreshDatabase::class,)->in('Feature', 'Unit');Testing Pages
Section titled “Testing Pages”Basic Page Rendering
Section titled “Basic Page Rendering”use App\Livewire\Pages\About;use JFA\FilamentCMSCore\Models\Page;use JFA\FilamentCMSCore\Models\Section;
it('renders the about page with sections', function () { $page = Page::factory()->create([ 'slug' => 'about', 'status' => 'published', ]);
$section = Section::factory()->create([ 'slug' => 'hero', 'status' => 'active', ]);
$page->sections()->attach($section, ['order_column' => 1]);
Livewire::test(About::class) ->assertStatus(200) ->assertSee('Welcome');});Page with Metatags
Section titled “Page with Metatags”it('injects metatags from cms page', function () { $page = Page::factory()->create([ 'slug' => 'about', 'metatags' => [ 'title' => 'About Us - My Company', 'description' => 'Learn more about our team.', ], ]);
$response = $this->get('/about');
$response->assertSee('About Us - My Company', false); $response->assertSee('Learn more about our team.', false);});Testing Sections
Section titled “Testing Sections”Section Component Mounting
Section titled “Section Component Mounting”use App\Livewire\Hero;use JFA\FilamentCMSCore\CMS\SectionContent;
it('mounts with section content', function () { $content = new SectionContent([ [ 'type' => 'hero', 'data' => [ 'label' => 'Welcome', 'headline' => 'Build Amazing Sites', ], ], ]);
$component = Livewire::test(Hero::class, ['content' => $content]);
$component->assertSet('label', 'Welcome'); $component->assertSet('headline', 'Build Amazing Sites');});renderField Without Visual Editing
Section titled “renderField Without Visual Editing”it('renders plain values when visual editing is unavailable', function () { $content = new SectionContent([ [ 'type' => 'hero', 'data' => [ 'label' => 'Welcome', 'headline' => 'Build Amazing Sites', ], ], ]);
$component = Livewire::test(Hero::class, ['content' => $content]);
// renderField should output plain value, not data-cms-source attributes $component->assertSeeHtml('Welcome'); $component->assertDontSeeHtml('data-cms-source');});Section with Repeater
Section titled “Section with Repeater”it('handles repeater content', function () { $content = new SectionContent([ [ 'type' => 'team', 'data' => [ 'section_title' => 'Our Team', 'members' => [ ['name' => 'Alice', 'role' => 'Developer'], ['name' => 'Bob', 'role' => 'Designer'], ], ], ], ]);
Livewire::test(Team::class, ['content' => $content]) ->assertSet('sectionTitle', 'Our Team') ->assertSet('members', [ ['name' => 'Alice', 'role' => 'Developer'], ['name' => 'Bob', 'role' => 'Designer'], ]);});Testing Visual Editing
Section titled “Testing Visual Editing”Editing Mode State
Section titled “Editing Mode State”use JFA\VeFilamentCMSLivewire\EditingMode;use JFA\VeFilamentCMSLivewire\Livewire\VisualEditor;
beforeEach(function () { Session::flush();});
it('toggles editing mode', function () { $user = actingAsAdmin(); // Helper from tests/Pest.php
Livewire::actingAs($user)->test(VisualEditor::class) ->call('toggle') ->assertRedirect();
expect(app(EditingMode::class)->isActive())->toBeTrue();});Inline Edit Form
Section titled “Inline Edit Form”use JFA\VeFilamentCMSLivewire\Livewire\InlineEditForm;use JFA\VeFilamentCMSLivewire\CmsSourceMap;
it('loads and saves content via source map', function () { $user = actingAsAdmin();
$section = Section::factory()->create([ 'content' => [ [ 'type' => 'hero', 'data' => [ 'label' => 'Old Label', 'headline' => 'Old Headline', ], ], ], ]);
$sourceMap = new CmsSourceMap( sectionId: $section->id, sectionSlug: 'hero', blockType: 'hero', blockIndex: 0, pageId: 1, field: 'headline', fieldType: 'text', );
Livewire::actingAs($user)->test(InlineEditForm::class) ->set('sourceMap', $sourceMap->toArray()) ->set('data.value', 'New Headline') ->call('save') ->assertDispatched('contentUpdated');
$section->refresh(); expect($section->content[0]['data']['headline'])->toBe('New Headline');});refreshFromCMS Event Listener (from InteractsWithVisualEditing trait)
Section titled “refreshFromCMS Event Listener (from InteractsWithVisualEditing trait)”it('refreshes content when contentUpdated event fires', function () { $section = Section::factory()->create([ 'content' => [ [ 'type' => 'hero', 'data' => [ 'headline' => 'Original', ], ], ], ]);
$content = new SectionContent([ [ 'type' => 'hero', 'data' => ['headline' => 'Original'], ], ]);
$component = Livewire::test(Hero::class, ['content' => $content]);
// Simulate component with source map $component->set('cmsSourceMap', [ 'section_id' => $section->id, 'section_slug' => 'hero', 'block_type' => 'hero', 'block_index' => 0, ]);
// Update section in database $section->content = [ [ 'type' => 'hero', 'data' => ['headline' => 'Updated'], ], ]; $section->save();
// Fire the event $component->dispatch('contentUpdated', sectionId: $section->id);
$component->assertSet('headline', 'Updated');});Testing Content Blocks
Section titled “Testing Content Blocks”Block Schema
Section titled “Block Schema”use App\CMS\Blocks\Hero;use JFA\FilamentCMSCore\Forms\Blocks\ContentBlockRegistry;
it('registers the hero block', function () { $registry = app(ContentBlockRegistry::class); $block = $registry->getByName('hero');
expect($block)->not->toBeNull(); expect($block->getName())->toBe('hero');});
it('has required fields', function () { $block = App\CMS\Blocks\Hero::make(); $schema = $block->getChildSchema();
$fields = collect($schema)->map(fn ($field) => $field->getName())->all();
expect($fields)->toContain('label'); expect($fields)->toContain('headline');});Testing Authorization
Section titled “Testing Authorization”Editor Panel Access
Section titled “Editor Panel Access”it('allows frontend_editor to access editor panel', function () { $user = User::factory()->create(); $user->assignRole('frontend_editor');
$this->actingAs($user) ->get('/editor') ->assertOk();});
it('denies non-editors from editor panel', function () { $user = User::factory()->create();
$this->actingAs($user) ->get('/editor') ->assertRedirect('/editor/login');});Helper Functions
Section titled “Helper Functions”Add to tests/Pest.php:
function actingAsAdmin(): App\Models\User{ $user = App\Models\User::factory()->create(); $user->assignRole('super_admin'); return $user;}
function actingAsFrontendEditor(): App\Models\User{ $user = App\Models\User::factory()->create(); $user->assignRole('frontend_editor'); return $user;}
function ensureShieldRolesExist(): void{ // Create roles if they don't exist Spatie\Permission\Models\Role::firstOrCreate(['name' => 'super_admin']); Spatie\Permission\Models\Role::firstOrCreate(['name' => 'frontend_editor']);}Running Tests
Section titled “Running Tests”# Run all testsphp artisan test --compact
# Run specific test filephp artisan test --compact --filter=SectionComponentsTest
# Run with coveragephp artisan test --compact --coverage
# Run and formatcomposer testBest Practices
Section titled “Best Practices”- Use factories for model creation
- Flush session before visual editing tests
- Test both authorized and unauthorized access
- Test empty content states
- Test content update events
- Test that
renderField()outputs plain values when VE is unavailable - Run
php artisan test --compactafter changes