Public source validation / validate (push) Failing after 3m8s
70 lines
4.4 KiB
TypeScript
70 lines
4.4 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test';
|
|
import path from 'node:path';
|
|
|
|
const observedAt = new Date().toISOString();
|
|
|
|
async function mockResponsiveData(page: Page): Promise<void> {
|
|
await page.route('**/api/v1/**', async (route) => {
|
|
const pathname = new URL(route.request().url()).pathname;
|
|
const body = pathname === '/api/v1/system/status' ? {
|
|
version: '1', generatedAt: observedAt, overallState: 'degraded',
|
|
components: [
|
|
{ id: 'database', state: 'healthy', reason: 'ok' },
|
|
{ id: 'prometheus', state: 'unknown', reason: 'source_stale' },
|
|
],
|
|
backup: { state: 'disabled', reason: 'not_configured' },
|
|
sourceLag: [{ sourceId: 'prometheus', state: 'unknown', reason: 'source_stale', ageSeconds: 900 }],
|
|
} : pathname === '/api/v1/host' ? {
|
|
identity: { name: 'responsive-fixture' }, cpu: { totalPercent: 42, perCore: [42, 38] },
|
|
memory: { utilizationPercent: 61 }, source: { state: 'healthy', freshness: 'fresh' },
|
|
} : pathname === '/api/v1/containers' ? { source: { state: 'healthy', freshness: 'fresh' }, containers: [], total: 0 }
|
|
: pathname === '/api/v1/pools' ? { source: { state: 'healthy', freshness: 'fresh' }, pools: [], total: 0 }
|
|
: pathname === '/api/v1/services' ? { capabilityState: 'available', configurationState: 'not_configured', services: [], total: 0 }
|
|
: pathname === '/api/v1/incidents' ? { items: [] }
|
|
: {};
|
|
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(body) });
|
|
});
|
|
}
|
|
|
|
test.beforeEach(async ({ page }) => mockResponsiveData(page));
|
|
|
|
test('mobile primary navigation and operational reasons remain readable at 390px', async ({ page }, testInfo) => {
|
|
test.skip(testInfo.project.name !== 'mobile-chromium', 'Mobile evidence uses the supported 390px viewport.');
|
|
await page.goto('/');
|
|
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
|
const labels = page.locator('.mobile-primary-list .nav-link span:last-child');
|
|
await expect(labels).toHaveCount(4);
|
|
const metrics = await labels.evaluateAll((nodes) => nodes.map((node) => {
|
|
const box = node.getBoundingClientRect();
|
|
return { left: box.left, right: box.right, top: box.top, bottom: box.bottom, font: Number.parseFloat(getComputedStyle(node).fontSize) };
|
|
}));
|
|
for (let index = 1; index < metrics.length; index += 1) expect(metrics[index - 1].right).toBeLessThanOrEqual(metrics[index].left);
|
|
expect(metrics.every((metric) => metric.font >= 12 && metric.bottom - metric.top <= 16)).toBe(true);
|
|
const reason = page.locator('.action-queue small').first();
|
|
await expect(reason).toBeVisible();
|
|
await expect(reason).toHaveCSS('white-space', 'normal');
|
|
const overflow = await page.evaluate(() => [...document.querySelectorAll<HTMLElement>('body *')]
|
|
.map((element) => ({ tag: element.tagName, className: element.className, right: Math.round(element.getBoundingClientRect().right), scrollWidth: element.scrollWidth, clientWidth: element.clientWidth }))
|
|
.filter((item) => item.right > innerWidth + 1 || item.scrollWidth > item.clientWidth + 1)
|
|
.slice(0, 12));
|
|
expect(overflow).toEqual([]);
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(390);
|
|
if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: path.resolve('../../artifacts/evidence/M12-06/responsive-mobile-390.png'), fullPage: true });
|
|
});
|
|
|
|
test('tablet sidebar stays compact and content uses the remaining viewport', async ({ page }, testInfo) => {
|
|
test.skip(testInfo.project.name !== 'tablet-chromium', 'Tablet evidence uses the supported 1024px viewport.');
|
|
await page.goto('/');
|
|
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
|
const sidebar = await page.locator('.sidebar').boundingBox();
|
|
const workspace = await page.locator('.app-workspace').boundingBox();
|
|
const queue = await page.locator('.action-queue').boundingBox();
|
|
const dataPlane = await page.locator('.signal-path-panel').boundingBox();
|
|
expect(sidebar?.width).toBeLessThanOrEqual(208);
|
|
expect(workspace?.width).toBeGreaterThanOrEqual(816);
|
|
expect(queue?.width).toBeGreaterThan(300);
|
|
expect(dataPlane?.width).toBeGreaterThan(500);
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(1024);
|
|
if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: path.resolve('../../artifacts/evidence/M12-06/responsive-tablet-1024.png'), fullPage: true });
|
|
});
|