create(); $tenantB = Tenant::factory()->create(); [$user] = createUserWithTenant($tenantA, role: 'owner'); $user->tenants()->syncWithoutDetaching([ $tenantB->getKey() => ['role' => 'owner'], ]); OperationRun::factory()->create([ 'tenant_id' => $tenantA->getKey(), 'type' => 'policy.sync', 'status' => 'running', 'outcome' => 'pending', 'initiator_name' => 'TenantA', ]); OperationRun::factory()->create([ 'tenant_id' => $tenantB->getKey(), 'type' => 'inventory_sync', 'status' => 'running', 'outcome' => 'pending', 'initiator_name' => 'TenantB', ]); $this->actingAs($user); Filament::setTenant($tenantA, true); assertNoOutboundHttp(function () { Livewire::test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('disabled', false) ->assertSee('Policy sync') ->assertDontSee('Inventory sync'); }); })->group('ops-ux'); it('keeps a just-completed successful run visible briefly as terminal success', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'completed', 'outcome' => 'succeeded', 'started_at' => now()->subMinutes(2), 'completed_at' => now()->subSeconds(10), ]); Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('hasActiveRuns', false) ->assertSee('Operation updates') ->assertSee('Inventory sync') ->assertSee('View operation') ->assertDontSee('Review operations') ->assertSee('Completed successfully') ->assertSee('No action needed.') ->assertSee('Dismiss') ->assertDontSee('Waiting for worker.') ->assertDontSee('Hide activity'); })->group('ops-ux'); it('keeps unresolved terminal follow-up runs visible instead of dropping them silently', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->set('tenantId', (int) $tenant->getKey()) ->call('refreshRuns') ->assertSet('hasActiveRuns', false) ->assertDontSee('Inventory sync'); OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'completed', 'outcome' => 'failed', 'started_at' => now()->subMinutes(3), 'completed_at' => now()->subSeconds(5), ]); Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('hasActiveRuns', false) ->assertSee('Operation updates') ->assertSee('Inventory sync') ->assertSee('View operation') ->assertDontSee('Review operations') ->assertSee('Acknowledge') ->assertDontSee('Dismiss') ->assertDontSee('Waiting for worker.') ->assertDontSee('Hide activity'); })->group('ops-ux'); it('uses a collective primary action when multiple shell-visible operation updates are shown', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'running', 'outcome' => 'pending', 'started_at' => now()->subMinute(), ]); OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'completed', 'outcome' => 'failed', 'started_at' => now()->subMinutes(3), 'completed_at' => now()->subSeconds(5), ]); Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('hasActiveRuns', true) ->assertSee('Operation updates') ->assertSee('Review operations') ->assertDontSee('View operation') ->assertSee('Show all operations') ->assertSee('Acknowledge') ->assertDontSee('Dismiss updates'); })->group('ops-ux'); it('shows likely stale runs in the progress overlay and keeps polling when only stale runs remain', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'queued', 'outcome' => 'pending', 'created_at' => now()->subWeeks(2), 'started_at' => null, ]); Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('hasActiveRuns', true) ->assertSee('Inventory sync') ->assertSee('Likely stale') ->assertSee('Waiting for worker.'); })->group('ops-ux'); it('registers Alpine cleanup for the Ops UX poller to avoid stale listeners across re-renders', function () { $contents = file_get_contents(resource_path('views/livewire/bulk-operation-progress.blade.php')); expect($contents)->toContain('new MutationObserver'); expect($contents)->toContain('teardownObserver'); expect($contents)->toContain('wire:poll.10s="refreshRuns"'); expect($contents)->not->toContain('wire:poll.5s="refreshRuns"'); })->group('ops-ux');