todays product advirories implemented
This commit is contained in:
293
src/Web/StellaOps.Web/tests/e2e/ux-components-visual.spec.ts
Normal file
293
src/Web/StellaOps.Web/tests/e2e/ux-components-visual.spec.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// ux-components-visual.spec.ts
|
||||
// Sprint: SPRINT_20260117_018_FE_ux_components
|
||||
// Task: UXC-008 - Integration tests with Playwright
|
||||
// Description: Visual regression tests for new UX components
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { policyAuthorSession } from '../../src/app/testing';
|
||||
|
||||
const mockConfig = {
|
||||
authority: {
|
||||
issuer: 'https://authority.local',
|
||||
clientId: 'stellaops-ui',
|
||||
authorizeEndpoint: 'https://authority.local/connect/authorize',
|
||||
tokenEndpoint: 'https://authority.local/connect/token',
|
||||
logoutEndpoint: 'https://authority.local/connect/logout',
|
||||
redirectUri: 'http://127.0.0.1:4400/auth/callback',
|
||||
postLogoutRedirectUri: 'http://127.0.0.1:4400/',
|
||||
scope: 'openid profile email ui.read findings:read vuln:view binary:read',
|
||||
audience: 'https://scanner.local',
|
||||
dpopAlgorithms: ['ES256'],
|
||||
refreshLeewaySeconds: 60,
|
||||
},
|
||||
apiBaseUrls: {
|
||||
authority: 'https://authority.local',
|
||||
scanner: 'https://scanner.local',
|
||||
policy: 'https://scanner.local',
|
||||
concelier: 'https://concelier.local',
|
||||
attestor: 'https://attestor.local',
|
||||
},
|
||||
quickstartMode: true,
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript((session) => {
|
||||
try {
|
||||
window.sessionStorage.clear();
|
||||
} catch {
|
||||
// ignore storage errors in restricted contexts
|
||||
}
|
||||
(window as any).__stellaopsTestSession = session;
|
||||
}, policyAuthorSession);
|
||||
|
||||
await page.route('**/config.json', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(mockConfig),
|
||||
})
|
||||
);
|
||||
|
||||
await page.route('https://authority.local/**', (route) => route.abort());
|
||||
});
|
||||
|
||||
test.describe('UX Components Visual Regression', () => {
|
||||
test.describe('Triage Card', () => {
|
||||
test('default state screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.triage-card').first()).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Wait for any animations to complete
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Take screenshot of first triage card
|
||||
await expect(page.locator('.triage-card').first()).toHaveScreenshot('triage-card-default.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('hover state screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
const card = page.locator('.triage-card').first();
|
||||
await expect(card).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Hover over card
|
||||
await card.hover();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await expect(card).toHaveScreenshot('triage-card-hover.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('expanded verification state screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
const card = page.locator('.triage-card').first();
|
||||
await expect(card).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Click Rekor Verify and wait for expansion
|
||||
await page.getByRole('button', { name: /Rekor Verify/ }).first().click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Screenshot expanded state
|
||||
await expect(card).toHaveScreenshot('triage-card-expanded.png', {
|
||||
maxDiffPixelRatio: 0.05,
|
||||
});
|
||||
});
|
||||
|
||||
test('risk chip variants screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.risk-chip').first()).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Screenshot all risk chips
|
||||
const riskChips = page.locator('.risk-chip');
|
||||
for (let i = 0; i < Math.min(4, await riskChips.count()); i++) {
|
||||
await expect(riskChips.nth(i)).toHaveScreenshot(`risk-chip-variant-${i}.png`, {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Filter Strip', () => {
|
||||
test('default state screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.filter-strip')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.filter-strip')).toHaveScreenshot('filter-strip-default.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('with filters active screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.filter-strip')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Activate some filters
|
||||
await page.getByLabel(/Only reachable/i).check();
|
||||
await page.locator('#epss-slider').fill('50');
|
||||
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await expect(page.locator('.filter-strip')).toHaveScreenshot('filter-strip-active.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('deterministic toggle states screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.filter-strip')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
const toggle = page.locator('.determinism-toggle');
|
||||
|
||||
// Active state (default)
|
||||
await expect(toggle).toHaveScreenshot('determinism-toggle-active.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
|
||||
// Inactive state
|
||||
await toggle.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await expect(toggle).toHaveScreenshot('determinism-toggle-inactive.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Binary-Diff Panel', () => {
|
||||
test('default state screenshot', async ({ page }) => {
|
||||
await page.goto('/binary/diff');
|
||||
await expect(page.locator('.binary-diff-panel')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.binary-diff-panel')).toHaveScreenshot('binary-diff-panel-default.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('scope selector states screenshot', async ({ page }) => {
|
||||
await page.goto('/binary/diff');
|
||||
await expect(page.locator('.binary-diff-panel')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
const scopeSelector = page.locator('.scope-selector');
|
||||
|
||||
// File scope (default)
|
||||
await expect(scopeSelector).toHaveScreenshot('scope-selector-file.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
|
||||
// Section scope
|
||||
await page.getByRole('button', { name: /Section/i }).click();
|
||||
await page.waitForTimeout(300);
|
||||
await expect(scopeSelector).toHaveScreenshot('scope-selector-section.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
|
||||
// Function scope
|
||||
await page.getByRole('button', { name: /Function/i }).click();
|
||||
await page.waitForTimeout(300);
|
||||
await expect(scopeSelector).toHaveScreenshot('scope-selector-function.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('tree item change indicators screenshot', async ({ page }) => {
|
||||
await page.goto('/binary/diff');
|
||||
await expect(page.locator('.binary-diff-panel')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
const tree = page.locator('.scope-tree');
|
||||
|
||||
await expect(tree).toHaveScreenshot('diff-tree-items.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('diff view lines screenshot', async ({ page }) => {
|
||||
await page.goto('/binary/diff');
|
||||
await expect(page.locator('.binary-diff-panel')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Select an entry to show diff
|
||||
await page.locator('.tree-item').first().click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
const diffView = page.locator('.diff-view');
|
||||
|
||||
await expect(diffView).toHaveScreenshot('diff-view-lines.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Dark Mode', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Enable dark mode
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
});
|
||||
|
||||
test('triage card dark mode screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.triage-card').first()).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.triage-card').first()).toHaveScreenshot('triage-card-dark.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('filter strip dark mode screenshot', async ({ page }) => {
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.filter-strip')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.filter-strip')).toHaveScreenshot('filter-strip-dark.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
|
||||
test('binary diff panel dark mode screenshot', async ({ page }) => {
|
||||
await page.goto('/binary/diff');
|
||||
await expect(page.locator('.binary-diff-panel')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.binary-diff-panel')).toHaveScreenshot('binary-diff-panel-dark.png', {
|
||||
maxDiffPixelRatio: 0.02,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Responsive', () => {
|
||||
test('filter strip mobile viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.filter-strip')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.filter-strip')).toHaveScreenshot('filter-strip-mobile.png', {
|
||||
maxDiffPixelRatio: 0.05,
|
||||
});
|
||||
});
|
||||
|
||||
test('triage card mobile viewport', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.goto('/triage/findings');
|
||||
await expect(page.locator('.triage-card').first()).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('.triage-card').first()).toHaveScreenshot('triage-card-mobile.png', {
|
||||
maxDiffPixelRatio: 0.05,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user