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('removes terminal runs from the progress overlay on the next refresh cycle without a new enqueue event', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); Filament::setTenant($tenant, true); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'inventory_sync', 'status' => 'running', 'outcome' => 'pending', ]); $component = Livewire::actingAs($user) ->test(BulkOperationProgress::class) ->call('refreshRuns') ->assertSet('hasActiveRuns', true) ->assertSee('Inventory sync'); $run->forceFill([ 'status' => 'completed', 'outcome' => 'failed', 'completed_at' => now(), ])->save(); $component ->call('refreshRuns') ->assertSet('hasActiveRuns', false) ->assertDontSee('Inventory sync'); })->group('ops-ux'); it('does not show likely stale runs in the progress overlay and stops 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', false) ->assertDontSee('Inventory sync'); })->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');