221 lines
11 KiB
PHP
221 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\BaselineProfile;
|
|
use App\Models\BaselineTenantAssignment;
|
|
use App\Models\Finding;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Support\Baselines\BaselineCompareReasonCode;
|
|
use App\Support\ManagedEnvironmentLinks;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
pest()->browser()->timeout(60_000);
|
|
|
|
function spec336BrowserScreenshotName(string $name): string
|
|
{
|
|
return 'spec336-baseline-compare-'.$name;
|
|
}
|
|
|
|
function spec336CopyBrowserScreenshot(string $name): void
|
|
{
|
|
$filename = spec336BrowserScreenshotName($name).'.png';
|
|
$source = base_path('tests/Browser/Screenshots/'.$filename);
|
|
$targetDirectory = repo_path('specs/336-baseline-compare-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 spec336AuthenticateBrowser(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(),
|
|
]);
|
|
}
|
|
|
|
function spec336EnvironmentFor(User $user, ManagedEnvironment $baseEnvironment, string $name): ManagedEnvironment
|
|
{
|
|
$environment = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $baseEnvironment->workspace_id,
|
|
'name' => $name,
|
|
]);
|
|
|
|
createUserWithTenant(tenant: $environment, user: $user, role: 'owner', workspaceRole: 'manager');
|
|
|
|
return $environment;
|
|
}
|
|
|
|
it('Spec336 smokes Baseline Compare product process flow states', function (): void {
|
|
[$user, $noBaselineEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
createInventorySyncOperationRunWithCoverage($noBaselineEnvironment, ['deviceConfiguration' => 'succeeded']);
|
|
|
|
$snapshotMissingEnvironment = spec336EnvironmentFor($user, $noBaselineEnvironment, 'Spec336 Snapshot Missing');
|
|
$missingProfile = BaselineProfile::factory()->active()->create([
|
|
'workspace_id' => (int) $snapshotMissingEnvironment->workspace_id,
|
|
]);
|
|
BaselineTenantAssignment::factory()->create([
|
|
'workspace_id' => (int) $snapshotMissingEnvironment->workspace_id,
|
|
'managed_environment_id' => (int) $snapshotMissingEnvironment->getKey(),
|
|
'baseline_profile_id' => (int) $missingProfile->getKey(),
|
|
]);
|
|
|
|
$compareRequiredEnvironment = spec336EnvironmentFor($user, $noBaselineEnvironment, 'Spec336 Compare Required');
|
|
seedActiveBaselineForTenant($compareRequiredEnvironment);
|
|
createInventorySyncOperationRunWithCoverage($compareRequiredEnvironment, ['deviceConfiguration' => 'succeeded']);
|
|
|
|
$compareAvailableEnvironment = spec336EnvironmentFor($user, $noBaselineEnvironment, 'Spec336 Compare Available');
|
|
[$availableProfile, $availableSnapshot] = seedActiveBaselineForTenant($compareAvailableEnvironment);
|
|
$availableRun = seedBaselineCompareRun($compareAvailableEnvironment, $availableProfile, $availableSnapshot, [
|
|
'reason_code' => BaselineCompareReasonCode::OverdueFindingsRemain->value,
|
|
'coverage' => [
|
|
'effective_types' => ['deviceConfiguration'],
|
|
'covered_types' => ['deviceConfiguration'],
|
|
'uncovered_types' => [],
|
|
'proof' => true,
|
|
],
|
|
]);
|
|
Finding::factory()->create([
|
|
'managed_environment_id' => (int) $compareAvailableEnvironment->getKey(),
|
|
'workspace_id' => (int) $compareAvailableEnvironment->workspace_id,
|
|
'finding_type' => Finding::FINDING_TYPE_DRIFT,
|
|
'scope_key' => 'baseline_profile:'.$availableProfile->getKey(),
|
|
'severity' => Finding::SEVERITY_HIGH,
|
|
'status' => Finding::STATUS_NEW,
|
|
'source' => OperationRunType::BaselineCompare->value,
|
|
'baseline_operation_run_id' => (int) $availableRun->getKey(),
|
|
]);
|
|
|
|
$evidenceUnavailableEnvironment = spec336EnvironmentFor($user, $noBaselineEnvironment, 'Spec336 Evidence Unavailable');
|
|
[$evidenceProfile, $evidenceSnapshot] = seedActiveBaselineForTenant($evidenceUnavailableEnvironment);
|
|
$evidenceRun = seedBaselineCompareRun($evidenceUnavailableEnvironment, $evidenceProfile, $evidenceSnapshot, [
|
|
'reason_code' => BaselineCompareReasonCode::EvidenceCaptureIncomplete->value,
|
|
'coverage' => [
|
|
'effective_types' => ['deviceConfiguration'],
|
|
'covered_types' => ['deviceConfiguration'],
|
|
'uncovered_types' => [],
|
|
'proof' => true,
|
|
],
|
|
'evidence_gaps' => [
|
|
'count' => 1,
|
|
'by_reason' => [
|
|
'inventory_record_missing' => 1,
|
|
],
|
|
],
|
|
], OperationRunStatus::Completed->value, OperationRunOutcome::PartiallySucceeded->value);
|
|
Finding::factory()->create([
|
|
'managed_environment_id' => (int) $evidenceUnavailableEnvironment->getKey(),
|
|
'workspace_id' => (int) $evidenceUnavailableEnvironment->workspace_id,
|
|
'finding_type' => Finding::FINDING_TYPE_DRIFT,
|
|
'scope_key' => 'baseline_profile:'.$evidenceProfile->getKey(),
|
|
'severity' => Finding::SEVERITY_MEDIUM,
|
|
'status' => Finding::STATUS_NEW,
|
|
'source' => OperationRunType::BaselineCompare->value,
|
|
'baseline_operation_run_id' => (int) $evidenceRun->getKey(),
|
|
]);
|
|
|
|
spec336AuthenticateBrowser($this, $user, $noBaselineEnvironment);
|
|
|
|
$page = visit(ManagedEnvironmentLinks::baselineCompareUrl($noBaselineEnvironment))
|
|
->waitForText('Which baseline drift requires action?')
|
|
->assertSee('Baseline not assigned')
|
|
->assertSee('Open baseline profiles')
|
|
->assertSee('Compare readiness flow')
|
|
->assertSee('Available inputs')
|
|
->assertScript('document.querySelectorAll("[data-testid=\"baseline-compare-readiness-step\"]").length === 5', true)
|
|
->assertScript('document.querySelector("[data-step-label=\"Baseline assigned\"]")?.dataset.stepState === "Missing"', true)
|
|
->assertScript('document.querySelector("[data-testid=\"baseline-compare-diagnostics\"]")?.open === false', true)
|
|
->assertDontSee('raw diff')
|
|
->assertDontSee('raw payload')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec336BrowserScreenshotName('01-no-baseline-assigned'));
|
|
spec336CopyBrowserScreenshot('01-no-baseline-assigned');
|
|
|
|
$page = visit(ManagedEnvironmentLinks::baselineCompareUrl($snapshotMissingEnvironment))
|
|
->waitForText('Baseline snapshot required')
|
|
->assertSee('Open baseline profile')
|
|
->assertSee('No usable baseline snapshot input is linked.')
|
|
->assertScript('document.querySelector("[data-step-label=\"Baseline snapshot\"]")?.dataset.stepState === "Missing"', true)
|
|
->assertScript('document.querySelector("[data-step-label=\"Baseline snapshot\"]")?.dataset.stepCurrentBlocker === "true"', true)
|
|
->assertDontSee('raw diff')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec336BrowserScreenshotName('02-baseline-snapshot-required'));
|
|
spec336CopyBrowserScreenshot('02-baseline-snapshot-required');
|
|
|
|
$page = visit(ManagedEnvironmentLinks::baselineCompareUrl($compareRequiredEnvironment))
|
|
->waitForText('Compare run required')
|
|
->assertSee('Compare now')
|
|
->assertScript('document.querySelector("[data-step-label=\"Compare run\"]")?.dataset.stepState === "Required"', true)
|
|
->assertScript('document.querySelector("[data-step-label=\"Compare run\"]")?.dataset.stepCurrentBlocker === "true"', true)
|
|
->assertScript('document.body.innerText.includes("Total Findings") === false', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec336BrowserScreenshotName('03-compare-run-required'));
|
|
spec336CopyBrowserScreenshot('03-compare-run-required');
|
|
|
|
$page = visit(ManagedEnvironmentLinks::baselineCompareUrl($compareAvailableEnvironment))
|
|
->waitForText('Drift findings available')
|
|
->assertSee('Review drift findings')
|
|
->assertSee('OperationRun proof')
|
|
->assertScript('document.querySelector("[data-step-label=\"Compare run\"]")?.dataset.stepState === "Available"', true)
|
|
->assertScript('document.querySelector("[data-step-label=\"Decision output\"]")?.dataset.stepState === "Available"', true)
|
|
->assertScript('Array.from(document.querySelectorAll("[data-testid=\"baseline-compare-status-badge\"]")).every((badge) => !badge.innerText.includes("...") && getComputedStyle(badge).overflow !== "hidden" && getComputedStyle(badge).textOverflow !== "ellipsis")', true)
|
|
->assertDontSee('customer-safe')
|
|
->assertDontSee('raw diff')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec336BrowserScreenshotName('04-compare-result-available'));
|
|
spec336CopyBrowserScreenshot('04-compare-result-available');
|
|
|
|
$page = visit(ManagedEnvironmentLinks::baselineCompareUrl($evidenceUnavailableEnvironment))
|
|
->waitForText('Evidence unavailable - Evidence gaps need review')
|
|
->assertSee('Compare result exists, but evidence output is not available.')
|
|
->assertScript('document.querySelector("[data-step-label=\"Decision output\"]")?.dataset.stepState === "Needs review"', true)
|
|
->assertScript('document.querySelector("[data-testid=\"baseline-compare-diagnostics\"]")?.open === false', true)
|
|
->assertDontSee('raw diff')
|
|
->assertDontSee('raw payload')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec336BrowserScreenshotName('05-evidence-unavailable'));
|
|
spec336CopyBrowserScreenshot('05-evidence-unavailable');
|
|
|
|
$page->screenshot(true, spec336BrowserScreenshotName('06-diagnostics-collapsed'));
|
|
spec336CopyBrowserScreenshot('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, spec336BrowserScreenshotName('07-dark-mode'));
|
|
spec336CopyBrowserScreenshot('07-dark-mode');
|
|
});
|