import { test, expect } from '@playwright/test'; // Helpers to probe canvas by reading pixel colors async function getPixelColor(page: any, selector: string, x: number, y: number) { return await page.$eval(selector, (canvas: HTMLCanvasElement, [x, y]) => { const ctx = canvas.getContext('2d')!; const dpr = window.devicePixelRatio || 1; const data = ctx.getImageData(Math.floor(x), Math.floor(y), 1, 1).data; return Array.from(data); }, [x, y]); } // Minimal smoke tests ensuring that UI controls affect the canvas // These assume the app starts with some graph data (test vault bundled) test.describe('Graph Canvas', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); // Open Graph view await page.getByRole('button', { name: /graphe|graph/i }).first().click(); // Ensure canvas exists await page.waitForSelector('app-graph-canvas canvas'); }); test('toggle Existing files only reduces node count', async ({ page }) => { // Open settings panel await page.getByRole('button', { name: /settings|param/i }).first().click(); // Toggle Existing files only const checkbox = page.getByText(/Existing files only/i).locator('..').locator('input[type="checkbox"]'); await checkbox.click({ force: true }); // Wait a bit for recompute await page.waitForTimeout(200); // We cannot count nodes directly; instead, ensure simulation tick updates occurred by reading overlay text const stats = await page.locator('text=/Nodes:/').first().innerText(); expect(stats).toMatch(/Nodes:/); }); test('change Link distance affects layout (smoke)', async ({ page }) => { await page.getByRole('button', { name: /settings|param/i }).first().click(); // Ensure Forces section is expanded in the settings accordion const forcesHeader = page.getByRole('button', { name: /^Forces$/i }).first(); await forcesHeader.click({ force: true }); // Slide Link distance; we approximate by focusing input[type=range] with label const slider = page.getByText(/Link distance/i).locator('..').locator('input[type="range"]').first(); await slider.evaluate((el: HTMLInputElement) => { el.value = '100'; el.dispatchEvent(new Event('input', { bubbles: true })); }); await page.waitForTimeout(300); // No assertion on pixels here; just ensure canvas repaints by checking it remains attached const canvasVisible = await page.isVisible('app-graph-canvas canvas'); expect(canvasVisible).toBeTruthy(); }); test('adding group tag:test colors some nodes and legend updates', async ({ page }) => { await page.getByRole('button', { name: /settings|param/i }).first().click(); // Add new group await page.getByRole('button', { name: /new group/i }).click({ force: true }); const lastGroupInput = page.locator('app-graph-groups-section input[type="text"]').last(); await lastGroupInput.fill('tag:test'); // Wait for legend to render and reflect the new group await page.waitForSelector('app-graph-legend'); const legendItems = page.locator('app-graph-legend button'); await legendItems.first().waitFor({ state: 'visible', timeout: 2000 }).catch(() => {}); const count = await legendItems.count(); expect(count).toBeGreaterThan(0); }); test('open/close Graph 10x remains responsive (no freeze)', async ({ page }) => { // Capture console errors during the whole test const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); // Buttons in header/sidebar const graphButtons = [ page.getByRole('button', { name: /graph|graphe/i }).first(), ]; const filesButtons = [ page.getByRole('button', { name: /files|fichiers/i }).first(), ]; // Perform 10 rapid toggles for (let i = 0; i < 10; i++) { await graphButtons[0].click({ timeout: 2000 }); // Canvas should be present quickly await page.waitForSelector('app-graph-canvas canvas', { timeout: 3000 }); // Evaluate a trivial script to ensure main thread is not locked const val = await page.evaluate(() => 21 + 21); expect(val).toBe(42); // Switch back to files await filesButtons[0].click({ timeout: 2000 }); // Quick evaluation again const val2 = await page.evaluate(() => 1 + 1); expect(val2).toBe(2); } // Ensure no console errors during toggles expect(errors.length).toBeLessThan(2); // allow transient warnings but not repeated errors }); });