UiEnforcement::for(Capabilities::TENANT_VIEW)->tenantFromRecord()->preserveVisibility()) ->toThrow(LogicException::class); expect(fn () => UiEnforcement::for(Capabilities::TENANT_VIEW)->preserveVisibility()->tenantFromRecord()) ->toThrow(LogicException::class); }); it('hides actions for non-members on record-scoped surfaces', function () { $tenant = Tenant::factory()->create(); [$user] = createUserWithTenant(); $action = Action::make('test'); UiEnforcement::for(Capabilities::TENANT_VIEW) ->tenantFromRecord() ->apply($action); $this->actingAs($user); $action->record($tenant); expect($action->isHidden())->toBeTrue(); }); it('disables actions with the standard tooltip for members without the capability', function () { $tenant = Tenant::factory()->create(); [$user] = createUserWithTenant($tenant, role: 'readonly'); $action = Action::make('test'); UiEnforcement::for(Capabilities::TENANT_SYNC) ->tenantFromRecord() ->apply($action); $this->actingAs($user); $action->record($tenant); expect($action->isHidden())->toBeFalse(); expect($action->isDisabled())->toBeTrue(); expect($action->getTooltip())->toBe(UiTooltips::insufficientPermission()); }); it('enables actions for members with the capability', function () { $tenant = Tenant::factory()->create(); [$user] = createUserWithTenant($tenant, role: 'owner'); $action = Action::make('test'); UiEnforcement::for(Capabilities::TENANT_SYNC) ->tenantFromRecord() ->apply($action); $this->actingAs($user); $action->record($tenant); expect($action->isHidden())->toBeFalse(); expect($action->isDisabled())->toBeFalse(); expect($action->getTooltip())->toBeNull(); }); it('supports mixed visibility composition via andVisibleWhen', function () { $tenant = Tenant::factory()->create(); [$user] = createUserWithTenant($tenant, role: 'owner'); Filament::setTenant($tenant, true); $action = Action::make('test'); UiEnforcement::for(Capabilities::TENANT_VIEW) ->andVisibleWhen(fn (): bool => false) ->apply($action); $this->actingAs($user); expect($action->isHidden())->toBeTrue(); }); it('supports mixed visibility composition via andHiddenWhen', function () { $tenant = Tenant::factory()->create(); [$user] = createUserWithTenant($tenant, role: 'owner'); Filament::setTenant($tenant, true); $action = Action::make('test'); UiEnforcement::for(Capabilities::TENANT_VIEW) ->andHiddenWhen(fn (): bool => true) ->apply($action); $this->actingAs($user); expect($action->isHidden())->toBeTrue(); }); it('disables bulk actions for mixed-authorization selections (capability preflight)', function () { $tenantA = Tenant::factory()->create(); $tenantB = Tenant::factory()->create(); [$user] = createUserWithTenant($tenantA, role: 'owner'); $user->tenants()->syncWithoutDetaching([ $tenantB->getKey() => ['role' => 'readonly'], ]); $enforcement = UiEnforcement::for(Capabilities::TENANT_SYNC) ->tenantFromRecord() ->preflightByCapability(); expect($enforcement->bulkSelectionIsAuthorized($user, collect([$tenantA, $tenantB])))->toBeFalse(); $user->tenants()->syncWithoutDetaching([ $tenantB->getKey() => ['role' => 'owner'], ]); expect($enforcement->bulkSelectionIsAuthorized($user, collect([$tenantA, $tenantB])))->toBeTrue(); });