browser()->timeout(60_000); beforeEach(function (): void { Storage::fake('exports'); }); it('Spec342 smokes final customer review consumption states', function (): void { [$user, $notReadyEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $notReadyEnvironment->forceFill(['name' => 'Spec342 Browser Evidence Incomplete'])->save(); $readyEnvironment = spec342BrowserEnvironmentFor($user, $notReadyEnvironment, 'Spec342 Browser Ready'); $findingsEnvironment = spec342BrowserEnvironmentFor($user, $notReadyEnvironment, 'Spec342 Browser Findings'); $acceptedRiskEnvironment = spec342BrowserEnvironmentFor($user, $notReadyEnvironment, 'Spec342 Browser Accepted Risks'); spec342BrowserCreatePublishedReviewWithPack( $notReadyEnvironment, $user, seedPartialEnvironmentReviewEvidence($notReadyEnvironment, findingCount: 0, driftCount: 0), [ 'debug_payload' => 'raw payload should stay hidden', 'provider_response' => 'provider response should stay hidden', 'stack_trace' => 'stack trace should stay hidden', 'source_fingerprint' => 'spec342-browser-hidden-fingerprint', 'governance_package' => [ 'decision_summary' => [ 'status' => 'incomplete', 'evidence_state' => EnvironmentReviewCompletenessState::Partial->value, 'decision_data_state' => 'incomplete', 'total_count' => 1, 'summary' => 'Decision evidence is incomplete for this released review.', 'next_action' => 'Review the evidence basis before relying on the decision summary.', 'entries' => [], ], ], ], 'review-packs/spec342-browser-evidence-incomplete.zip', normalizeOutputReadiness: false, ); spec342BrowserCreatePublishedReviewWithPack( $readyEnvironment, $user, seedEnvironmentReviewEvidence($readyEnvironment, findingCount: 0, driftCount: 0), [], 'review-packs/spec342-browser-ready.zip', ); spec342BrowserCreatePublishedReviewWithPack( $findingsEnvironment, $user, seedEnvironmentReviewEvidence($findingsEnvironment, findingCount: 0, driftCount: 0), [], 'review-packs/spec342-browser-findings.zip', ); Finding::factory()->create([ 'managed_environment_id' => (int) $findingsEnvironment->getKey(), 'workspace_id' => (int) $findingsEnvironment->workspace_id, 'severity' => Finding::SEVERITY_CRITICAL, 'status' => Finding::STATUS_NEW, ]); spec342BrowserCreatePublishedReviewWithPack( $acceptedRiskEnvironment, $user, seedEnvironmentReviewEvidence($acceptedRiskEnvironment, findingCount: 0, driftCount: 0), [ 'governance_package' => [ 'accepted_risks' => [ [ 'title' => 'Accepted risk renewal', 'governance_state' => 'expiring_exception', 'customer_summary' => 'Accepted risk requires customer awareness.', ], ], ], ], 'review-packs/spec342-browser-accepted-risk.zip', ); spec342BrowserCreateAcceptedRisk($acceptedRiskEnvironment, $user); spec342AuthenticateBrowser($this, $user, $notReadyEnvironment); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($notReadyEnvironment)) ->resize(1236, 900) ->waitForText('Output not customer-ready') ->assertSee('Review blockers are still recorded for this output.') ->assertSee('Needs review') ->assertSee('Download review pack with limitations') ->assertSee('Review consumption flow') ->assertScript('document.querySelectorAll("[data-testid=\"customer-review-readiness-step\"]").length === 6', true) ->assertScript('document.querySelector("[data-step-label=\"Review pack\"]")?.dataset.stepState === "Available"', true) ->assertScript('document.querySelector("[data-step-label=\"Customer-safe output\"]")?.dataset.stepState === "Needs review"', true) ->assertScript('document.querySelector("[data-testid=\"customer-review-diagnostics\"]")?.open === false', true) ->assertDontSee('raw payload should stay hidden') ->assertDontSee('provider response should stay hidden') ->assertDontSee('stack trace should stay hidden') ->assertDontSee('spec342-browser-hidden-fingerprint') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec342BrowserScreenshotName('01-evidence-incomplete-not-ready')); spec342CopyBrowserScreenshot('01-evidence-incomplete-not-ready'); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($readyEnvironment)) ->waitForText('Customer-safe review pack ready') ->assertSee('Stakeholders can use the current review pack and released review as the evidence path.') ->assertSee('Download customer-safe review pack') ->assertSee('Review pack state') ->assertSee('Export ready') ->assertSee('Operation proof') ->assertSee('Spec342 Browser Operator') ->assertSee('No open findings require customer action.') ->assertScript('document.querySelectorAll("[data-testid=\"customer-review-primary-action\"]").length === 1', true) ->assertScript('document.querySelector("[data-testid=\"customer-review-evidence-path-panel\"]")?.innerText.includes("Download review pack") === false', true) ->assertScript('document.querySelector("[data-testid=\"customer-review-secondary-action\"]")?.innerText.includes("Download customer-safe review pack") === false', true) ->assertScript('document.querySelectorAll("[data-testid=\"customer-review-readiness-step\"]").length === 6', true) ->assertScript('document.querySelector("[data-step-label=\"Customer-safe output\"]")?.dataset.stepState === "Ready"', true) ->assertScript('Array.from(document.querySelectorAll("[data-testid=\"customer-review-readiness-step\"] [class*=\"badge\"], [data-testid=\"customer-review-review-pack-panel\"] [class*=\"badge\"], [data-testid=\"customer-review-accepted-risk-panel\"] [class*=\"badge\"]")).every((badge) => ! badge.innerText.includes("..."))', true) ->assertScript('document.body.innerHTML.includes("source_surface=customer_review_workspace")', true) ->assertScript('! document.body.innerHTML.includes("/admin/t/") && ! window.location.search.includes("tenant_id=")', true) ->assertDontSee('Auditor-ready') ->assertDontSee('environment is healthy') ->assertDontSee('compliant') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec342BrowserScreenshotName('02-ready-with-evidence')); spec342CopyBrowserScreenshot('02-ready-with-evidence'); $page->script('document.querySelector("[data-testid=\"customer-review-review-pack-panel\"]")?.scrollIntoView({ block: "center" });'); $page ->assertSee('Export availability') ->assertSee('Available'); $page->screenshot(true, spec342BrowserScreenshotName('03-review-pack-available')); spec342CopyBrowserScreenshot('03-review-pack-available'); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($findingsEnvironment)) ->waitForText('Findings needing attention') ->assertSee('Published with limitations') ->assertSee('1 open finding needs attention; 1 is high impact.') ->assertSee('Keep open findings visible before customer handoff.') ->assertSee('Do not treat this review as share-ready until open findings are resolved, accepted, or explicitly reviewed.') ->assertSee('High impact') ->assertSee('Open review') ->assertSee('Download review pack with limitations') ->assertScript('document.querySelector("[data-testid=\"customer-review-decision-card\"]")?.innerText.includes("Download review pack with limitations") === true', true) ->assertScript('document.querySelector("[data-testid=\"customer-review-evidence-path-panel\"]")?.innerText.includes("Download review pack") === false', true) ->assertScript('document.querySelector("[data-step-label=\"Findings triaged\"]")?.dataset.stepState === "Needs review"', true) ->assertScript('document.querySelector("[data-step-label=\"Findings triaged\"]")?.dataset.stepCurrent === "true"', true) ->assertScript('document.querySelector("[data-step-label=\"Customer-safe output\"]")?.dataset.stepState === "Needs review"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec342BrowserScreenshotName('04-findings-need-attention')); spec342CopyBrowserScreenshot('04-findings-need-attention'); $page = visit(CustomerReviewWorkspace::environmentFilterUrl($acceptedRiskEnvironment)) ->waitForText('Published with limitations') ->assertSee('Accepted-risk follow-up is recorded for this review. Review the owner, rationale, and review date before handoff.') ->assertSee('The pack can be shared only with the accepted-risk context included in the customer handoff.') ->assertSee('Open review') ->assertSee('Follow-up required') ->assertSee('Accepted-risk accountability') ->assertSee('Spec342 Browser Risk Owner') ->assertSee('Customer-approved maintenance window.') ->assertSee('Review date not recorded') ->assertSee('Accepted risk requires customer awareness.') ->assertSee('Download review pack with limitations') ->assertScript('document.querySelector("[data-testid=\"customer-review-decision-card\"]")?.innerText.includes("Download review pack with limitations") === true', true) ->assertScript('document.querySelector("[data-step-label=\"Accepted risks reviewed\"]")?.dataset.stepCurrent === "true"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $page->screenshot(true, spec342BrowserScreenshotName('05-accepted-risks-present')); spec342CopyBrowserScreenshot('05-accepted-risks-present'); $page->assertScript('document.querySelector("[data-testid=\"customer-review-diagnostics\"]")?.open === false', true); $page->screenshot(true, spec342BrowserScreenshotName('06-diagnostics-collapsed')); spec342CopyBrowserScreenshot('06-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, spec342BrowserScreenshotName('07-dark-mode')); spec342CopyBrowserScreenshot('07-dark-mode'); }); function spec342BrowserScreenshotName(string $name): string { return 'spec342-customer-review-workspace-'.$name; } function spec342CopyBrowserScreenshot(string $name): void { $filename = spec342BrowserScreenshotName($name).'.png'; $source = base_path('tests/Browser/Screenshots/'.$filename); $targetDirectory = repo_path('specs/342-customer-review-workspace-final-consumption-productization/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 spec342AuthenticateBrowser(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 spec342BrowserEnvironmentFor(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; } /** * @param array $summaryOverrides * @return array{0: EnvironmentReview, 1: ReviewPack} */ function spec342BrowserCreatePublishedReviewWithPack( ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot, array $summaryOverrides = [], string $filePath = 'review-packs/spec342-browser-review-pack.zip', bool $normalizeOutputReadiness = true, ): array { $review = composeEnvironmentReviewForTest($environment, $user, $snapshot); $summary = array_replace_recursive( is_array($review->summary) ? $review->summary : [], [ 'control_interpretation' => [ 'version_key' => ComplianceEvidenceMappingV1::VERSION_KEY, 'controls' => [ [ 'control_key' => 'customer-output', 'title' => 'Customer output', 'readiness_bucket' => 'evidence_on_record', 'readiness_label' => 'Evidence on record', 'primary_reason' => 'Evidence path is complete.', 'recommended_next_action' => 'Open the current customer review pack.', ], ], ], 'governance_package' => [ 'decision_summary' => [ 'status' => 'none', 'evidence_state' => EnvironmentReviewCompletenessState::Complete->value, 'decision_data_state' => 'complete', 'total_count' => 0, 'summary' => 'No governance decisions require customer awareness.', 'next_action' => 'Open the current customer review pack.', 'entries' => [], ], ], ], $summaryOverrides, ); $run = OperationRun::factory()->forTenant($environment)->create([ 'type' => OperationRunType::ReviewPackGenerate->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'started_at' => now()->subMinutes(6), 'completed_at' => now()->subMinutes(4), 'initiator_name' => 'Spec342 Browser Operator', ]); Storage::disk('exports')->put($filePath, 'PK-spec342-browser-test'); $review->forceFill([ 'status' => EnvironmentReviewStatus::Published->value, 'completeness_state' => EnvironmentReviewCompletenessState::Complete->value, 'summary' => $summary, 'operation_run_id' => (int) $run->getKey(), 'generated_at' => now()->subMinutes(5), 'published_at' => now()->subMinutes(3), 'published_by_user_id' => (int) $user->getKey(), ])->save(); if ($normalizeOutputReadiness) { $review = markEnvironmentReviewCustomerSafeReady($review); } $pack = 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(), 'operation_run_id' => (int) $run->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'status' => ReviewPackStatus::Ready->value, 'options' => [ 'include_pii' => false, 'include_operations' => true, ], 'file_path' => $filePath, 'file_disk' => 'exports', 'generated_at' => now()->subMinutes(4), ]); $review->forceFill([ 'current_export_review_pack_id' => (int) $pack->getKey(), ])->save(); return [$review->refresh(), $pack->refresh()]; } function spec342BrowserCreateAcceptedRisk(ManagedEnvironment $environment, User $user): void { $owner = User::factory()->create(['name' => 'Spec342 Browser Risk Owner']); $findingWithReviewDate = Finding::factory()->riskAccepted()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, ]); $findingWithoutReviewDate = Finding::factory()->riskAccepted()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, ]); FindingException::query()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'finding_id' => (int) $findingWithReviewDate->getKey(), 'status' => FindingException::STATUS_ACTIVE, 'current_validity_state' => FindingException::VALIDITY_VALID, 'requested_by_user_id' => (int) $user->getKey(), 'request_reason' => 'Customer-approved maintenance window.', 'owner_user_id' => (int) $owner->getKey(), 'approved_by_user_id' => (int) $owner->getKey(), 'requested_at' => now()->subDays(3), 'approved_at' => now()->subDays(2), 'effective_from' => now()->subDays(2), 'review_due_at' => now()->addDays(30), ]); FindingException::query()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'finding_id' => (int) $findingWithoutReviewDate->getKey(), 'status' => FindingException::STATUS_ACTIVE, 'current_validity_state' => FindingException::VALIDITY_EXPIRING, 'requested_by_user_id' => (int) $user->getKey(), 'request_reason' => 'Pending owner confirmation.', 'requested_at' => now()->subDay(), 'approved_at' => now()->subDay(), 'effective_from' => now()->subDay(), 'review_due_at' => null, 'expires_at' => null, ]); }