ktg-plugin-marketplace/plugins/ms-ai-architect/tests/screenshot/run.mjs
Kjell Tore Guttormsen d8882f5220 feat(ms-ai-architect): v1.15.0 — playground v3 project-view integration
Erstatter v2 project-surface (screen-tabs + category-tabs + per-command paste-cards)
med v3 renderProjectView (sidebar med 17 artifacts + main-area + import-modal overlay).
renderActive() ruter project-surface til renderProjectSurfaceV3() som wrapper
renderProjectView + topbar + app-shell.

V2-surface helt fjernet:
- renderProjectSurface (152 linjer)
- renderCommandSubCard (87 linjer)
- rehydratePasteImports (15 linjer)
- ACTIONS['project-screen'], currentProjectScreen
- 5 v2-CSS-klasser: .project-tabs, .project-tab*, .sub-zone, .paste-import-row, .project-header__*, .command-cards

Zombie-handlers beholdt for test-back-compat:
currentProjectTab, ACTIONS['project-tab'], ACTIONS['parse'],
handlePasteImport, window.__handlePasteImport. Unreachable fra v3 DOM
men nødvendige for test-playground-v3.sh + test-playground-parsers.sh.

2 fingerprint-gap lukket:
- requirements.headers: utvidet med "EU AI Act — Krav" pattern
- license.headers: utvidet med "Lisens-kapabilitetsmatrise" pattern
- KNOWN_GAP_FIXTURES = {} i test-playground-fingerprints.sh

migrateDataVersion utvidet med parserFor (3. arg):
- Demo-state med kun raw_markdown auto-parses til project.artifacts[cid]
- defaultParserFor(cmdId) resolverer PARSERS[archetypeFor(cmdId)]
- 3 bootstrap-callsites oppdatert (cold-load, import, load-demo)

Ship-QA bugfixes funnet via browser-dogfood:
- components-tier4-project-view.css lagt til i <link>-kjeden (var ikke loaded
  -> modal-overlay og two-column layout virket ikke)
- renderImportModal setter data-open="true" (DS-kontrakt for display: flex)

Bundler også sesjon 2-4 deliverables som ikke ble committed tidligere:
- shared/playground-design-system v0.6.0 (Tier 4 project-view CSS + 6 tokens)
- ms-ai-architect/playground/vendor/ re-sync til DS v0.6.0
- tests/test-playground-fingerprints.sh (sesjon 4 NY - 32 PASS)
- tests/test-playground-projectview.sh (sesjon 4 NY - 30 PASS)
- tests/test-playground-actions.sh (sesjon 4 NY - 19 PASS)
- tests/test-playground-migrations.sh utvidet (7 -> 16 PASS)
- tests/run-e2e.sh wirer alle 6 playground-suiter

Stats:
- bash tests/run-e2e.sh --playground: 386 PASS, 0 FAIL, 2 WARN (pre-eks)
- bash tests/run-e2e.sh (full): All E2E suites passed
- bash tests/validate-plugin.sh: 219 PASS

Screenshots regenerert til playground/screenshots/v1.15.0/ (24 PNG-er, 12
surfaces x 2 tema). Nye v3-surfaces: project-overview, project-artifact-*,
project-import-modal (viewport-only), project-search.

Docs oppdatert (3 nivåer): README.md (badge + version history),
CHANGELOG.md, CLAUDE.md (playground-seksjon + valideringstabell),
rot-README.md + rot-CLAUDE.md (marketplace-landingen + plugin-index).

.gitignore: ny pattern *.local.html + *.local.json for sesjon-state-filer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:58:51 +02:00

207 lines
7.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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/<surface>-<theme>.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);
});