265 lines
11 KiB
PHP
265 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Governance\GovernanceInbox;
|
|
use App\Models\Finding;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
pest()->browser()->timeout(60_000);
|
|
|
|
it('Spec327 smokes non-empty governance inbox decision workbench entry', function (): void {
|
|
[$user, $environmentA, $environmentB] = spec327GovernanceInboxFixture();
|
|
spec327AuthenticateGovernanceInboxBrowser($this, $user, $environmentA);
|
|
|
|
visit(GovernanceInbox::getUrl(panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Governance Inbox')
|
|
->assertSee('Prioritized governance decisions, owners, evidence, and follow-up actions across entitled environments.')
|
|
->assertSee(__('localization.shell.no_environment_selected'))
|
|
->assertDontSee('Environment filter:')
|
|
->assertSee('What decision clears the highest-priority item?')
|
|
->assertSee('Decision workbench')
|
|
->assertSee('Decision summary')
|
|
->assertSee('Finding #2')
|
|
->assertSee('Reason')
|
|
->assertSee('The finding reopened after a previous resolution path.')
|
|
->assertSee('Impact')
|
|
->assertSee('Medium drift')
|
|
->assertSee('Owner')
|
|
->assertSee('Owner missing')
|
|
->assertSee('Due')
|
|
->assertSee('In 14 days')
|
|
->assertSee('Evidence missing')
|
|
->assertSee('Evidence path')
|
|
->assertSee('Source record only')
|
|
->assertSee('Accepted risk')
|
|
->assertSee('Primary next action')
|
|
->assertSee('Triage finding')
|
|
->assertSee('No accepted risk')
|
|
->assertSee('Queue context')
|
|
->assertSee($environmentA->name)
|
|
->assertSee($environmentB->name)
|
|
->assertDontSee('No governance decisions need attention')
|
|
->assertDontSee('tenant filter')
|
|
->assertDontSee('current tenant')
|
|
->assertDontSee('entitled tenant')
|
|
->assertDontSee('all tenants')
|
|
->assertDontSee('raw payload should stay hidden')
|
|
->assertDontSee('stack trace should stay hidden')
|
|
->assertDontSee('provider secret should stay hidden')
|
|
->assertDontSee('debug metadata should stay hidden')
|
|
->assertScript('document.querySelector("[data-testid=\"governance-inbox-diagnostics\"]")?.open === false', true)
|
|
->assertScript('(() => {
|
|
const grid = document.querySelector("[data-testid=\"governance-inbox-decision-workbench\"]");
|
|
const workbench = document.querySelector("[data-testid=\"governance-inbox-priority-card\"]");
|
|
const detail = document.querySelector("[data-testid=\"governance-inbox-decision-detail\"]");
|
|
|
|
if (! grid || ! workbench || ! detail) {
|
|
return false;
|
|
}
|
|
|
|
const children = Array.from(grid.children);
|
|
const workbenchBox = workbench.getBoundingClientRect();
|
|
const detailBox = detail.getBoundingClientRect();
|
|
|
|
return window.innerWidth >= 1024
|
|
&& grid.classList.contains("lg:grid-cols-[minmax(0,1fr)_22rem]")
|
|
&& detail.tagName === "ASIDE"
|
|
&& children.indexOf(workbench) !== -1
|
|
&& children.indexOf(detail) > children.indexOf(workbench)
|
|
&& detailBox.left > workbenchBox.right
|
|
&& Math.abs(detailBox.top - workbenchBox.top) <= 8;
|
|
})()', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec327GovernanceInboxScreenshot('governance-inbox--clean'));
|
|
|
|
spec327CopyBrowserScreenshot('governance-inbox--clean');
|
|
spec327CopyBrowserScreenshot('governance-inbox--clean', 'governance-inbox-decision-workbench.png');
|
|
});
|
|
|
|
it('Spec327 smokes filtered governance inbox clear and reload behavior', function (): void {
|
|
[$user, $environmentA, $environmentB] = spec327GovernanceInboxFixture();
|
|
$cleanPath = json_encode((string) parse_url(GovernanceInbox::getUrl(panel: 'admin'), PHP_URL_PATH), JSON_THROW_ON_ERROR);
|
|
spec327AuthenticateGovernanceInboxBrowser($this, $user, $environmentA);
|
|
|
|
$page = visit(GovernanceInbox::getUrl(panel: 'admin', parameters: [
|
|
'environment_id' => (int) $environmentA->getKey(),
|
|
]))
|
|
->waitForText('Environment filter:')
|
|
->assertSee('Environment filter: '.$environmentA->name)
|
|
->assertSee('What decision clears the highest-priority item?')
|
|
->assertSee($environmentA->name)
|
|
->assertDontSee($environmentB->name)
|
|
->assertScript('document.querySelector("[data-testid=\"governance-inbox-diagnostics\"]")?.open === false', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec327GovernanceInboxScreenshot('governance-inbox--filtered'));
|
|
|
|
spec327CopyBrowserScreenshot('governance-inbox--filtered');
|
|
|
|
$page
|
|
->click('[data-testid="workspace-hub-environment-filter-clear"]')
|
|
->waitForText(__('localization.shell.no_environment_selected'))
|
|
->assertDontSee('Environment filter:')
|
|
->assertSee($environmentB->name)
|
|
->assertScript("window.location.pathname === {$cleanPath}", true)
|
|
->assertScript('! window.location.search.includes("environment_id=")', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec327GovernanceInboxScreenshot('governance-inbox--after-clear'));
|
|
|
|
spec327CopyBrowserScreenshot('governance-inbox--after-clear');
|
|
|
|
$page->script('window.location.reload();');
|
|
|
|
$page
|
|
->waitForText(__('localization.shell.no_environment_selected'))
|
|
->assertDontSee('Environment filter:')
|
|
->assertSee($environmentB->name)
|
|
->assertScript("window.location.pathname === {$cleanPath}", true)
|
|
->assertScript('! window.location.search.includes("environment_id=")', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec327GovernanceInboxScreenshot('governance-inbox--after-reload'));
|
|
|
|
spec327CopyBrowserScreenshot('governance-inbox--after-reload');
|
|
});
|
|
|
|
it('Spec327 smokes governance inbox diagnostics disclosure and secondary queue', function (): void {
|
|
[$user, $environmentA] = spec327GovernanceInboxFixture();
|
|
spec327AuthenticateGovernanceInboxBrowser($this, $user, $environmentA);
|
|
|
|
visit(GovernanceInbox::getUrl(panel: 'admin'))
|
|
->waitForText('Queue context')
|
|
->assertSee('Assigned findings')
|
|
->assertScript('document.querySelector("[data-testid=\"governance-inbox-diagnostics\"]")?.open === false', true)
|
|
->click('[data-testid="governance-inbox-diagnostics"] summary')
|
|
->assertScript('document.querySelector("[data-testid=\"governance-inbox-diagnostics\"]")?.open === true', true)
|
|
->assertSee('Source diagnostics and raw support details stay on authorized source surfaces')
|
|
->assertDontSee('raw payload should stay hidden')
|
|
->assertDontSee('internal exception should stay hidden')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec327GovernanceInboxScreenshot('governance-inbox--diagnostics'));
|
|
|
|
spec327CopyBrowserScreenshot('governance-inbox--diagnostics');
|
|
});
|
|
|
|
/**
|
|
* @return array{0: User, 1: ManagedEnvironment, 2: ManagedEnvironment}
|
|
*/
|
|
function spec327GovernanceInboxFixture(): array
|
|
{
|
|
$environmentA = ManagedEnvironment::factory()->active()->create([
|
|
'name' => 'Spec327 Browser Environment A',
|
|
'external_id' => 'spec327-browser-environment-a',
|
|
]);
|
|
|
|
[$user, $environmentA] = createUserWithTenant(
|
|
tenant: $environmentA,
|
|
role: 'owner',
|
|
workspaceRole: 'owner',
|
|
);
|
|
|
|
$environmentB = ManagedEnvironment::factory()->active()->create([
|
|
'workspace_id' => (int) $environmentA->workspace_id,
|
|
'name' => 'Spec327 Browser Environment B',
|
|
'external_id' => 'spec327-browser-environment-b',
|
|
]);
|
|
|
|
createUserWithTenant(
|
|
tenant: $environmentB,
|
|
user: $user,
|
|
role: 'owner',
|
|
workspaceRole: 'owner',
|
|
);
|
|
|
|
Finding::factory()
|
|
->for($environmentA)
|
|
->assignedTo((int) $user->getKey())
|
|
->ownedBy((int) $user->getKey())
|
|
->overdueByHours()
|
|
->create([
|
|
'workspace_id' => (int) $environmentA->workspace_id,
|
|
'subject_external_id' => 'spec327-browser-priority-a',
|
|
'severity' => Finding::SEVERITY_HIGH,
|
|
'evidence_jsonb' => [
|
|
'summary' => [
|
|
'kind' => 'policy_snapshot',
|
|
'raw_payload' => 'raw payload should stay hidden',
|
|
'stack_trace' => 'stack trace should stay hidden',
|
|
'provider_secret' => 'provider secret should stay hidden',
|
|
'debug_metadata' => 'debug metadata should stay hidden',
|
|
'internal_exception' => 'internal exception should stay hidden',
|
|
],
|
|
],
|
|
]);
|
|
|
|
Finding::factory()
|
|
->for($environmentB)
|
|
->reopened()
|
|
->create([
|
|
'workspace_id' => (int) $environmentB->workspace_id,
|
|
'subject_external_id' => 'spec327-browser-secondary-b',
|
|
'severity' => Finding::SEVERITY_MEDIUM,
|
|
'owner_user_id' => null,
|
|
'assignee_user_id' => null,
|
|
'due_at' => now()->addDays(14),
|
|
'evidence_jsonb' => [],
|
|
]);
|
|
|
|
return [$user, $environmentA, $environmentB];
|
|
}
|
|
|
|
function spec327AuthenticateGovernanceInboxBrowser(
|
|
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 spec327GovernanceInboxScreenshot(string $name): string
|
|
{
|
|
return 'spec327-'.$name;
|
|
}
|
|
|
|
function spec327CopyBrowserScreenshot(string $name, ?string $targetFilename = null): void
|
|
{
|
|
$filename = spec327GovernanceInboxScreenshot($name).'.png';
|
|
$source = \Pest\Browser\Support\Screenshot::path($filename);
|
|
$targetDirectory = repo_path('specs/327-governance-inbox-decision-first-workbench-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)) {
|
|
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$targetFilename);
|
|
}
|
|
}
|