Implemented the consolidated operator guidance panel for the environment dashboard. Updated EnvironmentDashboardSummaryBuilder to prioritize and select guidance based on the operator guidance contract. Added comprehensive unit, feature, and browser tests to verify the guidance selection logic and UI rendering. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #423
211 lines
8.9 KiB
PHP
211 lines
8.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\EnvironmentDashboard;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ManagedEnvironmentPermission;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\User;
|
|
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
pest()->browser()->timeout(20_000);
|
|
|
|
function spec352BrowserScreenshotName(string $name): string
|
|
{
|
|
return 'spec352-environment-dashboard-'.$name;
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* review:\App\Models\EnvironmentReview,
|
|
* successor:\App\Models\EnvironmentReview|null,
|
|
* reviewPack:ReviewPack,
|
|
* }
|
|
*/
|
|
function spec352BrowserSeedBlockedReviewOutput(ManagedEnvironment $environment, User $user, bool $withSuccessorDraft = false): array
|
|
{
|
|
$snapshot = seedPartialEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0, operationRunCount: 0);
|
|
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now()->subHour(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
'summary' => array_replace_recursive(is_array($review->summary) ? $review->summary : [], [
|
|
'publish_blockers' => ['Operator approval note is still missing.'],
|
|
]),
|
|
])->save();
|
|
|
|
$reviewPack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
'file_path' => 'review-packs/spec352-browser.zip',
|
|
'file_disk' => 'exports',
|
|
'generated_at' => now()->subMinutes(10),
|
|
'options' => [
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
]);
|
|
|
|
$review->forceFill([
|
|
'current_export_review_pack_id' => (int) $reviewPack->getKey(),
|
|
])->save();
|
|
|
|
$successor = null;
|
|
|
|
if ($withSuccessorDraft) {
|
|
$successor = app(EnvironmentReviewLifecycleService::class)->createNextReview($review->fresh(), $user, $snapshot);
|
|
}
|
|
|
|
return [
|
|
'review' => $review->fresh(),
|
|
'successor' => $successor?->fresh(),
|
|
'reviewPack' => $reviewPack->fresh(),
|
|
];
|
|
}
|
|
|
|
function spec352BrowserApplicationPermissionKey(): 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 spec352BrowserSeedPermissionRows(
|
|
ManagedEnvironment $environment,
|
|
array $missingKeys = [],
|
|
array $errorKeys = [],
|
|
): 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' => 'spec352-browser-test'],
|
|
'last_checked_at' => now(),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
function spec352BrowserActAs(User $user, ManagedEnvironment $environment): void
|
|
{
|
|
test()->actingAs($user)->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id,
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $environment->workspace_id => (int) $environment->getKey(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
it('smokes provider-blocker guidance as the dominant dashboard case', function (): void {
|
|
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
|
|
ProviderConnection::factory()->platform()->consentGranted()->create([
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
spec352BrowserSeedBlockedReviewOutput($environment, $user, withSuccessorDraft: true);
|
|
|
|
$missingPermissionKey = spec352BrowserApplicationPermissionKey();
|
|
spec352BrowserSeedPermissionRows($environment, missingKeys: [$missingPermissionKey]);
|
|
|
|
spec352BrowserActAs($user, $environment);
|
|
|
|
visit(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $environment))
|
|
->waitForText('Provider readiness blocks evidence refresh')
|
|
->assertSee('Recommended next action')
|
|
->assertSee('Review permissions')
|
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-primary-next-action\"]')?.textContent?.includes('Review permissions') ?? false", true)
|
|
->assertScript("Array.from(document.querySelectorAll('a')).filter((node) => node.textContent?.trim().includes('Review permissions')).length === 1", true)
|
|
->assertScript("document.querySelectorAll('[data-testid=\"tenant-dashboard-operator-guidance-secondary-action\"]').length === 0", true)
|
|
->assertDontSee('No single repo-real follow-up is currently available.')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec352BrowserScreenshotName('provider-blocker'));
|
|
});
|
|
|
|
it('smokes review-output guidance with subordinate secondary links when provider blockers are absent', function (): void {
|
|
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
|
|
ProviderConnection::factory()->platform()->consentGranted()->create([
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
spec352BrowserSeedBlockedReviewOutput($environment, $user, withSuccessorDraft: true);
|
|
spec352BrowserSeedPermissionRows($environment);
|
|
|
|
spec352BrowserActAs($user, $environment);
|
|
|
|
visit(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $environment))
|
|
->waitForText('Draft review exists')
|
|
->assertSee('Open draft review')
|
|
->assertSee('Additional follow-ups')
|
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-primary-next-action\"]')?.textContent?.includes('Open draft review') ?? false", true)
|
|
->assertScript("Array.from(document.querySelectorAll('a')).filter((node) => node.textContent?.trim().includes('Open draft review')).length === 1", true)
|
|
->assertScript("document.querySelectorAll('[data-testid=\"tenant-dashboard-operator-guidance-secondary-action\"]').length >= 1", true)
|
|
->assertScript("document.querySelector('[data-recommended-actions-style=\"compact\"]') !== null", true)
|
|
->assertDontSee('No single repo-real follow-up is currently available.')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec352BrowserScreenshotName('review-output'));
|
|
});
|
|
|
|
it('smokes the no-urgent-action dashboard state with preserved secondary proof surfaces', function (): void {
|
|
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
|
|
ProviderConnection::factory()->platform()->consentGranted()->create([
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
spec352BrowserSeedPermissionRows($environment);
|
|
workspaceOverviewSeedQuietTenantTruth($environment);
|
|
$backupSet = workspaceOverviewSeedHealthyBackup($environment);
|
|
workspaceOverviewSeedRestoreHistory($environment, $backupSet, 'completed');
|
|
|
|
spec352BrowserActAs($user, $environment);
|
|
|
|
visit(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $environment))
|
|
->waitForText('No urgent operator action')
|
|
->assertSee('Review environment')
|
|
->assertSee('Readiness proof')
|
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-recommended-actions-empty\"]') !== null", true)
|
|
->assertScript("Array.from(document.querySelectorAll('a')).filter((node) => node.textContent?.trim().includes('Review environment')).length === 1", true)
|
|
->assertDontSee('No single repo-real follow-up is currently available.')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec352BrowserScreenshotName('no-urgent-action'));
|
|
});
|