/** Sweep diag : nav partielle vs complet sur N pages — erreurs + état fdCtx/appState */ const { test } = require('@playwright/test'); const FD_BASE = process.env.FD_BASE_URL || 'http://localhost:8080'; const USER = process.env.FD_USER || 'e2e@flowdeck.local'; const PASS = process.env.FD_PASS || 'e2e-secret-123'; async function login(page) { await page.goto(`${FD_BASE}/auth/login?provider=local`, { waitUntil: 'domcontentloaded' }); await page.fill('#email', USER); await page.fill('#password', PASS); await page.click('.btn-primary'); const ok = await page.waitForURL('**/workspaces', { timeout: 8000 }).then(() => true).catch(() => false); if (!ok) { await page.request.post(`${FD_BASE}/auth/register`, { data: { email: USER, password: PASS, name: 'E2E' } }); await page.goto(`${FD_BASE}/workspaces`, { waitUntil: 'domcontentloaded' }); await page.waitForURL('**/workspaces', { timeout: 15000 }); } } const PAGES = ['/workspaces', '/library', '/local-workspace', '/settings', '/trash', '/accounts', '/import']; test('sweep', async ({ page }) => { test.setTimeout(180000); const msgs = []; page.on('console', (m) => msgs.push(m.type() + ' | ' + m.text().slice(0, 200).replace(/\s+/g, ' '))); page.on('pageerror', (e) => msgs.push('PAGEERROR | ' + String(e).slice(0, 160))); await login(page); for (const path of PAGES) { // baseline : chargement complet msgs.length = 0; await page.goto(FD_BASE + path, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(2200); const fullErrs = msgs.filter((m) => m.includes('PAGEERROR') || m.includes('Alpine Expression Error') || m.includes('Uncaught')); const stFull = await page.evaluate(() => ({ fdCtx: (() => { try { return typeof Alpine.store('fdCtx'); } catch (e) { return 'ERR'; } })(), fdCtxHas: (() => { try { var s = Alpine.store('fdCtx'); return s ? typeof s.has + '/' + typeof s.avail : 'no store'; } catch (e) { return 'ERR'; } })(), appState: typeof window.appState, })); // nav partielle : depuis /workspaces await page.goto(`${FD_BASE}/workspaces`, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(700); msgs.length = 0; await page.evaluate((p) => { const a = document.createElement('a'); a.href = p; a.textContent = 'x'; document.body.appendChild(a); a.click(); }, path); await page.waitForTimeout(2500); const partErrs = msgs.filter((m) => m.includes('PAGEERROR') || m.includes('Alpine Expression Error') || m.includes('Uncaught')); // 2e visite partielle dans LE MEME document (declencheur du SyntaxError // « Identifier ... has already been declared » sur script sans garde) await page.evaluate(() => { const a = document.createElement('a'); a.href = '/workspaces'; a.textContent = 'x'; document.body.appendChild(a); a.click(); }); await page.waitForTimeout(900); msgs.length = 0; await page.evaluate((p) => { const a = document.createElement('a'); a.href = p; a.textContent = 'x'; document.body.appendChild(a); a.click(); }, path); await page.waitForTimeout(2500); const part2Errs = msgs.filter((m) => m.includes('PAGEERROR') || m.includes('Alpine Expression Error') || m.includes('Uncaught') || m.includes('already been declared')); const stPart = await page.evaluate(() => ({ fdCtx: (() => { try { return typeof Alpine.store('fdCtx'); } catch (e) { return 'ERR'; } })(), fdCtxHas: (() => { try { var s = Alpine.store('fdCtx'); return s ? typeof s.has + '/' + typeof s.avail : 'no store'; } catch (e) { return 'ERR'; } })(), appState: typeof window.appState, lwGuard: !!window.__fdLocalWorkspaceScriptsLoaded, })); console.log(`PAGE ${path}`); console.log(` complet : errs=${fullErrs.length} ${JSON.stringify(stFull)}`); fullErrs.slice(0, 2).forEach((m) => console.log(' F>', m)); console.log(` 1ere partiel : errs=${partErrs.length} ${JSON.stringify(stPart)}`); partErrs.slice(0, 3).forEach((m) => console.log(' P>', m)); console.log(` 2e partiel : errs=${part2Errs.length}`); part2Errs.slice(0, 3).forEach((m) => console.log(' P2>', m)); } });