browser()->timeout(90_000); beforeEach(function (): void { Storage::fake('exports'); }); it('Spec366 smokes management report layout variants and print behavior', function (): void { [$user, $customerEnvironment, $customerReview, $customerPack] = spec366BrowserCreateLayoutPack( environmentName: 'Spec366 Browser Customer', customerSafeReady: true, ); [$limitedUser, $limitedEnvironment, $limitedReview, $limitedPack] = spec366BrowserCreateLayoutPack( user: $user, workspaceId: (int) $customerEnvironment->workspace_id, environmentName: 'Spec366 Browser Limited', customerSafeReady: true, partialEvidence: true, ); [$internalUser, $internalEnvironment, $internalReview, $internalPack] = spec366BrowserCreateLayoutPack( user: $user, workspaceId: (int) $customerEnvironment->workspace_id, environmentName: 'Spec366 Browser Internal', customerSafeReady: true, packOverrides: [ 'options' => [ 'include_pii' => true, 'include_operations' => true, ], ], ); spec366BrowserAuthenticate($this, $user, $customerEnvironment); $customerExecutivePage = visit(spec366BrowserRenderedReportUrl($customerPack, $customerReview, ReportProfileRegistry::CUSTOMER_EXECUTIVE)) ->resize(1280, 1440) ->waitForText(__('localization.review.report_profile_customer_executive')) ->assertSee(__('localization.review.report_kpi_key_risks')) ->assertSee(__('localization.review.report_kpi_open_decisions')) ->assertSee('Spec366 Browser Control') ->assertSee(__('localization.review.report_appendix_hidden_for_profile')) ->assertDontSee('Spec366 Browser Technical Appendix') ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.layoutMode === "executive"', true) ->assertScript('Boolean(document.querySelector("[data-testid=\"rendered-report-toolbar\"]").compareDocumentPosition(document.querySelector("[data-testid=\"rendered-report-canvas\"]")) & Node.DOCUMENT_POSITION_FOLLOWING)', true) ->assertScript('(() => { const control = document.querySelector("[data-testid=\"rendered-report-toolbar\"] a, [data-testid=\"rendered-report-print-action\"]"); control?.focus(); return document.activeElement === control; })()', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $customerExecutivePage->screenshot(true, spec366BrowserScreenshotName('01-customer-executive-report')); spec366CopyBrowserScreenshot('01-customer-executive-report'); $customerExecutivePage->script('document.body.classList.add("print-preview-smoke"); window.scrollTo(0, 0);'); $customerExecutivePage ->assertScript('window.getComputedStyle(document.querySelector("[data-testid=\"rendered-report-toolbar\"]")).display === "none"', true) ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").offsetParent !== null', true) ->assertScript('document.querySelector("[data-testid=\"rendered-report-disclosure\"]") instanceof HTMLElement', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); $customerExecutivePage->screenshot(true, spec366BrowserScreenshotName('06-print-view')); spec366CopyBrowserScreenshot('06-print-view'); $customerExecutivePage->screenshot(false, spec366BrowserScreenshotName('07-report-toolbar-hidden-print')); spec366CopyBrowserScreenshot('07-report-toolbar-hidden-print'); $customerExecutivePage->script('document.body.classList.remove("print-preview-smoke");'); visit(spec366BrowserRenderedReportUrl($limitedPack, $limitedReview, ReportProfileRegistry::CUSTOMER_EXECUTIVE)) ->resize(1280, 1440) ->waitForText(__('localization.review.report_state_with_limitations')) ->assertSee(__('localization.review.report_external_sharing_warning')) ->assertSee(__('localization.review.output_limitations')) ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.layoutMode === "executive"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec366BrowserScreenshotName('02-customer-executive-limited-report')); spec366CopyBrowserScreenshot('02-customer-executive-limited-report'); visit(spec366BrowserRenderedReportUrl($internalPack, $internalReview, ReportProfileRegistry::INTERNAL_MSP_REVIEW)) ->resize(1280, 1440) ->waitForText(__('localization.review.report_profile_internal_msp_review')) ->assertSee(__('localization.review.report_state_internal_with_limitations')) ->assertSee('Spec366 Browser Technical Appendix') ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.layoutMode === "internal"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec366BrowserScreenshotName('03-internal-msp-report')); spec366CopyBrowserScreenshot('03-internal-msp-report'); visit(spec366BrowserRenderedReportUrl($customerPack, $customerReview, ReportProfileRegistry::CUSTOMER_TECHNICAL)) ->resize(390, 1100) ->waitForText(__('localization.review.report_profile_customer_technical')) ->assertSee(__('localization.review.report_kpi_key_risks')) ->assertSee('Spec366 Browser Technical Appendix') ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.layoutMode === "technical"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec366BrowserScreenshotName('04-customer-technical-report')); spec366CopyBrowserScreenshot('04-customer-technical-report'); visit(spec366BrowserRenderedReportUrl($customerPack, $customerReview, ReportProfileRegistry::AUDITOR_APPENDIX)) ->resize(1280, 1440) ->waitForText(__('localization.review.report_profile_auditor_appendix')) ->assertSee(__('localization.review.report_audience_controlled_auditor')) ->assertSee('Spec366 Browser Technical Appendix') ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.layoutMode === "auditor_appendix"', true) ->assertScript('document.querySelector("[data-testid=\"rendered-report-canvas\"]").dataset.appendixProminence === "high"', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec366BrowserScreenshotName('05-auditor-appendix-report')); spec366CopyBrowserScreenshot('05-auditor-appendix-report'); expect($limitedUser)->toBeInstanceOf(User::class) ->and($limitedEnvironment)->toBeInstanceOf(ManagedEnvironment::class) ->and($internalUser)->toBeInstanceOf(User::class) ->and($internalEnvironment)->toBeInstanceOf(ManagedEnvironment::class); }); function spec366BrowserScreenshotName(string $name): string { return $name; } function spec366CopyBrowserScreenshot(string $name): void { $filename = spec366BrowserScreenshotName($name).'.png'; $primarySource = base_path('tests/Browser/Screenshots/'.$filename); $fallbackSource = \Pest\Browser\Support\Screenshot::path($filename); $targetDirectories = array_values(array_unique([ repo_path('specs/366-management-report-layout-branded-report-themes-v1/artifacts/screenshots'), base_path('specs/366-management-report-layout-branded-report-themes-v1/artifacts/screenshots'), ])); foreach ($targetDirectories as $targetDirectory) { if (! is_dir($targetDirectory)) { @mkdir($targetDirectory, 0755, true); } } $source = null; for ($attempt = 0; $attempt < 50 && $source === null; $attempt++) { foreach ([$primarySource, $fallbackSource] as $candidate) { if (is_file($candidate)) { $source = $candidate; break; } } if ($source !== null) { break; } usleep(100_000); clearstatcache(true, $primarySource); clearstatcache(true, $fallbackSource); } if (is_string($source) && is_file($source)) { foreach ($targetDirectories as $targetDirectory) { if (is_dir($targetDirectory) && is_writable($targetDirectory)) { @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png'); } } } } function spec366BrowserAuthenticate(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 spec366BrowserRenderedReportUrl(ReviewPack $pack, EnvironmentReview $review, string $profile): string { return app(ReviewPackService::class)->generateRenderedReportUrl($pack, [ 'source_surface' => 'review_pack', 'review_id' => (int) $review->getKey(), 'interpretation_version' => $review->controlInterpretationVersion(), ReportProfileRegistry::QUERY_PARAMETER => $profile, ]); } /** * @param array|null $packOverrides * @return array{0:User,1:ManagedEnvironment,2:EnvironmentReview,3:ReviewPack} */ function spec366BrowserCreateLayoutPack( ?User $user = null, ?int $workspaceId = null, string $environmentName = 'Spec366 Browser Environment', bool $customerSafeReady = false, bool $partialEvidence = false, ?array $packOverrides = [], ): array { $environment = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => $workspaceId, 'name' => $environmentName, ]); if ($user === null) { [$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager'); } else { createUserWithTenant(tenant: $environment, user: $user, role: 'owner', workspaceRole: 'manager'); } $environment->workspace?->forceFill(['name' => 'Spec366 Browser MSP'])->save(); $environment = $environment->fresh('workspace'); $snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0); $review = composeEnvironmentReviewForTest($environment, $user, $snapshot); $review->forceFill([ 'status' => 'published', 'published_at' => now(), 'published_by_user_id' => (int) $user->getKey(), ])->save(); if ($customerSafeReady) { $review = markEnvironmentReviewCustomerSafeReady($review); } if ($partialEvidence) { restateEnvironmentReviewEvidenceSnapshot($review->evidenceSnapshot, EvidenceCompletenessState::Partial); $review = $review->fresh(['sections', 'evidenceSnapshot']); } $review->loadMissing('sections'); $appendixSection = $review->sections->first(); if ($appendixSection instanceof EnvironmentReviewSection) { $appendixSection->forceFill([ 'render_payload' => array_replace_recursive( is_array($appendixSection->render_payload) ? $appendixSection->render_payload : [], [ 'entries' => [ [ 'title' => 'Spec366 Browser Technical Appendix', 'summary' => 'Visible in technical, internal, and auditor layouts.', ], ], 'highlights' => ['Spec366 browser appendix highlight.'], ], ), ])->save(); } $filePath = 'review-packs/'.($environment->external_id ?: 'spec366').'/browser-report.zip'; Storage::disk('exports')->put($filePath, 'PK-spec366-browser-report'); $summary = array_replace_recursive([ 'governance_package' => [ 'executive_summary' => 'Spec 366 browser management report.', 'top_findings' => [ [ 'title' => 'Spec366 Browser Control', 'summary' => 'A management-visible control requires owner awareness.', ], ], 'accepted_risks' => [], 'decision_summary' => [ 'status' => 'open', 'summary' => 'A governance decision still requires stakeholder awareness.', 'next_action' => 'Confirm the owner review date.', 'entries' => [ [ 'title' => 'Confirm owner review', 'summary' => 'Owner review remains the next governance step.', ], ], ], ], 'control_interpretation' => [ 'non_certification_disclosure' => 'Spec 366 browser non-certification disclosure.', ], 'recommended_next_actions' => [ 'Confirm the owner review date.', ], 'delivery_bundle' => [ 'executive_entrypoint_file' => 'executive-summary.md', 'appendix_files' => ['metadata.json', 'summary.json', 'sections.json'], ], ], is_array($packOverrides['summary'] ?? null) ? $packOverrides['summary'] : []); $pack = ReviewPack::factory()->ready()->create(array_merge([ '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(), 'options' => [ 'include_pii' => false, 'include_operations' => true, ], 'summary' => $summary, 'file_path' => $filePath, 'file_disk' => 'exports', 'generated_at' => now()->subMinutes(3), 'expires_at' => now()->addDay(), ], $packOverrides ?? [])); $review->forceFill([ 'current_export_review_pack_id' => (int) $pack->getKey(), ])->save(); return [$user, $environment, $review->fresh(['sections', 'evidenceSnapshot', 'currentExportReviewPack']), $pack->fresh()]; }