$parameters */ function spec125AssertPersistedTableState( string $componentClass, array $parameters, string $search, string $sortColumn, string $sortDirection, string $filterPath, mixed $filterValue, array $queryParams = [], ): void { $component = Livewire::withQueryParams($queryParams) ->test($componentClass, $parameters) ->searchTable($search) ->call('sortTable', $sortColumn, $sortDirection) ->set($filterPath, $filterValue); $instance = $component->instance(); expect(session()->get($instance->getTableSearchSessionKey()))->toBe($search); expect(session()->get($instance->getTableSortSessionKey()))->toBe("{$sortColumn}:{$sortDirection}"); expect(data_get(session()->get($instance->getTableFiltersSessionKey()), str($filterPath)->after('tableFilters.')->value()))->toBe($filterValue); Livewire::withQueryParams($queryParams) ->test($componentClass, $parameters) ->assertSet('tableSearch', $search) ->assertSet('tableSort', "{$sortColumn}:{$sortDirection}") ->assertSet($filterPath, $filterValue); } it('persists tenant list search, sort, and filter state across remounts', function (): void { [$user] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setCurrentPanel('admin'); Filament::setTenant(null, true); Filament::bootCurrentPanel(); spec125AssertPersistedTableState( ListTenants::class, [], 'Tenant', 'name', 'desc', 'tableFilters.environment.value', 'prod', ); }); it('persists policy list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListPolicies::class, [], 'Policy', 'display_name', 'desc', 'tableFilters.visibility.value', 'active', ); }); it('persists backup-set list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListBackupSets::class, [], 'Backup', 'name', 'desc', 'tableFilters.trashed.value', 1, ); }); it('persists backup-schedule list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListBackupSchedules::class, [], 'Schedule', 'name', 'desc', 'tableFilters.enabled_state.value', 'enabled', ); }); it('persists provider-connections list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListProviderConnections::class, [], 'Contoso', 'display_name', 'desc', 'tableFilters.default_only.isActive', true, ); }); it('persists findings list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListFindings::class, [], 'drift', 'created_at', 'asc', 'tableFilters.status.value', 'new', ); }); it('persists inventory item list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListInventoryItems::class, [], 'Policy', 'display_name', 'asc', 'tableFilters.platform.value', 'windows', ); }); it('persists policy version list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListPolicyVersions::class, [], 'Policy', 'captured_at', 'asc', 'tableFilters.platform.value', 'windows', ); }); it('persists restore run list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListRestoreRuns::class, [], 'Restore', 'started_at', 'asc', 'tableFilters.status.value', 'completed', ); }); it('persists alert delivery list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListAlertDeliveries::class, [], 'alert', 'created_at', 'asc', 'tableFilters.status.value', 'sent', ); }); it('persists Entra group list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( ListEntraGroups::class, [], 'Group', 'display_name', 'desc', 'tableFilters.group_type.value', 'security', ); }); it('persists baseline snapshot list search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]); spec125AssertPersistedTableState( ListBaselineSnapshots::class, [], 'Baseline', 'captured_at', 'asc', 'tableFilters.snapshot_state.value', 'with_gaps', ); }); it('persists monitoring operations search, sort, and filter state across remounts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( Operations::class, [], 'policy', 'type', 'desc', 'tableFilters.status.value', 'queued', ); }); it('restores operations table state while the requested tab stays query-driven', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); spec125AssertPersistedTableState( Operations::class, [], 'baseline', 'created_at', 'desc', 'tableFilters.status.value', 'failed', ['activeTab' => 'failed'], ); Livewire::withQueryParams(['activeTab' => 'failed']) ->actingAs($user) ->test(Operations::class) ->assertSet('activeTab', 'failed'); }); it('clears selected audit event state when persisted filters no longer contain the record', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $selectedAudit = AuditLog::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'actor_email' => 'owner@example.com', 'actor_name' => 'Owner', 'actor_type' => 'human', 'action' => 'workspace.selected', 'status' => 'success', 'resource_type' => 'workspace', 'resource_id' => (string) $tenant->workspace_id, 'target_label' => 'Workspace '.$tenant->workspace_id, 'summary' => 'Selected audit event', 'recorded_at' => now(), ]); AuditLog::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'actor_email' => 'owner@example.com', 'actor_name' => 'Owner', 'actor_type' => 'human', 'action' => 'operation_run.failed', 'status' => 'failure', 'resource_type' => 'operation_run', 'resource_id' => '1', 'target_label' => 'Run #1', 'summary' => 'Failure event', 'recorded_at' => now()->addSecond(), ]); $this->actingAs($user); Filament::setTenant(null, true); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); $auditComponent = Livewire::withQueryParams(['event' => (int) $selectedAudit->getKey()]) ->actingAs($user) ->test(AuditLogPage::class); session()->put($auditComponent->instance()->getTableFiltersSessionKey(), [ 'outcome' => ['value' => 'failure'], ]); Livewire::withQueryParams(['event' => (int) $selectedAudit->getKey()]) ->actingAs($user) ->test(AuditLogPage::class) ->assertSet('selectedAuditLogId', null); }); it('clears selected exception state when persisted queue filters no longer contain the record', function (): void { [$approver, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $finding = Finding::factory()->for($tenant)->create(); $selectedException = FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'finding_id' => (int) $finding->getKey(), 'requested_by_user_id' => (int) $approver->getKey(), 'owner_user_id' => (int) $approver->getKey(), 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT, 'request_reason' => 'Selected queue exception', 'requested_at' => now()->subDay(), 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); $rejectedFinding = Finding::factory()->for($tenant)->create(); FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'finding_id' => (int) $rejectedFinding->getKey(), 'requested_by_user_id' => (int) $approver->getKey(), 'owner_user_id' => (int) $approver->getKey(), 'status' => FindingException::STATUS_REJECTED, 'current_validity_state' => FindingException::VALIDITY_REJECTED, 'request_reason' => 'Rejected queue exception', 'requested_at' => now()->subDay(), 'rejected_at' => now()->subHour(), 'rejection_reason' => 'No longer needed', 'evidence_summary' => ['reference_count' => 0], ]); $this->actingAs($approver); setAdminPanelContext(); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); $queueComponent = Livewire::withQueryParams(['exception' => (int) $selectedException->getKey()]) ->test(FindingExceptionsQueue::class); session()->put($queueComponent->instance()->getTableFiltersSessionKey(), [ 'status' => ['value' => FindingException::STATUS_REJECTED], ]); Livewire::withQueryParams(['exception' => (int) $selectedException->getKey()]) ->test(FindingExceptionsQueue::class) ->assertSet('selectedFindingExceptionId', null); }); it('reseeds the provider-connections tenant filter when the remembered admin tenant changes', function (): void { $tenantA = Tenant::factory()->create(); [$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner'); $tenantB = Tenant::factory()->create([ 'workspace_id' => (int) $tenantA->workspace_id, ]); createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner'); $this->actingAs($user); Filament::setTenant(null, true); $workspaceId = (int) $tenantA->workspace_id; session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ (string) $workspaceId => (int) $tenantA->getKey(), ]); Livewire::actingAs($user)->test(ListProviderConnections::class) ->assertSet('tableFilters.tenant.value', (string) $tenantA->external_id); session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ (string) $workspaceId => (int) $tenantB->getKey(), ]); Livewire::actingAs($user)->test(ListProviderConnections::class) ->assertSet('tableFilters.tenant.value', (string) $tenantB->external_id); });