Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -0,0 +1,318 @@
/**
* Binary Resolution UI E2E Tests.
* Sprint: SPRINT_1227_0003_0001 (Backport-Aware Resolution UI)
* Task: T9 - E2E tests
*
* Tests the complete binary resolution flow:
* 1. Viewing a vulnerability with binary resolution
* 2. Opening the evidence drawer
* 3. Viewing function diffs
* 4. Copying attestation
*/
import { test, expect, Page } from '@playwright/test';
test.describe('Binary Resolution UI', () => {
const vulnUrl = '/vulnerabilities/CVE-2024-0001';
test.describe('Resolution Chip Display', () => {
test('displays resolution chip for vulnerability with binary resolution', async ({ page }) => {
await page.goto(vulnUrl);
// Wait for resolution chip to appear
const chip = page.locator('stella-resolution-chip');
await expect(chip).toBeVisible();
});
test('shows Fixed (backport) label for fingerprint matches', async ({ page }) => {
await page.goto(vulnUrl);
const chip = page.locator('.resolution-chip');
await expect(chip).toContainText('backport');
});
test('displays correct icon for backport detection', async ({ page }) => {
await page.goto(vulnUrl);
const icon = page.locator('.resolution-chip__icon');
await expect(icon).toContainText('🔍');
});
test('shows info button when evidence is available', async ({ page }) => {
await page.goto(vulnUrl);
const infoButton = page.locator('.resolution-chip__info-btn');
await expect(infoButton).toBeVisible();
});
test('shows correct color for Fixed status', async ({ page }) => {
await page.goto(vulnUrl);
const chip = page.locator('.resolution-chip');
await expect(chip).toHaveClass(/resolution-chip--fixed/);
});
});
test.describe('Evidence Drawer Interaction', () => {
test('opens evidence drawer when Show evidence button clicked', async ({ page }) => {
await page.goto(vulnUrl);
// Click the show evidence button
await page.locator('.evidence-toggle').click();
// Verify drawer is visible
const drawer = page.locator('.evidence-drawer');
await expect(drawer).toHaveClass(/evidence-drawer--open/);
});
test('opens drawer when info button on chip is clicked', async ({ page }) => {
await page.goto(vulnUrl);
// Click info button on chip
await page.locator('.resolution-chip__info-btn').click();
// Verify drawer is open
const drawer = page.locator('.evidence-drawer');
await expect(drawer).toHaveClass(/evidence-drawer--open/);
});
test('closes drawer when X button clicked', async ({ page }) => {
await page.goto(vulnUrl);
// Open drawer
await page.locator('.evidence-toggle').click();
await expect(page.locator('.evidence-drawer')).toHaveClass(/evidence-drawer--open/);
// Close drawer
await page.locator('.evidence-drawer__close').click();
// Verify drawer is closed
await expect(page.locator('.evidence-drawer')).not.toHaveClass(/evidence-drawer--open/);
});
test('closes drawer when backdrop clicked', async ({ page }) => {
await page.goto(vulnUrl);
// Open drawer
await page.locator('.evidence-toggle').click();
// Click backdrop
await page.locator('.evidence-drawer__backdrop').click();
// Verify drawer is closed
await expect(page.locator('.evidence-drawer')).not.toHaveClass(/evidence-drawer--open/);
});
test('closes drawer when Escape key pressed', async ({ page }) => {
await page.goto(vulnUrl);
// Open drawer
await page.locator('.evidence-toggle').click();
// Press Escape
await page.keyboard.press('Escape');
// Verify drawer is closed
await expect(page.locator('.evidence-drawer')).not.toHaveClass(/evidence-drawer--open/);
});
});
test.describe('Evidence Content Display', () => {
test.beforeEach(async ({ page }) => {
await page.goto(vulnUrl);
await page.locator('.evidence-toggle').click();
});
test('displays match method', async ({ page }) => {
await expect(page.locator('.evidence-drawer')).toContainText('Match Method');
await expect(page.locator('.evidence-drawer__value--highlight')).toContainText('Fingerprint');
});
test('displays confidence gauge', async ({ page }) => {
const gauge = page.locator('.confidence-gauge');
await expect(gauge).toBeVisible();
const label = page.locator('.confidence-gauge__label');
await expect(label).toContainText('%');
});
test('displays advisory link', async ({ page }) => {
const advisoryLink = page.locator('.evidence-drawer__link').first();
await expect(advisoryLink).toBeVisible();
});
test('opens advisory in new tab when clicked', async ({ page, context }) => {
const advisoryLink = page.locator('.evidence-drawer__link').first();
// Listen for new page
const [newPage] = await Promise.all([
context.waitForEvent('page'),
advisoryLink.click(),
]);
expect(newPage.url()).toContain('debian.org/security');
});
});
test.describe('Function Diff Interaction', () => {
test.beforeEach(async ({ page }) => {
await page.goto(vulnUrl);
await page.locator('.evidence-toggle').click();
});
test('displays changed functions list', async ({ page }) => {
const functionList = page.locator('.function-list');
await expect(functionList).toBeVisible();
const items = page.locator('.function-list__item');
await expect(items.first()).toBeVisible();
});
test('shows View Diff button for modified functions', async ({ page }) => {
const diffButton = page.locator('.function-list__diff-btn').first();
await expect(diffButton).toBeVisible();
await expect(diffButton).toHaveText('View Diff');
});
test('function names are displayed', async ({ page }) => {
const functionName = page.locator('.function-list__name').first();
await expect(functionName).toBeVisible();
});
test('change type badge is displayed', async ({ page }) => {
const changeType = page.locator('.function-list__type').first();
await expect(changeType).toBeVisible();
await expect(changeType).toHaveText(/Modified|Added|Removed/);
});
});
test.describe('DSSE Attestation Handling', () => {
test.beforeEach(async ({ page }) => {
await page.goto(vulnUrl);
await page.locator('.evidence-toggle').click();
});
test('displays attestation section when available', async ({ page }) => {
await expect(page.locator('.evidence-drawer')).toContainText('Attestation');
});
test('shows signer information', async ({ page }) => {
await expect(page.locator('.evidence-drawer')).toContainText('Signer');
});
test('shows Copy DSSE Envelope button', async ({ page }) => {
const copyButton = page.locator('.evidence-drawer__copy-btn');
await expect(copyButton).toBeVisible();
await expect(copyButton).toContainText('Copy DSSE Envelope');
});
test('shows Copied feedback after clicking copy button', async ({ page }) => {
const copyButton = page.locator('.evidence-drawer__copy-btn');
await copyButton.click();
await expect(copyButton).toContainText('Copied!');
});
test('resets copy button text after 2 seconds', async ({ page }) => {
const copyButton = page.locator('.evidence-drawer__copy-btn');
await copyButton.click();
await expect(copyButton).toContainText('Copied!');
// Wait for reset
await page.waitForTimeout(2500);
await expect(copyButton).toContainText('Copy DSSE Envelope');
});
});
test.describe('Responsive Behavior', () => {
test('drawer takes full width on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(vulnUrl);
await page.locator('.evidence-toggle').click();
const drawer = page.locator('.evidence-drawer');
const box = await drawer.boundingBox();
expect(box?.width).toBeGreaterThan(300);
});
test('drawer has max-width on desktop', async ({ page }) => {
await page.setViewportSize({ width: 1920, height: 1080 });
await page.goto(vulnUrl);
await page.locator('.evidence-toggle').click();
const drawer = page.locator('.evidence-drawer');
const box = await drawer.boundingBox();
expect(box?.width).toBeLessThanOrEqual(400);
});
});
test.describe('Dark Mode Support', () => {
test('applies dark theme styles when dark mode is enabled', async ({ page }) => {
await page.goto(vulnUrl);
// Enable dark mode (assuming it's toggled via a class on body)
await page.evaluate(() => {
document.body.classList.add('dark-theme');
});
await page.locator('.evidence-toggle').click();
// Drawer should have dark mode styles applied via :host-context
const drawer = page.locator('.evidence-drawer');
await expect(drawer).toBeVisible();
});
});
test.describe('Error Handling', () => {
test('handles missing resolution gracefully', async ({ page }) => {
// Mock API to return no resolution
await page.route('**/api/v1/resolve/**', route => {
route.fulfill({
status: 404,
body: JSON.stringify({ error: 'Not found' }),
});
});
await page.goto(vulnUrl);
// Resolution chip should not be visible
const chip = page.locator('stella-resolution-chip');
await expect(chip).not.toBeVisible();
});
test('handles API errors gracefully', async ({ page }) => {
// Mock API to return error
await page.route('**/api/v1/resolve/**', route => {
route.fulfill({
status: 500,
body: JSON.stringify({ error: 'Internal server error' }),
});
});
await page.goto(vulnUrl);
// Page should still render without resolution
await expect(page.locator('h1')).toBeVisible();
});
});
});
test.describe('Keyboard Navigation', () => {
test('can navigate through drawer with Tab key', async ({ page }) => {
await page.goto('/vulnerabilities/CVE-2024-0001');
await page.locator('.evidence-toggle').click();
// Focus should be manageable via Tab
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
// Should be able to close with Enter on close button
await page.locator('.evidence-drawer__close').focus();
await page.keyboard.press('Enter');
await expect(page.locator('.evidence-drawer')).not.toHaveClass(/evidence-drawer--open/);
});
});