/** * 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 = { '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(); });