browser()->timeout(40_000); beforeEach(function (): void { config()->set('graph.client_id', 'spec353-platform-client'); config()->set('graph.client_secret', 'spec353-platform-secret'); config()->set('graph.managed_environment_id', 'organizations'); }); function spec353BrowserApplicationPermissionKey(): string { $permission = collect(spec283ConfiguredPermissionRows()) ->first(static fn (mixed $row): bool => is_array($row) && ($row['type'] ?? null) === 'application'); expect($permission)->not->toBeNull(); return (string) $permission['key']; } function spec353BrowserSeedPermissionRows( ManagedEnvironment $environment, array $missingKeys = [], array $errorKeys = [], ?\DateTimeInterface $lastCheckedAt = null, ): void { foreach (spec283ConfiguredPermissionRows() as $permission) { if (! is_array($permission)) { continue; } $permissionKey = (string) ($permission['key'] ?? ''); if ($permissionKey === '') { continue; } ManagedEnvironmentPermission::query()->updateOrCreate( [ 'managed_environment_id' => (int) $environment->getKey(), 'permission_key' => $permissionKey, 'workspace_id' => (int) $environment->workspace_id, ], [ 'status' => in_array($permissionKey, $errorKeys, true) ? 'error' : (in_array($permissionKey, $missingKeys, true) ? 'missing' : 'granted'), 'details' => ['source' => 'spec353-browser-test'], 'last_checked_at' => $lastCheckedAt ?? now(), ], ); } } /** * @return array{ * user: User, * workspace: Workspace, * blockedEnvironment: ManagedEnvironment, * blockedConnection: ProviderConnection, * failedEnvironment: ManagedEnvironment, * failedConnection: ProviderConnection, * readyEnvironment: ManagedEnvironment, * readyConnection: ProviderConnection, * } */ function spec353BrowserFixture(): array { [$user, $blockedEnvironment] = createUserWithTenant( role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $workspace = $blockedEnvironment->workspace()->firstOrFail(); $failedEnvironment = ManagedEnvironment::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'name' => 'Spec353 Verification Failed', ]); $readyEnvironment = ManagedEnvironment::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'name' => 'Spec353 Provider Ready', ]); $user->tenants()->syncWithoutDetaching([ (int) $failedEnvironment->getKey() => ['role' => 'owner'], (int) $readyEnvironment->getKey() => ['role' => 'owner'], ]); $blockedConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'managed_environment_id' => (int) $blockedEnvironment->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'display_name' => 'Spec353 Blocked Connection', 'is_default' => true, ]); $failedConnection = ProviderConnection::factory()->platform()->consentGranted()->create([ 'managed_environment_id' => (int) $failedEnvironment->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'display_name' => 'Spec353 Failed Connection', 'is_default' => true, 'verification_status' => ProviderVerificationStatus::Error->value, 'last_error_message' => 'Spec353 verification failed during health check.', ]); $readyConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'managed_environment_id' => (int) $readyEnvironment->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'display_name' => 'Spec353 Ready Connection', 'is_default' => true, 'last_health_check_at' => now(), ]); $missingPermissionKey = spec353BrowserApplicationPermissionKey(); spec353BrowserSeedPermissionRows($blockedEnvironment, missingKeys: [$missingPermissionKey]); spec353BrowserSeedPermissionRows($failedEnvironment); spec353BrowserSeedPermissionRows($readyEnvironment); OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'managed_environment_id' => (int) $failedEnvironment->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, 'context' => [ 'provider_connection_id' => (int) $failedConnection->getKey(), ], ]); return [ 'user' => $user, 'workspace' => $workspace, 'blockedEnvironment' => $blockedEnvironment, 'blockedConnection' => $blockedConnection, 'failedEnvironment' => $failedEnvironment, 'failedConnection' => $failedConnection, 'readyEnvironment' => $readyEnvironment, 'readyConnection' => $readyConnection, ]; } function spec353BrowserActAs(User $user, Workspace $workspace, ManagedEnvironment $environment): void { test()->actingAs($user)->withSession([ WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspace->getKey() => (int) $environment->getKey(), ], ]); } function spec353BrowserScreenshot(string $name): string { return 'spec353-'.$name; } function spec353CopyBrowserScreenshot(string $name): void { $filename = spec353BrowserScreenshot($name).'.png'; $source = \Pest\Browser\Support\Screenshot::path($filename); $targetDirectory = repo_path('specs/353-provider-connections-resolution-guidance-v1/artifacts/screenshots'); if (! is_dir($targetDirectory)) { @mkdir($targetDirectory, 0755, true); } if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) { return; } if (is_file($source)) { @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$filename); } } function spec353BrowserTextContainsScript(string $selector, string $text): string { $encodedSelector = json_encode($selector); $encodedText = json_encode($text); return << { const element = document.querySelector($encodedSelector); if (! element) { return false; } return element.textContent.replace(/\\s+/g, ' ').trim().includes($encodedText); })() JS; } it('smokes provider readiness guidance across dashboard, required permissions, provider connections, and screenshots', function (): void { $fixture = spec353BrowserFixture(); spec353BrowserActAs($fixture['user'], $fixture['workspace'], $fixture['blockedEnvironment']); $blockedPermissionsPage = visit(ManagedEnvironmentLinks::requiredPermissionsUrl($fixture['blockedEnvironment'])) ->resize(1440, 1100) ->waitForText(__('localization.provider_guidance.required_permissions_missing_title')) ->assertScript( spec353BrowserTextContainsScript( '[data-testid="provider-readiness-reason"]', __('localization.provider_guidance.required_application_permissions_reason'), ), true, ) ->assertSee(__('localization.provider_guidance.action_open_admin_consent')) ->assertScript('document.querySelector("[data-testid=\"provider-readiness-details\"]")?.open === false', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec353BrowserScreenshot('01-required-permissions-blocked')); spec353CopyBrowserScreenshot('01-required-permissions-blocked'); $blockedPermissionsPage->resize(900, 1100) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); visit(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $fixture['blockedEnvironment'])) ->waitForText('Provider readiness blocks evidence refresh') ->click('[data-testid="tenant-dashboard-primary-next-action"] a') ->waitForText(__('localization.provider_guidance.required_permissions_missing_title')) ->assertScript( spec353BrowserTextContainsScript( '[data-testid="provider-readiness-reason"]', __('localization.provider_guidance.required_application_permissions_reason'), ), true, ) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); visit(ManagedEnvironmentLinks::providerConnectionUrl($fixture['failedConnection'], 'view', $fixture['failedEnvironment'])) ->resize(1440, 1100) ->waitForText(__('localization.provider_guidance.verification_failed_title')) ->assertSee(__('localization.provider_guidance.action_open_verification_operation')) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec353BrowserScreenshot('02-provider-connection-verification-failed')); spec353CopyBrowserScreenshot('02-provider-connection-verification-failed'); visit(ManagedEnvironmentLinks::providerConnectionUrl($fixture['readyConnection'], 'view', $fixture['readyEnvironment'])) ->resize(1440, 1100) ->waitForText(__('localization.provider_guidance.ready_title')) ->assertSee(__('localization.provider_guidance.ready_reason')) ->assertSee(__('localization.provider_guidance.action_open_environment_dashboard')) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec353BrowserScreenshot('03-provider-connection-ready')); spec353CopyBrowserScreenshot('03-provider-connection-ready'); });