ktg-plugin-marketplace/plugins/ms-ai-architect/tests/screenshot/run.mjs
Kjell Tore Guttormsen 7ffaa82207 feat(ms-ai-architect): release v1.11.0 — design-system 100%-adoption + visual upgrade
Sesjon 3 av 3 — leverer Fase 7-9 av v1.11.0-planen.

Fase 7 (Acme-rename på demo-state):
- Rename "Acme AS" → "Acme Kommune" og "Demosystem" → "Acme Kunde-chatbot"
  konsistent på tvers av alle 17 fixtures.
- build-demo-state.mjs: organization.name → "Acme Kommune", projects[0] →
  id "acme-kunde-chatbot" / name "Acme: Kunde-chatbot".
- Re-bygd demo-state-v1-blokk i playground HTML.

Fase 8 (Screenshots-regenerering):
- 24 nye PNG-er under playground/screenshots/v1.11.0/ (12 surfaces × 2 tema,
  retina, fullPage). v1.10.0-mappen beholdt som historisk referanse.
- tests/screenshot/run.mjs: OUT_DIR + kommentarer bumpet til v1.11.0.

Fase 9 (Release: docs + versjonsbump):
- plugin.json 1.10.1 → 1.11.0.
- README.md (plugin): version-badge + Version History + screenshot-gallery refs +
  demo-data refs oppdatert.
- CLAUDE.md (plugin): Playground-overskrift v3/v1.10.0 → v3/v1.11.0,
  Demo system-seksjon v1.10.1 → v1.11.0, screenshot-refs v1.10.0 → v1.11.0,
  "Inline CSS-kandidater" konvertert til "Design-system 100%-adoption" status.
- Root README.md: ms-ai-architect-versjon 1.10.1 → 1.11.0, demo-tekst og
  Playground-tekst regenerert for v1.11.0, "271 PASS combined" → "278 PASS".

Verifisering:
- bash tests/run-e2e.sh --playground → 271/271 PASS (static + parsers).
- bash tests/test-playground-migrations.sh → 7/7 PASS.
- Total: 278/278 PASS, 0 FAIL.

Refs: NEXT-SESSION-PROMPT.local.md (Sesjon 3 av 3, plan
.claude/plans/jeg-skal-pr-ve-effervescent-token.md).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 17:41:36 +02:00

186 lines
6.2 KiB
JavaScript

#!/usr/bin/env node
// Capture playground screenshots for v1.11.0 documentation.
//
// Opens the single-file playground HTML via file://, drives it through:
// - Initial onboarding (empty state)
// - "Last inn demo-data" → project surface with all 17 reports rehydrated
// - All 4 project screen-tabs (oversikt / rapporter / kontekst / eksport)
// - Each rapport-tab category (regulatory / security / economy / docs / tool)
// - Both themes (dark + light)
//
// Output: playground/screenshots/v1.11.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.11.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 {
// Best-effort: clear IndexedDB databases.
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();
});
// Wait for project surface to render + rehydrate paste-imports.
await page.waitForSelector('[data-surface="project"]:not([hidden])', { timeout: 5000 });
await page.waitForTimeout(800); // settle rehydrate microtasks
}
async function clickAction(page, action) {
await page.evaluate((a) => {
const el = document.querySelector('[data-action="' + a + '"]');
if (el) el.click();
}, action);
await page.waitForTimeout(300);
}
async function clickProjectTab(page, tabId) {
await page.evaluate((t) => {
const el = document.querySelector('[data-action="project-tab"][data-tab="' + t + '"]');
if (el) el.click();
}, tabId);
await page.waitForTimeout(400);
}
async function clickProjectScreen(page, screenId) {
await page.evaluate((s) => {
const el = document.querySelector('[data-action="project-screen"][data-screen="' + s + '"]');
if (el) el.click();
}, screenId);
await page.waitForTimeout(400);
}
async function shoot(page, name) {
const path = join(OUT_DIR, name + '.png');
await page.screenshot({ path, fullPage: FULL_PAGE });
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 surface (rapporter screen, regulatory tab default)
await loadDemo(page);
await setTheme(page, theme);
await shoot(page, '02-project-rapporter-regulatory-' + theme);
// 3. Project tab cycle (5 categories)
const TABS = [
{ id: 'security', label: 'security' },
{ id: 'economy', label: 'economy' },
{ id: 'documentation', label: 'documentation' },
{ id: 'tool', label: 'tool' }
];
for (const tab of TABS) {
await clickProjectTab(page, tab.id);
await page.waitForTimeout(500);
await shoot(page, '03-project-rapporter-' + tab.label + '-' + theme);
}
// 4. Project screen-tabs (oversikt / kontekst / eksport)
await clickProjectScreen(page, 'oversikt');
await shoot(page, '04-project-oversikt-' + theme);
await clickProjectScreen(page, 'kontekst');
await shoot(page, '05-project-kontekst-' + theme);
await clickProjectScreen(page, 'eksport');
await shoot(page, '06-project-eksport-' + theme);
// Back to rapporter for nav screenshots
await clickProjectScreen(page, 'rapporter');
// 5. Home surface
await clickAction(page, 'goto-home');
await page.waitForSelector('[data-surface="home"]:not([hidden])');
await page.waitForTimeout(300);
await shoot(page, '07-home-' + theme);
// 6. Catalog surface
await clickAction(page, 'goto-catalog');
await page.waitForSelector('[data-surface="catalog"]:not([hidden])');
await page.waitForTimeout(300);
await shoot(page, '08-catalog-' + theme);
// 7. Onboarding (with prefilled state from demo)
await clickAction(page, 'goto-onboarding');
await page.waitForSelector('[data-surface="onboarding"]:not([hidden])');
await page.waitForTimeout(300);
await shoot(page, '09-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 // crisper screenshots for retina
});
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);
});