browser()->timeout(60_000); function spec337BrowserScreenshotName(string $name): string { return 'spec337-evidence-review-pack-'.$name; } function spec337CopyBrowserScreenshot(string $name): void { $filename = spec337BrowserScreenshotName($name).'.png'; $source = base_path('tests/Browser/Screenshots/'.$filename); $targetDirectory = repo_path('specs/337-evidence-review-pack-product-process-flow-alignment/artifacts/screenshots'); if (! is_dir($targetDirectory)) { @mkdir($targetDirectory, 0755, true); } if (! is_file($source)) { $source = \Pest\Browser\Support\Screenshot::path($filename); } for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) { usleep(100_000); clearstatcache(true, $source); } if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) { @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png'); } } function spec337AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void { $workspaceId = (int) $environment->workspace_id; $test->actingAs($user)->withSession([ WorkspaceContext::SESSION_KEY => $workspaceId, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspaceId => (int) $environment->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [ (string) $workspaceId => (int) $environment->getKey(), ]); setAdminPanelContext($environment); } function spec337BrowserEnvironmentFor(User $user, ManagedEnvironment $baseEnvironment, string $name): ManagedEnvironment { $environment = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $baseEnvironment->workspace_id, 'name' => $name, ]); createUserWithTenant(tenant: $environment, user: $user, role: 'owner', workspaceRole: 'manager'); return $environment; } it('Spec337 smokes Evidence Review Pack product process flow states', function (): void { [$user, $missingEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $missingEnvironment->forceFill(['name' => 'Spec337 Evidence Snapshot Required'])->save(); $generatingEnvironment = spec337BrowserEnvironmentFor($user, $missingEnvironment, 'Spec337 Evidence Generating'); $generatingRun = OperationRun::factory()->forTenant($generatingEnvironment)->create([ 'type' => OperationRunType::EvidenceSnapshotGenerate->value, 'status' => OperationRunStatus::Running->value, 'outcome' => OperationRunOutcome::Pending->value, 'started_at' => now()->subMinute(), 'initiator_name' => 'Spec337 Browser Operator', ]); spec337BrowserCreateEvidenceSnapshot($generatingEnvironment, [ 'operation_run_id' => (int) $generatingRun->getKey(), 'status' => EvidenceSnapshotStatus::Generating->value, 'generated_at' => null, ]); $storedReportRequiredEnvironment = spec337BrowserEnvironmentFor($user, $missingEnvironment, 'Spec337 Stored Report Required'); spec337BrowserCreateEvidenceSnapshot($storedReportRequiredEnvironment); $reviewPackRequiredEnvironment = spec337BrowserEnvironmentFor($user, $missingEnvironment, 'Spec337 Review Pack Required'); spec337BrowserCreateEvidenceSnapshot($reviewPackRequiredEnvironment); spec337BrowserCreateStoredReport($reviewPackRequiredEnvironment); $availableEnvironment = spec337BrowserEnvironmentFor($user, $missingEnvironment, 'Spec337 Review Pack Available'); $availableSnapshot = spec337BrowserCreateEvidenceSnapshot($availableEnvironment); spec337BrowserCreateStoredReport($availableEnvironment); spec337BrowserCreatePublishedReviewWithReadyPack($availableEnvironment, $user, $availableSnapshot); $exportUnavailableEnvironment = spec337BrowserEnvironmentFor($user, $missingEnvironment, 'Spec337 Export Unavailable'); $exportUnavailableSnapshot = spec337BrowserCreateEvidenceSnapshot($exportUnavailableEnvironment); spec337BrowserCreateStoredReport($exportUnavailableEnvironment); ReviewPack::factory()->ready()->create([ 'managed_environment_id' => (int) $exportUnavailableEnvironment->getKey(), 'workspace_id' => (int) $exportUnavailableEnvironment->workspace_id, 'evidence_snapshot_id' => (int) $exportUnavailableSnapshot->getKey(), 'file_disk' => null, 'file_path' => null, 'file_size' => null, 'sha256' => null, ]); spec337AuthenticateBrowser($this, $user, $missingEnvironment); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $missingEnvironment->getKey(), ])) ->resize(1236, 862) ->waitForText('Evidence snapshot required') ->assertSee('Is this evidence package ready for customer or auditor consumption?') ->assertSee('Generate evidence snapshot') ->assertSee('Evidence readiness flow') ->assertScript('document.querySelectorAll("[data-testid=\"evidence-readiness-step\"]").length === 6', true) ->assertScript('(() => { const list = document.querySelector("[data-testid=\"evidence-readiness-flow\"] ol"); return list !== null && getComputedStyle(list).flexDirection === "column"; })()', true) ->assertScript('(() => { const steps = Array.from(document.querySelectorAll("[data-testid=\"evidence-readiness-step\"]")); return steps.length === 6 && steps.every((step) => step.getBoundingClientRect().width >= 300); })()', true) ->assertScript('document.querySelector("[data-step-label=\"Evidence snapshot\"]")?.dataset.stepState === "Missing"', true) ->assertScript('document.querySelector("[data-step-label=\"Evidence snapshot\"]")?.dataset.stepCurrentBlocker === "true"', true) ->assertScript('document.querySelector("[data-testid=\"evidence-disclosure-diagnostics\"]")?.open === false', true) ->assertDontSee('raw payload should stay hidden') ->assertDontSee('provider response should stay hidden') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('01-evidence-snapshot-required')); spec337CopyBrowserScreenshot('01-evidence-snapshot-required'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $generatingEnvironment->getKey(), ])) ->waitForText('Evidence generation in progress') ->assertSee('View operation progress') ->assertSee('Operation proof') ->assertSee('Spec337 Browser Operator') ->assertScript('document.querySelector("[data-step-label=\"Evidence snapshot\"]")?.dataset.stepState === "Generating"', true) ->assertScript('document.querySelector("[data-step-label=\"Evidence snapshot\"]")?.dataset.stepCurrentBlocker === "true"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('02-evidence-generating')); spec337CopyBrowserScreenshot('02-evidence-generating'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $storedReportRequiredEnvironment->getKey(), ])) ->waitForText('Stored report required') ->assertSee('Open evidence snapshot') ->assertScript('document.querySelector("[data-step-label=\"Stored report\"]")?.dataset.stepState === "Missing"', true) ->assertScript('document.querySelector("[data-step-label=\"Stored report\"]")?.dataset.stepCurrentBlocker === "true"', true) ->assertDontSee('Download export') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('03-stored-report-required')); spec337CopyBrowserScreenshot('03-stored-report-required'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $reviewPackRequiredEnvironment->getKey(), ])) ->waitForText('Review pack required') ->assertSee('Generate review pack') ->assertScript('document.querySelector("[data-step-label=\"Review pack\"]")?.dataset.stepState === "Required"', true) ->assertScript('document.querySelector("[data-step-label=\"Review pack\"]")?.dataset.stepCurrentBlocker === "true"', true) ->assertScript('document.querySelector("[data-testid=\"evidence-primary-proof-action\"]") instanceof HTMLAnchorElement', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('04-review-pack-required')); spec337CopyBrowserScreenshot('04-review-pack-required'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $availableEnvironment->getKey(), ])) ->waitForText('Review pack export available') ->assertSee('Download export') ->assertSee('Customer-safe output') ->assertSee('Review pack contents / coverage') ->assertScript('document.querySelector("[data-step-label=\"Review pack\"]")?.dataset.stepState === "Available"', true) ->assertScript('document.querySelector("[data-step-label=\"Customer-safe output\"]")?.dataset.stepState === "Ready"', true) ->assertScript('document.querySelector("[data-step-label=\"Export / delivery\"]")?.dataset.stepState === "Available"', true) ->assertScript('Array.from(document.querySelectorAll("[data-testid=\"evidence-review-pack-status-badge\"]")).every((badge) => !badge.innerText.includes("...") && getComputedStyle(badge).overflow !== "hidden" && getComputedStyle(badge).textOverflow !== "ellipsis")', true) ->assertDontSee('Auditor-ready') ->assertDontSee('environment is healthy') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('05-review-pack-available')); spec337CopyBrowserScreenshot('05-review-pack-available'); $page->screenshot(true, spec337BrowserScreenshotName('06-customer-safe-output-state')); spec337CopyBrowserScreenshot('06-customer-safe-output-state'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $exportUnavailableEnvironment->getKey(), ])) ->waitForText('Export unavailable') ->assertSee('Open review pack') ->assertScript('document.querySelector("[data-step-label=\"Export / delivery\"]")?.dataset.stepState === "Required"', true) ->assertDontSee('Download export') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec337BrowserScreenshotName('07-export-unavailable')); spec337CopyBrowserScreenshot('07-export-unavailable'); $page->assertScript('document.querySelector("[data-testid=\"evidence-disclosure-diagnostics\"]")?.open === false', true); $page->screenshot(true, spec337BrowserScreenshotName('08-diagnostics-collapsed')); spec337CopyBrowserScreenshot('08-diagnostics-collapsed'); $page->script("document.documentElement.classList.add('dark');"); $page->script('window.scrollTo(0, 0);'); $page->assertScript('document.documentElement.classList.contains("dark")', true); $page->screenshot(true, spec337BrowserScreenshotName('09-dark-mode')); spec337CopyBrowserScreenshot('09-dark-mode'); }); /** * @param array $attributes */ function spec337BrowserCreateEvidenceSnapshot(ManagedEnvironment $environment, array $attributes = []): EvidenceSnapshot { $snapshot = EvidenceSnapshot::query()->create(array_replace([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'status' => EvidenceSnapshotStatus::Active->value, 'fingerprint' => sha1('spec337-browser-snapshot-'.$environment->getKey().'-'.microtime(true)), 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'dimension_count' => 5, 'missing_dimensions' => 0, 'stale_dimensions' => 0, 'raw_payload' => 'raw payload should stay hidden', 'provider_response' => 'provider response should stay hidden', ], 'generated_at' => now(), 'expires_at' => now()->addDays(30), ], $attributes)); $snapshot->items()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'dimension_key' => 'permission_posture', 'state' => (string) ($snapshot->completeness_state ?: EvidenceCompletenessState::Complete->value), 'required' => true, 'source_kind' => 'stored_report', 'source_record_type' => StoredReport::class, 'source_record_id' => null, 'source_fingerprint' => null, 'measured_at' => now(), 'freshness_at' => now(), 'summary_payload' => [ 'label' => 'Permission posture', 'state' => 'complete', ], 'sort_order' => 1, ]); return $snapshot->refresh(); } function spec337BrowserCreateStoredReport(ManagedEnvironment $environment): StoredReport { return StoredReport::factory()->permissionPosture([ 'raw_payload' => 'raw payload should stay hidden', 'provider_response' => 'provider response should stay hidden', ])->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'fingerprint' => sha1('spec337-browser-report-'.$environment->getKey().'-'.microtime(true)), ]); } function spec337BrowserCreatePublishedReviewWithReadyPack( ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot, ): ReviewPack { $review = EnvironmentReview::query()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'published_by_user_id' => (int) $user->getKey(), 'fingerprint' => sha1('spec337-browser-review-'.$environment->getKey().'-'.microtime(true)), 'status' => EnvironmentReviewStatus::Published->value, 'completeness_state' => EnvironmentReviewCompletenessState::Complete->value, 'summary' => [ 'governance_package' => [ 'decision_summary' => [ 'status' => 'ready', 'evidence_state' => EnvironmentReviewCompletenessState::Complete->value, 'decision_data_state' => 'complete', ], ], ], 'generated_at' => now()->subMinutes(5), 'published_at' => now()->subMinutes(3), ]); $run = OperationRun::factory()->forTenant($environment)->create([ 'type' => OperationRunType::ReviewPackGenerate->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'started_at' => now()->subMinutes(4), 'completed_at' => now()->subMinutes(2), 'initiator_name' => $user->name, ]); $pack = ReviewPack::factory()->ready()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'environment_review_id' => (int) $review->getKey(), 'operation_run_id' => (int) $run->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'status' => ReviewPackStatus::Ready->value, 'summary' => [ 'finding_count' => 3, 'report_count' => 2, 'operation_count' => 1, 'section_count' => 4, 'evidence_resolution' => [ 'required_dimensions' => [ 'findings_summary', 'permission_posture', 'entra_admin_roles', ], ], ], ]); $review->forceFill([ 'current_export_review_pack_id' => (int) $pack->getKey(), ])->save(); return $pack->refresh(); }