Files
git.stella-ops.org/tests/e2e/playwright/evidence-panel-micro-interactions.spec.ts
master 2170a58734
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
Add comprehensive security tests for OWASP A02, A05, A07, and A08 categories
- Implemented tests for Cryptographic Failures (A02) to ensure proper handling of sensitive data, secure algorithms, and key management.
- Added tests for Security Misconfiguration (A05) to validate production configurations, security headers, CORS settings, and feature management.
- Developed tests for Authentication Failures (A07) to enforce strong password policies, rate limiting, session management, and MFA support.
- Created tests for Software and Data Integrity Failures (A08) to verify artifact signatures, SBOM integrity, attestation chains, and feed updates.
2025-12-16 16:40:44 +02:00

314 lines
11 KiB
TypeScript

/**
* Evidence Panel Micro-Interactions E2E Tests
* SPRINT_0341_0001_0001 - T8: Playwright tests for EvidencePanel micro-interactions
*
* Tests the "Verify locally" commands, copy affordances, and ProofSpine interactions.
*/
import { test, expect, Page } from '@playwright/test';
// Test fixtures for deterministic evidence data
const MOCK_EVIDENCE = {
digest: 'sha256:abc123def456',
artifactPurl: 'pkg:oci/myimage@sha256:abc123def456',
sbomDigest: 'sha256:sbom789012',
rekorLogIndex: 12345678,
rekorLogId: 'test-rekor-log-id',
bundleDigest: 'sha256:bundle456789'
};
test.describe('Evidence Panel - Verify Locally Commands', () => {
test.beforeEach(async ({ page }) => {
// Navigate to evidence panel with mock data
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
// Wait for evidence panel to load
await page.waitForSelector('.evidence-panel');
// Navigate to Linkset tab
await page.click('[role="tab"]:has-text("Linkset")');
await page.waitForSelector('.linkset-panel');
});
test('should display verify locally section when linkset has verification data', async ({ page }) => {
// Arrange - Wait for verify section
const verifySection = page.locator('.linkset-panel__verify');
// Assert
await expect(verifySection).toBeVisible();
await expect(verifySection.locator('h4')).toHaveText('Verify Locally');
await expect(verifySection.locator('.linkset-panel__verify-description')).toContainText(
'independently verify the evidence chain'
);
});
test('should display artifact signature verification command', async ({ page }) => {
// Arrange
const verifyCommands = page.locator('.verify-command');
// Find the artifact signature command
const signatureCommand = verifyCommands.filter({ hasText: 'Verify Artifact Signature' });
// Assert
await expect(signatureCommand).toBeVisible();
await expect(signatureCommand.locator('.verify-command__description')).toContainText('Cosign');
// The command should contain the artifact reference
const codeBlock = signatureCommand.locator('.verify-command__code code');
await expect(codeBlock).toContainText('cosign verify');
});
test('should display SBOM attestation verification command', async ({ page }) => {
// Arrange
const verifyCommands = page.locator('.verify-command');
// Find the SBOM attestation command
const sbomCommand = verifyCommands.filter({ hasText: 'Verify SBOM Attestation' });
// Assert
await expect(sbomCommand).toBeVisible();
await expect(sbomCommand.locator('.verify-command__description')).toContainText('attestation');
// The command should contain the predicate type
const codeBlock = sbomCommand.locator('.verify-command__code code');
await expect(codeBlock).toContainText('--type spdxjson');
});
test('should display Rekor transparency verification command when available', async ({ page }) => {
// Arrange
const verifyCommands = page.locator('.verify-command');
// Find the Rekor command
const rekorCommand = verifyCommands.filter({ hasText: 'Verify Transparency Log' });
// Assert
await expect(rekorCommand).toBeVisible();
await expect(rekorCommand.locator('.verify-command__description')).toContainText('Rekor');
// The command should contain rekor-cli
const codeBlock = rekorCommand.locator('.verify-command__code code');
await expect(codeBlock).toContainText('rekor-cli get');
});
test('should display policy decision verification command', async ({ page }) => {
// Arrange
const verifyCommands = page.locator('.verify-command');
// Find the policy command
const policyCommand = verifyCommands.filter({ hasText: 'Verify Policy Decision' });
// Assert
await expect(policyCommand).toBeVisible();
// The command should contain stella policy
const codeBlock = policyCommand.locator('.verify-command__code code');
await expect(codeBlock).toContainText('stella policy verify');
});
});
test.describe('Evidence Panel - Copy Interactions', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.waitForSelector('.evidence-panel');
await page.click('[role="tab"]:has-text("Linkset")');
await page.waitForSelector('.linkset-panel__verify');
});
test('should copy verification command on copy button click', async ({ page }) => {
// Arrange
const copyButton = page.locator('.verify-command__copy').first();
// Act
await copyButton.click();
// Assert - button should show "Copied!" state
await expect(copyButton).toHaveText('Copied!');
await expect(copyButton).toHaveClass(/copied/);
});
test('should reset copy button state after delay', async ({ page }) => {
// Arrange
const copyButton = page.locator('.verify-command__copy').first();
// Act
await copyButton.click();
await expect(copyButton).toHaveText('Copied!');
// Wait for reset (typically 2-3 seconds)
await page.waitForTimeout(3500);
// Assert - button should reset to "Copy"
await expect(copyButton).toHaveText('Copy');
await expect(copyButton).not.toHaveClass(/copied/);
});
test('should copy correct command text to clipboard', async ({ page, context }) => {
// Grant clipboard permissions
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
// Arrange
const firstCommand = page.locator('.verify-command').first();
const expectedCommand = await firstCommand.locator('.verify-command__code code').textContent();
// Act
await firstCommand.locator('.verify-command__copy').click();
// Assert - check clipboard content
const clipboardText = await page.evaluate(() => navigator.clipboard.readText());
expect(clipboardText).toBe(expectedCommand?.trim());
});
test('should be keyboard accessible', async ({ page }) => {
// Arrange
const copyButton = page.locator('.verify-command__copy').first();
// Act - focus and press Enter
await copyButton.focus();
await page.keyboard.press('Enter');
// Assert
await expect(copyButton).toHaveText('Copied!');
});
test('should have proper aria-label for copy button', async ({ page }) => {
// Arrange
const copyButton = page.locator('.verify-command__copy').first();
// Assert - initial state
await expect(copyButton).toHaveAttribute('aria-label', 'Copy command');
// Act
await copyButton.click();
// Assert - copied state
await expect(copyButton).toHaveAttribute('aria-label', 'Copied!');
});
});
test.describe('Evidence Panel - ProofSpine Component', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.waitForSelector('.evidence-panel');
await page.click('[role="tab"]:has-text("Linkset")');
});
test('should display bundle hash in ProofSpine', async ({ page }) => {
// The ProofSpine should show the evidence bundle digest
const proofSpine = page.locator('.linkset-panel__provenance');
await expect(proofSpine).toBeVisible();
// Check for bundle hash display
const bundleHash = proofSpine.locator('code').filter({ hasText: /sha256:/ });
await expect(bundleHash.first()).toBeVisible();
});
test('should truncate long hashes with copy on click', async ({ page }) => {
// Find truncated hash
const truncatedHash = page.locator('.linkset-panel__provenance code').first();
// Verify it shows truncated form
const text = await truncatedHash.textContent();
expect(text?.length).toBeLessThan(64); // SHA256 is 64 chars
});
});
test.describe('Evidence Panel - Tab Navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.waitForSelector('.evidence-panel');
});
test('should support keyboard navigation between tabs', async ({ page }) => {
// Focus first tab
const firstTab = page.locator('[role="tab"]').first();
await firstTab.focus();
// Press right arrow to move to next tab
await page.keyboard.press('ArrowRight');
// Verify focus moved
const focusedElement = page.locator(':focus');
await expect(focusedElement).toHaveAttribute('role', 'tab');
});
test('should announce tab content changes to screen readers', async ({ page }) => {
// The tabpanel should have proper aria attributes
const tabpanel = page.locator('[role="tabpanel"]');
await expect(tabpanel).toHaveAttribute('aria-label');
});
});
test.describe('Evidence Panel - Responsive Behavior', () => {
test('should stack verify commands on mobile viewport', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.waitForSelector('.evidence-panel');
await page.click('[role="tab"]:has-text("Linkset")');
// Verify commands container should be flex column
const commandsContainer = page.locator('.verify-commands');
const display = await commandsContainer.evaluate((el) =>
window.getComputedStyle(el).flexDirection
);
expect(display).toBe('column');
});
test('should wrap long command text on small screens', async ({ page }) => {
// Set small viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.click('[role="tab"]:has-text("Linkset")');
// Command code should wrap
const codeBlock = page.locator('.verify-command__code').first();
const whiteSpace = await codeBlock.evaluate((el) =>
window.getComputedStyle(el).whiteSpace
);
expect(whiteSpace).toBe('pre-wrap');
});
});
test.describe('Evidence Panel - Error States', () => {
test('should not show verify section when no verification data available', async ({ page }) => {
// Navigate to evidence without Rekor/signature data
await page.goto('/evidence?digest=sha256:nosig123');
await page.waitForSelector('.evidence-panel');
await page.click('[role="tab"]:has-text("Linkset")');
// Verify section should be hidden or empty
const verifySection = page.locator('.linkset-panel__verify');
// Either hidden or shows no commands
const verifyCommands = page.locator('.verify-command');
const count = await verifyCommands.count();
if (count === 0) {
await expect(verifySection).not.toBeVisible();
}
});
test('should handle clipboard API failure gracefully', async ({ page, context }) => {
// Deny clipboard permissions
await context.clearPermissions();
await page.goto('/evidence?digest=' + MOCK_EVIDENCE.digest);
await page.click('[role="tab"]:has-text("Linkset")');
// Click copy - should not crash
const copyButton = page.locator('.verify-command__copy').first();
await copyButton.click();
// Should show error state or fallback
// Implementation may vary - check it doesn't throw
await expect(page.locator('.evidence-panel')).toBeVisible();
});
});