#!/usr/bin/env node // Capture playground screenshots for v1.15.0 documentation. // // v1.15.0: v2 project-surface (renderProjectSurface med screen-tabs + // category-tabs) erstattet av renderProjectView (sidebar med 17 artifacts + // main-area med per-artifact view + import-modal). Skjermbilder oppdatert // til å fange v3-surfaces. // // Output: playground/screenshots/v1.15.0/-.png // // Usage: // cd tests/screenshot // npm install // npx playwright install chromium # ~150MB download, one-time // node run.mjs import { chromium } from 'playwright'; import { fileURLToPath } from 'node:url'; import { dirname, resolve, join } from 'node:path'; import { mkdirSync, existsSync } from 'node:fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const PLUGIN_ROOT = resolve(__dirname, '..', '..'); const HTML_PATH = join(PLUGIN_ROOT, 'playground', 'ms-ai-architect-playground.html'); const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v1.15.0'); const HTML_URL = 'file://' + HTML_PATH; const VIEWPORT = { width: 1440, height: 900 }; const FULL_PAGE = true; function ensureOutDir() { if (!existsSync(OUT_DIR)) mkdirSync(OUT_DIR, { recursive: true }); } async function setTheme(page, theme) { await page.evaluate((t) => { document.documentElement.setAttribute('data-theme', t); try { localStorage.setItem('ms-ai-architect-theme', t); } catch (e) {} const labels = document.querySelectorAll('[data-theme-label]'); for (const l of labels) l.textContent = t === 'dark' ? 'Mørk' : 'Lys'; }, theme); await page.waitForTimeout(150); } async function clearState(page) { await page.evaluate(() => { try { localStorage.clear(); } catch (e) {} try { const dbs = ['ms-ai-architect-state-v1', 'ms-ai-architect-playground']; dbs.forEach((n) => indexedDB.deleteDatabase(n)); } catch (e) {} }); } async function loadDemo(page) { await page.evaluate(() => { const action = document.querySelector('[data-action="load-demo"]'); if (action) action.click(); }); await page.waitForSelector('[data-surface="project"]:not([hidden])', { timeout: 5000 }); // Settle migrasjon (v2→v3 auto-parse) + render. await page.waitForTimeout(1200); } async function clickAction(page, action) { await page.evaluate((a) => { const el = document.querySelector('[data-action="' + a + '"]'); if (el) el.click(); }, action); await page.waitForTimeout(400); } async function selectArtifact(page, artifactId) { await page.evaluate((id) => { const el = document.querySelector('[data-action="project-select-artifact"][data-artifact-id="' + id + '"]'); if (el) el.click(); }, artifactId); await page.waitForTimeout(500); } async function openImportModal(page, prefillCmd) { await page.evaluate((cid) => { // Foretrukket: artifact-reimport-knappen (har eksisterende markdown). if (cid) { const el = document.querySelector('[data-action="artifact-reimport"][data-command="' + cid + '"]'); if (el) { el.click(); return; } } // Fallback: generisk "Importer rapport"-knapp. const open = document.querySelector('[data-action="import-open"]'); if (open) open.click(); }, prefillCmd); await page.waitForSelector('[data-import-modal]', { timeout: 3000 }); await page.waitForTimeout(400); } async function setSearchQuery(page, query) { await page.evaluate((q) => { const input = document.querySelector('[data-project-search]'); if (!input) return; input.value = q; input.dispatchEvent(new Event('input', { bubbles: true })); }, query); await page.waitForTimeout(400); } async function shoot(page, name, opts) { const path = join(OUT_DIR, name + '.png'); const useFullPage = (opts && opts.fullPage != null) ? opts.fullPage : FULL_PAGE; await page.screenshot({ path, fullPage: useFullPage }); console.log(' → ' + name + '.png'); } async function captureAllSurfaces(page, theme) { console.log('\n[' + theme + ' theme]'); // 1. Onboarding (empty state) await clearState(page); await page.goto(HTML_URL); await page.waitForSelector('[data-surface="onboarding"]:not([hidden])', { timeout: 5000 }); await setTheme(page, theme); await shoot(page, '01-onboarding-empty-' + theme); // 2. Load demo → project-view overview (default — ingen artifact valgt) await loadDemo(page); await setTheme(page, theme); await shoot(page, '02-project-overview-' + theme); // 3-7. 5 sample artifacts som dekker arketype-bredden const SAMPLE_ARTIFACTS = [ { id: 'classify', label: 'classify' }, // AI Act-pyramide { id: 'security', label: 'security' }, // 6×5 sikkerhets-matrise { id: 'ros', label: 'ros' }, // ROS matrise + radar { id: 'cost', label: 'cost' }, // P10/P50/P90 distribusjon { id: 'summary', label: 'summary' } // Beslutningsnotat ]; for (let i = 0; i < SAMPLE_ARTIFACTS.length; i++) { const a = SAMPLE_ARTIFACTS[i]; await selectArtifact(page, a.id); const num = String(3 + i).padStart(2, '0'); await shoot(page, num + '-project-artifact-' + a.label + '-' + theme); } // 8. Import-modal åpen (med prefill fra eksisterende ros-artifact) // Viewport-only (ikke fullPage) — modal er position:fixed; fullPage // skroller forbi overlay-en og kaster bort kontekst. await openImportModal(page, 'ros'); await page.evaluate(() => window.scrollTo(0, 0)); await page.waitForTimeout(200); await shoot(page, '08-project-import-modal-' + theme, { fullPage: false }); await clickAction(page, 'import-close'); await page.waitForTimeout(300); // 9. Sidebar-søk aktivt (filtrer på "ros") await setSearchQuery(page, 'ros'); await shoot(page, '09-project-search-' + theme); await setSearchQuery(page, ''); // reset // 10. Home surface await clickAction(page, 'goto-home'); await page.waitForSelector('[data-surface="home"]:not([hidden])'); await page.waitForTimeout(300); await shoot(page, '10-home-' + theme); // 11. Catalog surface await clickAction(page, 'goto-catalog'); await page.waitForSelector('[data-surface="catalog"]:not([hidden])'); await page.waitForTimeout(300); await shoot(page, '11-catalog-' + theme); // 12. Onboarding prefilled (post-demo med org-felter fylt) await clickAction(page, 'goto-onboarding'); await page.waitForSelector('[data-surface="onboarding"]:not([hidden])'); await page.waitForTimeout(300); await shoot(page, '12-onboarding-prefilled-' + theme); } async function main() { ensureOutDir(); console.log('[screenshot] launching Chromium…'); const browser = await chromium.launch(); const context = await browser.newContext({ viewport: VIEWPORT, deviceScaleFactor: 2 }); const page = await context.newPage(); page.on('console', (msg) => { const t = msg.type(); if (t === 'error' || t === 'warning') { console.warn(' [browser ' + t + '] ' + msg.text()); } }); try { for (const theme of ['dark', 'light']) { await captureAllSurfaces(page, theme); } console.log('\n[screenshot] done — output: ' + OUT_DIR); } finally { await browser.close(); } } main().catch((err) => { console.error('[screenshot] FAILED:', err); process.exit(1); });