/** * Phase-A-Verifikation (ROADMAP Übergabepunkt): * Beweist mit echtem Chromium bei gesetzttem VITE_TOOL_DISPATCHER=v2: * 1. Ribbon-V2-Sektion rendert die registrierten V2-Werkzeuge * 2. Werkzeugwechsel über den v2tool:-Kanal funktioniert * 3. Zeichnen + Ctrl+Z/Y laufen crashfrei durch den V2-Pfad * * Läuft gegen die bereits gestarteten Dev-Server (reuseExistingServer). */ 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'; 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('Phase-A: V2-Pfad end-to-end (Ribbon-Sektion, Toolwechsel, Zeichnen+Undo crashfrei)', async () => { test.setTimeout(180000); // Summe aller Schritte (Auth+Nav+Pixi lazy+Draw+Undo+Shots) const browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1600, height: 1000 } }); // ── Auth & Navigation (Muster aus diagnose-editor.test.ts) ── const email = `pa-verify-${Date.now()}@example.com`; const reg = await api('POST', '/api/auth/register', { email, password: 'Test1234!', name: 'PA' }); expect(reg.status).toBe(201); const token = reg.body.session.token; const proj = await api('POST', '/api/projects', { name: `PA-${Date.now()}`, description: 'x' }, token); expect(proj.status).toBe(201); await page.goto(`${BASE_URL}/`); await page.evaluate((t) => localStorage.setItem('auth_token', t), token); await page.reload(); // Dashboard-Karte warten (Klasse .dashboard-project-card existiert in Dashboard.tsx Z.688): 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(1500); // Engines mounten lassen // ── Fehlerkanäle sammeln — V2-Pfad darf KEINE erzeugen ── const pageErrors: string[] = []; const consoleErrors: string[] = []; page.on('pageerror', (err) => pageErrors.push(err.message)); page.on('console', (msg) => { if (msg.type() === 'error') consoleErrors.push(msg.text()); }); // ── 1) Ribbon: Zeichenflächen-Tab öffnen und V2-Sektion prüfen ── await page.locator('.ribbon-tab', { hasText: 'Zeichenfläche' }).first().click(); await page.waitForTimeout(300); const v2Section = page.locator('text=V2:').first(); await expect(v2Section).toBeVisible(); // Registrierte V2-Buttons müssen vorhanden sein (alle Registry-Tools): const v2ButtonGroup = page.locator('[data-v2tool]'); const buttonCount = await v2ButtonGroup.count(); expect(buttonCount).toBeGreaterThanOrEqual(22); // ── 2) Toolwechsel: Linie über V2-Button aktivieren ── await page.locator('[data-v2tool="line"]').first().click(); await page.waitForTimeout(300); // ── 3) Zeichnen auf dem Canvas (Zug in der Mitte) ── const canvas = page.locator('canvas').first(); const box = await canvas.boundingBox(); expect(box).not.toBeNull(); const cx = box!.x + box!.width / 2; const cy = box!.y + box!.height / 2; await page.mouse.move(cx - 100, cy - 50); await page.mouse.down(); await page.mouse.move(cx + 100, cy + 50, { steps: 8 }); await page.mouse.up(); await page.waitForTimeout(400); // ── 4) Undo/Redo Shortcuts crashfrei ── await page.keyboard.press('Control+z'); await page.waitForTimeout(300); await page.keyboard.press('Control+y'); await page.waitForTimeout(300); // ── 4b) Task C4: PixiRenderer hat die Elemente im Cache ── const pixiInfo = await page.evaluate(() => { const debug = (window as unknown as { __v2Debug?: { info(): { cachedElements: number; ready: boolean } | null } }).__v2Debug; return debug?.info() ?? null; }); expect(pixiInfo).not.toBeNull(); expect(pixiInfo!.ready).toBe(true); expect(pixiInfo!.cachedElements).toBeGreaterThanOrEqual(1); // Nach Undo muss der Cache leer sein (Referenzdiff synchronisiert) await page.keyboard.press('Control+z'); await page.waitForTimeout(300); const pixiInfoAfterUndo = await page.evaluate(() => { const debug = (window as unknown as { __v2Debug?: { info(): { cachedElements: number; ready: boolean } | null } }).__v2Debug; return debug?.info() ?? null; }); expect(pixiInfoAfterUndo!.cachedElements).toBe(0); // Kein Seitenfehler durch den gesamten V2-Ablauf: expect(pageErrors).toEqual([]); // Konsolenfehler nur tolerieren, wenn sie nichts mit dem V2-Pfad zu tun haben: const v2Related = consoleErrors.filter((e) => /dispatcher|v2tool|undo/i.test(e)); expect(v2Related).toEqual([]); // ── 5) Sichtbelege ── await page.screenshot({ path: 'playwright-tests/screenshots/phase-a-canvas.png', fullPage: false }); await page.keyboard.press('Control+z'); await page.waitForTimeout(250); await page.screenshot({ path: 'playwright-tests/screenshots/phase-a-after-undo.png', fullPage: false }); await browser.close(); });