Files

91 lines
3.7 KiB
TypeScript

/**
* Performance Gate (Task I1): 10.000 Interaktions-Budget.
* Erstellt ein Projekt mit Elementen, misst die Zeit fuer 10.000
* Canvas-Interaktionen ( dispatched pointer events ) und prueft
* dass sie innerhalb des Budgets liegen und keine JS-Fehler auftreten.
*/
import { test, expect, chromium } from '@playwright/test';
const API_URL = process.env.API_URL || 'http://localhost:3001';
const BASE_URL = process.env.FRONTEND_URL || 'http://localhost:5173';
const N_INTERACTIONS = 10000;
const BUDGET_MS = 15000; // 10k Interaktionen muessen in < 15s sein
async function api(method: string, endpoint: string, body?: unknown, token?: string) {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (token) headers.Authorization = `Bearer ${token}`;
const res = await fetch(`${API_URL}${endpoint}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
return { status: res.status, body: await res.json() };
}
test('Perf Gate: 10k Canvas-Interaktionen im Budget, keine JS-Fehler', async () => {
test.setTimeout(180000);
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1600, height: 1000 } });
const jsErrors: string[] = [];
page.on('pageerror', (err) => jsErrors.push(String(err)));
const email = `perf-${Date.now()}@example.com`;
const reg = await api('POST', '/api/auth/register', { email, password: 'Test1234!', name: 'Perf' });
expect(reg.status).toBe(201);
const token = reg.body.session.token;
const proj = await api('POST', '/api/projects', { name: `Perf-${Date.now()}`, description: 'perf gate' }, token);
expect(proj.status).toBe(201);
await page.goto(`${BASE_URL}/`);
await page.evaluate((t) => localStorage.setItem('auth_token', t), token);
await page.reload();
await page.waitForSelector('.dashboard-project-card', { timeout: 30000 });
await page.locator('.dashboard-project-card').first().click();
await page.waitForSelector('canvas', { timeout: 30000 });
await page.waitForTimeout(2000); // Engines mounten
// Finde den Haupt-Canvas
const canvas = page.locator('canvas').first();
const box = await canvas.boundingBox();
expect(box).toBeTruthy();
// ── 10.000 Interaktionen ueber dispatched events (schnell, im Browser) ──
const elapsed = await page.evaluate(({ n, bx, by }) => {
const target = document.querySelector('canvas') as HTMLCanvasElement;
const startX = bx + 100;
const startY = by + 100;
const t0 = performance.now();
let x = startX, y = startY;
for (let i = 0; i < n; i++) {
x = startX + Math.sin(i * 0.01) * 300;
y = startY + Math.cos(i * 0.013) * 200;
target.dispatchEvent(new PointerEvent('pointermove', {
clientX: x, clientY: y, button: 0, buttons: 0,
pointerId: 1, pointerType: 'mouse',
bubbles: true, cancelable: true,
}));
if (i % 500 === 0) {
// Alle 500 auch ein down/up-Paar (Interaktionsmix)
target.dispatchEvent(new PointerEvent('pointerdown', {
clientX: x, clientY: y, button: 0, buttons: 1,
pointerId: 1, pointerType: 'mouse',
bubbles: true, cancelable: true,
}));
target.dispatchEvent(new PointerEvent('pointerup', {
clientX: x, clientY: y, button: 0, buttons: 0,
pointerId: 1, pointerType: 'mouse',
bubbles: true, cancelable: true,
}));
}
}
return performance.now() - t0;
}, { n: N_INTERACTIONS, bx: box!.x, by: box!.y });
console.log(`Perf Gate: ${N_INTERACTIONS} Interaktionen in ${elapsed.toFixed(0)}ms (Budget: ${BUDGET_MS}ms)`);
expect(elapsed).toBeLessThan(BUDGET_MS);
expect(jsErrors).toHaveLength(0);
await browser.close();
});