browser()->timeout(60_000); it('Spec329 smokes Evidence Overview proof-first disclosure and filter clearing', function (): void { $fixture = spec329BrowserDisclosureFixture(); spec329AuthenticateDisclosureBrowser($this, $fixture['user'], $fixture['environmentA']); $cleanPath = json_encode((string) parse_url(route('admin.evidence.overview'), PHP_URL_PATH), JSON_THROW_ON_ERROR); $page = visit(route('admin.evidence.overview')) ->resize(1440, 1100) ->waitForText('What proof is available for this scope?') ->assertDontSee(__('localization.shell.no_environment_selected')) ->assertDontSee('Environment filter:') ->assertSee('Evidence proof workbench') ->assertSee('Primary proof path') ->assertSee('Evidence path') ->assertSee('Evidence snapshot') ->assertSee('Review pack') ->assertSee('Stored report / export') ->assertSee('Operation proof') ->assertSee('Proof incomplete') ->assertSee('A proof record exists, but no usable captured evidence is available yet.') ->assertSee('Primary evidence snapshot is empty.') ->assertSee('Supporting proof exists through the review pack, stored report, and operation record.') ->assertSee('Empty') ->assertSee('Ready') ->assertSee('Available') ->assertSourceHas('Search evidence or next step') ->assertSee('Evidence inventory') ->assertSee($fixture['environmentA']->name) ->assertSee($fixture['environmentB']->name) ->assertDontSee('The artifact row exists, but it does not contain usable captured content.') ->assertDontSee('artifact row exists') ->assertSourceMissing('Search tenant or next') ->assertDontSee('Empty...') ->assertDontSee('Re...') ->assertDontSee('raw payload should stay hidden') ->assertDontSee('provider secret should stay hidden') ->assertDontSee('stack trace should stay hidden') ->assertDontSee('debug metadata should stay hidden') ->assertDontSee('internal exception should stay hidden') ->assertDontSee('current tenant') ->assertDontSee('tenant filter') ->assertDontSee('entitled tenant') ->assertDontSee('all tenants') ->assertScript('document.querySelector("[data-testid=\"evidence-disclosure-diagnostics\"]")?.open === false', true) ->assertScript('(() => { const action = document.querySelector("[data-testid=\"evidence-primary-proof-action\"]"); if (! action) { return false; } const box = action.getBoundingClientRect(); return action.textContent.trim() === "Open evidence snapshot" && box.height > 0 && box.height <= 44 && action.scrollWidth <= Math.ceil(action.clientWidth) + 1 && getComputedStyle(action).whiteSpace === "nowrap"; })()', true) ->assertScript('(() => { const badges = Array.from(document.querySelectorAll("[data-testid=\"evidence-path-state-badge\"]")); return badges.length >= 4 && badges.every((badge) => { const label = badge.textContent.trim(); const box = badge.getBoundingClientRect(); return ! ["Empty...", "Re..."].includes(label) && box.width > 0 && badge.scrollWidth <= Math.ceil(badge.clientWidth) + 1; }); })()', true) ->assertScript('(() => { const grid = document.querySelector("[data-testid=\"evidence-disclosure-workbench\"]"); const main = document.querySelector("[data-testid=\"evidence-proof-primary\"]"); const aside = document.querySelector("[data-testid=\"evidence-proof-aside\"]"); if (! grid || ! main || ! aside) { return false; } const children = Array.from(grid.children); const mainBox = main.getBoundingClientRect(); const asideBox = aside.getBoundingClientRect(); return window.innerWidth >= 1024 && grid.classList.contains("lg:grid-cols-[minmax(0,1fr)_22rem]") && aside.tagName === "ASIDE" && children.indexOf(main) !== -1 && children.indexOf(aside) > children.indexOf(main) && asideBox.left > mainBox.right && Math.abs(asideBox.top - mainBox.top) <= 8; })()', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('evidence-overview--clean')); spec329CopyDisclosureScreenshot('evidence-overview--clean'); $page = visit(route('admin.evidence.overview', [ 'environment_id' => (int) $fixture['environmentA']->getKey(), ])) ->waitForText('Environment filter:') ->assertSee('Environment filter: '.$fixture['environmentA']->name) ->assertSee('What proof is available for this scope?') ->assertSee($fixture['environmentA']->name) ->assertSee('Proof incomplete') ->assertSee('Primary evidence snapshot is empty.') ->assertDontSee('The artifact row exists, but it does not contain usable captured content.') ->assertDontSee('Empty...') ->assertDontSee('Re...') ->assertDontSee($fixture['environmentB']->name) ->assertScript('document.querySelector("[data-testid=\"evidence-disclosure-diagnostics\"]")?.open === false', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('evidence-overview--filtered')); spec329CopyDisclosureScreenshot('evidence-overview--filtered'); spec329ClearDisclosureEnvironmentFilter($page) ->waitForText($fixture['environmentB']->name) ->assertDontSee('Environment filter:') ->waitForText($fixture['environmentB']->name) ->assertScript("window.location.pathname === {$cleanPath}", true) ->assertScript('! window.location.search.includes("environment_id=")', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('evidence-overview--after-clear')); spec329CopyDisclosureScreenshot('evidence-overview--after-clear'); $page->script('window.location.reload();'); $page ->waitForText($fixture['environmentB']->name) ->assertDontSee('Environment filter:') ->assertSee($fixture['environmentB']->name) ->assertScript("window.location.pathname === {$cleanPath}", true) ->assertScript('! window.location.search.includes("environment_id=")', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('evidence-overview--after-reload')); spec329CopyDisclosureScreenshot('evidence-overview--after-reload'); }); it('Spec329 smokes Audit Log event-proof disclosure and filter clearing', function (): void { $fixture = spec329BrowserDisclosureFixture(); spec329AuthenticateDisclosureBrowser($this, $fixture['user'], $fixture['environmentA']); $cleanPath = json_encode((string) parse_url(route('admin.monitoring.audit-log'), PHP_URL_PATH), JSON_THROW_ON_ERROR); $page = visit(route('admin.monitoring.audit-log', [ 'event' => (int) $fixture['auditA']->getKey(), ])) ->resize(1440, 1100) ->waitForText('Which event proves what happened?') ->assertDontSee(__('localization.shell.no_environment_selected')) ->assertDontSee('Environment filter:') ->assertSee('Audit proof workbench') ->assertSee('Selected event proof') ->assertSee('Event proof') ->assertSee('Actor') ->assertSee('Action') ->assertSee('Target') ->assertSee('Outcome') ->assertSee('Time') ->assertSee('Related proof') ->assertSee('Operation proof') ->assertSee('Readable context') ->assertSee('Audit event history') ->assertSee('Spec329 Browser Operator A') ->assertSee('Permission posture checked') ->assertSee('Permission posture report') ->assertDontSee('raw payload should stay hidden') ->assertDontSee('provider secret should stay hidden') ->assertDontSee('stack trace should stay hidden') ->assertDontSee('debug metadata should stay hidden') ->assertDontSee('internal exception should stay hidden') ->assertDontSee('provider response should stay hidden') ->assertDontSee('current tenant') ->assertDontSee('tenant filter') ->assertDontSee('entitled tenant') ->assertDontSee('all tenants') ->assertScript('document.querySelector("[data-testid=\"audit-disclosure-diagnostics\"]")?.open === false', true) ->assertScript('document.querySelector("[data-testid=\"audit-event-diagnostics\"]")?.open === false', true) ->assertScript('(() => { const grid = document.querySelector("[data-testid=\"audit-disclosure-workbench\"]"); const main = document.querySelector("[data-testid=\"audit-proof-primary\"]"); const aside = document.querySelector("[data-testid=\"audit-proof-aside\"]"); if (! grid || ! main || ! aside) { return false; } const children = Array.from(grid.children); const mainBox = main.getBoundingClientRect(); const asideBox = aside.getBoundingClientRect(); return window.innerWidth >= 1024 && grid.classList.contains("lg:grid-cols-[minmax(0,1fr)_22rem]") && aside.tagName === "ASIDE" && children.indexOf(main) !== -1 && children.indexOf(aside) > children.indexOf(main) && asideBox.left > mainBox.right && Math.abs(asideBox.top - mainBox.top) <= 8; })()', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('audit-log--clean')); spec329CopyDisclosureScreenshot('audit-log--clean'); $page = visit(route('admin.monitoring.audit-log', [ 'environment_id' => (int) $fixture['environmentA']->getKey(), 'event' => (int) $fixture['auditA']->getKey(), ])) ->waitForText('Environment filter:') ->assertSee('Environment filter: '.$fixture['environmentA']->name) ->assertSee('Which event proves what happened?') ->assertSee('Permission posture checked') ->assertDontSee('Workspace selected by browser proof B') ->assertScript('document.querySelector("[data-testid=\"audit-disclosure-diagnostics\"]")?.open === false', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('audit-log--filtered')); spec329CopyDisclosureScreenshot('audit-log--filtered'); spec329ClearDisclosureEnvironmentFilter($page) ->waitForText('Workspace selected by browser proof B') ->assertDontSee('Environment filter:') ->waitForText('Workspace selected by browser proof B') ->assertScript("window.location.pathname === {$cleanPath}", true) ->assertScript('! window.location.search.includes("environment_id=")', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('audit-log--after-clear')); spec329CopyDisclosureScreenshot('audit-log--after-clear'); $page->script('window.location.reload();'); $page ->waitForText('Workspace selected by browser proof B') ->assertDontSee('Environment filter:') ->assertSee('Workspace selected by browser proof B') ->assertScript("window.location.pathname === {$cleanPath}", true) ->assertScript('! window.location.search.includes("environment_id=")', true) ->assertNoJavaScriptErrors() ->assertNoConsoleLogs() ->screenshot(true, spec329DisclosureScreenshot('audit-log--after-reload')); spec329CopyDisclosureScreenshot('audit-log--after-reload'); }); /** * @return array{ * user: User, * environmentA: ManagedEnvironment, * environmentB: ManagedEnvironment, * auditA: AuditLog, * auditB: AuditLog * } */ function spec329BrowserDisclosureFixture(): array { $environmentA = ManagedEnvironment::factory()->active()->create([ 'name' => 'Spec329 Browser Tenant Environment A', 'external_id' => 'spec329-browser-environment-a', ]); [$user, $environmentA] = createUserWithTenant( tenant: $environmentA, role: 'owner', workspaceRole: 'owner', ); $environmentB = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $environmentA->workspace_id, 'name' => 'Spec329 Browser Environment B', 'external_id' => 'spec329-browser-environment-b', ]); createUserWithTenant( tenant: $environmentB, user: $user, role: 'owner', workspaceRole: 'owner', ); $runA = OperationRun::factory()->forTenant($environmentA)->create([ 'type' => 'tenant.evidence.snapshot.generate', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'raw_payload' => 'raw payload should stay hidden', 'provider_secret' => 'provider secret should stay hidden', 'stack_trace' => 'stack trace should stay hidden', 'debug_metadata' => 'debug metadata should stay hidden', 'internal_exception' => 'internal exception should stay hidden', ], 'completed_at' => now()->subMinutes(10), ]); $runB = OperationRun::factory()->forTenant($environmentB)->create([ 'type' => 'policy.sync', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subMinutes(5), ]); $snapshotA = EvidenceSnapshot::query()->create([ 'managed_environment_id' => (int) $environmentA->getKey(), 'workspace_id' => (int) $environmentA->workspace_id, 'operation_run_id' => (int) $runA->getKey(), 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Complete->value, 'summary' => [ 'missing_dimensions' => 0, 'stale_dimensions' => 0, 'raw_payload' => 'raw payload should stay hidden', ], 'generated_at' => now()->subMinutes(4), 'expires_at' => now()->addDays(30), ]); EvidenceSnapshot::query()->create([ 'managed_environment_id' => (int) $environmentB->getKey(), 'workspace_id' => (int) $environmentB->workspace_id, 'operation_run_id' => (int) $runB->getKey(), 'status' => EvidenceSnapshotStatus::Active->value, 'completeness_state' => EvidenceCompletenessState::Partial->value, 'summary' => [ 'missing_dimensions' => 1, 'stale_dimensions' => 0, ], 'generated_at' => now()->subMinutes(9), 'expires_at' => now()->addDays(30), ]); ReviewPack::factory()->ready()->create([ 'managed_environment_id' => (int) $environmentA->getKey(), 'workspace_id' => (int) $environmentA->workspace_id, 'evidence_snapshot_id' => (int) $snapshotA->getKey(), 'operation_run_id' => (int) $runA->getKey(), 'status' => ReviewPackStatus::Ready->value, ]); $storedReport = StoredReport::factory()->permissionPosture([ 'raw_payload' => 'raw payload should stay hidden', ])->create([ 'managed_environment_id' => (int) $environmentA->getKey(), 'workspace_id' => (int) $environmentA->workspace_id, 'fingerprint' => 'spec329-browser-report', ]); $auditA = AuditLog::query()->create([ 'workspace_id' => (int) $environmentA->workspace_id, 'managed_environment_id' => (int) $environmentA->getKey(), 'operation_run_id' => (int) $runA->getKey(), 'actor_email' => 'spec329-a@example.test', 'actor_name' => 'Spec329 Browser Operator A', 'actor_type' => 'human', 'action' => 'permission_posture.checked', 'status' => 'success', 'resource_type' => 'stored_report', 'resource_id' => (string) $storedReport->getKey(), 'target_label' => 'Permission posture report', 'summary' => 'Permission posture checked', 'metadata' => [ 'reason' => 'Evidence review', 'raw_payload' => 'raw payload should stay hidden', 'provider_secret' => 'provider secret should stay hidden', 'stack_trace' => 'stack trace should stay hidden', 'debug_metadata' => 'debug metadata should stay hidden', 'internal_exception' => 'internal exception should stay hidden', 'provider_response' => 'provider response should stay hidden', ], 'recorded_at' => now()->subMinutes(2), ]); $auditB = AuditLog::query()->create([ 'workspace_id' => (int) $environmentB->workspace_id, 'managed_environment_id' => (int) $environmentB->getKey(), 'actor_email' => 'spec329-b@example.test', 'actor_name' => 'Spec329 Browser Operator B', 'actor_type' => 'human', 'action' => 'workspace.selected', 'status' => 'success', 'resource_type' => 'workspace', 'resource_id' => (string) $environmentB->workspace_id, 'target_label' => 'Workspace '.$environmentB->workspace_id, 'summary' => 'Workspace selected by browser proof B', 'recorded_at' => now()->subMinute(), ]); return [ 'user' => $user, 'environmentA' => $environmentA, 'environmentB' => $environmentB, 'auditA' => $auditA, 'auditB' => $auditB, ]; } function spec329AuthenticateDisclosureBrowser( mixed $test, User $user, ManagedEnvironment $rememberedEnvironment, ): void { $workspaceId = (int) $rememberedEnvironment->workspace_id; $session = [ WorkspaceContext::SESSION_KEY => $workspaceId, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspaceId => (int) $rememberedEnvironment->getKey(), ], ]; $test->actingAs($user)->withSession($session); foreach ($session as $key => $value) { session()->put($key, $value); } setAdminPanelContext($rememberedEnvironment); } function spec329ClearDisclosureEnvironmentFilter(mixed $page): mixed { $page->assertScript('document.querySelector(\'[data-testid="workspace-hub-environment-filter-clear"]\') instanceof HTMLAnchorElement', true); $page->script('window.location.assign(document.querySelector(\'[data-testid="workspace-hub-environment-filter-clear"]\').href);'); return $page; } function spec329DisclosureScreenshot(string $name): string { return 'spec329-'.$name; } function spec329CopyDisclosureScreenshot(string $name, ?string $targetFilename = null): void { $filename = spec329DisclosureScreenshot($name).'.png'; $source = base_path('tests/Browser/Screenshots/'.$filename); $targetDirectory = repo_path('specs/329-evidence-audit-log-disclosure-productization/artifacts/screenshots'); $targetFilename ??= $filename; if (! is_dir($targetDirectory)) { @mkdir($targetDirectory, 0755, true); } if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) { return; } if (! is_file($source)) { $source = \Pest\Browser\Support\Screenshot::path($filename); } if (is_file($source)) { @copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$targetFilename); } }