Testing
The package's own test suite uses Pest 4 with pest-plugin-laravel and pest-plugin-livewire — the same plugins your consumer app already has if it tests Filament. Nothing extra to install. This page covers patterns for testing the timeline inside your application; for testing the package itself, see tests/Feature/ in the repo.
Setup
The base case in your consumer app is the standard Laravel test scaffold:
// tests/Pest.php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class)->in('Feature');
A typical beforeEach for any test that touches the timeline UI logs in an admin and creates the record under test:
use App\Models\Person;
use App\Models\User;
beforeEach(function (): void {
$this->actingAs(User::factory()->admin()->create());
$this->record = Person::factory()->create();
});
Testing the infolist via livewire()
Mount the Filament ViewRecord page that hosts your ActivityLog::make() component, mutate the record so spatie writes an activity row, and assert the rendered output. The built-in ActivityLogRenderer emits the diff sentence, so assert against the new value:
use App\Filament\Resources\People\Pages\ViewPerson;
use function Pest\Livewire\livewire;
it('renders an entry on the person view page after an update', function (): void {
$this->record->update(['name' => 'Updated name']);
livewire(ViewPerson::class, ['record' => $this->record->getKey()])
->assertSeeHtml('Updated name');
});
If you've registered a custom renderer (see /essentials/customization), assert against your specific markup instead — the assertSeeHtml() target should match what your renderer outputs, not the diff sentence.
Testing the relation manager
The package's own RelationManagerTest.php asserts the static configuration surface; your consumer-app test should mount the relation manager against an owner record and a host page. Filament requires both ownerRecord and pageClass:
use App\Filament\Resources\People\Pages\EditPerson;
use Relaticle\ActivityLog\Filament\RelationManagers\ActivityLogRelationManager;
use function Pest\Livewire\livewire;
it('mounts the activity log relation manager for a record', function (): void {
livewire(ActivityLogRelationManager::class, [
'ownerRecord' => $this->record,
'pageClass' => EditPerson::class,
])->assertSuccessful();
});
Adjust pageClass to whichever resource page hosts the relation manager (typically Edit* or View*).
Testing the header action
The default action name is 'activityLog' — that's the string you pass to callAction():
use App\Filament\Resources\People\Pages\ViewPerson;
use function Pest\Livewire\livewire;
it('opens the activity log slide-over', function (): void {
livewire(ViewPerson::class, ['record' => $this->record->getKey()])
->callAction('activityLog')
->assertSuccessful();
});
If you renamed the action via ActivityLogAction::make('history'), pass 'history' instead.
Asserting timeline entries directly
Skip the UI when you're testing source composition — call the builder and assert against its return value. This mirrors the package's own ActivityLogSourceTest.php:
use Spatie\Activitylog\Models\Activity;
it('emits an entry per activity row', function (): void {
Activity::create([
'log_name' => 'default',
'description' => 'updated',
'event' => 'updated',
'subject_type' => $this->record->getMorphClass(),
'subject_id' => $this->record->getKey(),
'properties' => ['attributes' => ['name' => 'New'], 'old' => ['name' => 'Old']],
]);
$entries = $this->record->timeline()->fromActivityLog()->get();
expect($entries)
->toHaveCount(1)
->and($entries->first()->event)->toBe('updated');
});
The same shape works for fromRelation(), fromActivityLogOf(), and fromCustom() — drive the builder, collect the entries, assert against $entries->pluck('event'), ->count(), or any other property on TimelineEntry. The package's BuilderFilteringTest.php is a good reference for between(), ofEvent(), and sortByDateAsc() patterns.
Factories for Spatie\Activitylog\Models\Activity
Spatie's Activity model doesn't ship a factory. If you need one in your consumer app, drop this in database/factories/:
namespace Database\Factories\Spatie;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Models\Activity;
/** @extends Factory<Activity> */
final class ActivityFactory extends Factory
{
protected $model = Activity::class;
public function definition(): array
{
return [
'log_name' => 'default',
'description' => $this->faker->word(),
'event' => $this->faker->randomElement(['created', 'updated', 'deleted']),
'subject_type' => null,
'subject_id' => null,
'causer_type' => null,
'causer_id' => null,
'properties' => [],
];
}
public function for(Model $subject): static
{
return $this->state([
'subject_type' => $subject->getMorphClass(),
'subject_id' => $subject->getKey(),
]);
}
}
Wire spatie's Activity to the factory by overriding Laravel's factory resolver in a service provider's boot() method — see Laravel's factory docs for the canonical setup with Factory::guessFactoryNamesUsing(). Then use it like any other factory:
use Database\Factories\Spatie\ActivityFactory;
ActivityFactory::new()->for($this->record)->create();
Where to look next
- The package's own
tests/Feature/directory has working examples for every public surface. - For UI-level assertions on grouping, pagination, and infinite scroll, mount
ActivityLogLivewiredirectly — seeRelationManagerTest.phpfor the static-config approach. - For end-to-end flows (action opens slide-over, scroll loads more entries), use Pest 4 browser tests with
visit()instead oflivewire().