// Takes the README screenshots from a fresh server in demo mode (sample data of admin/admin). // Usage: e2e/screenshots.sh (builds the image) or MINDSIFT_URL=… SHOTS_DIR=… node screenshots.mjs import { chromium } from 'playwright'; const BASE = process.env.MINDSIFT_URL || 'http://localhost:18080'; const OUT = process.env.SHOTS_DIR || '../screenshots'; const DESKTOP = { viewport: { width: 1518, height: 886 }, deviceScaleFactor: 2 }; const MOBILE = { viewport: { width: 390, height: 844 }, deviceScaleFactor: 2, isMobile: true, hasTouch: true }; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function login(user) { const res = await fetch(`${BASE}/api/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ login: user, password: user }), }); if (!res.ok) throw new Error(`login ${user}: ${res.status}`); return (await res.json()).token; } async function open(browser, token, options) { const ctx = await browser.newContext({ ...options, colorScheme: 'dark', locale: 'de-DE' }); await ctx.addInitScript((t) => localStorage.setItem('mindsift_jwt_token', t), token); const page = await ctx.newPage(); page.on('pageerror', (e) => console.log('PAGEERROR', e.message)); await page.goto(BASE); await page.waitForLoadState('networkidle'); return page; } const tab = (page, name) => page.getByRole('button', { name: new RegExp(name) }).first().click(); async function shot(page, file) { await sleep(800); // animations await page.mouse.move(0, 0); await page.screenshot({ path: `${OUT}/${file}` }); console.log(`OK ${file}`); } const main = async () => { const admin = await login('admin'); const user = await login('user'); const browser = await chromium.launch({ args: ['--lang=de-DE'] }); const page = await open(browser, admin, DESKTOP); await tab(page, 'Triage'); await shot(page, 'Triage_desktop.png'); await tab(page, 'Aufgaben'); await page.getByText('Homelab', { exact: true }).first().click().catch(() => {}); await page.getByRole('button', { name: /Kanban|Board/ }).first().click().catch(() => {}); await shot(page, 'aufgaben_desktop.png'); // The demo user edits a bullet of the shared note, so the screenshot shows lock and presence const other = await open(browser, user, DESKTOP); await tab(other, 'Workspace'); await other.getByText(/^Homelab 2\.0/).first().click(); await other.waitForFunction(() => document.querySelectorAll('textarea').length > 5, null, { timeout: 10000 }); await other.getByText('Endlich Platz für die Dienste', { exact: false }).first().click(); await other.keyboard.press('End'); await other.keyboard.type(': Paperless und Immich', { delay: 20 }); await tab(page, 'Workspace'); await page.getByText(/^Homelab 2\.0/).first().click(); await page.waitForFunction(() => document.querySelectorAll('textarea').length > 5, null, { timeout: 10000 }); await page.getByText('User', { exact: false }).first().waitFor({ timeout: 15000 }).catch(() => {}); await sleep(2000); await shot(page, 'workspace_desktop.png'); await tab(page, 'Listen'); await page.getByText('Wocheneinkauf', { exact: true }).first().click().catch(() => {}); await shot(page, 'Listen_desktop.png'); const mobile = await open(browser, admin, MOBILE); await mobile.getByText('Später im Browser weiter nutzen').click({ timeout: 5000 }).catch(() => {}); // install prompt await shot(mobile, 'Mobile_Triage.png'); await browser.close(); }; main().catch((e) => { console.error(e); process.exit(1); });