/** * CannaManage Sprint 4 — Visual Screenshot Tour * * Takes screenshots of every page in both dark and light mode. * Requires: pnpm exec playwright install chromium * Usage: pnpm exec playwright test e2e/screenshot-tour.spec.ts --reporter=list * * Prerequisites: * 1. Start mock backend: node e2e/mock-backend.mjs * 2. Start dev server: pnpm dev */ import path from "path" import { expect, test } from "@playwright/test" import type { Page } from "@playwright/test" const SCREENSHOT_DIR = path.join(__dirname, "..", "docs", "screenshots") // Pages accessible without auth const PUBLIC_PAGES = [ { route: "/login", name: "01-login", title: "Admin Login" }, { route: "/portal-login", name: "02-portal-login", title: "Member Portal Login", }, ] // Admin pages (require auth session) const ADMIN_PAGES = [ { route: "/dashboard", name: "03-dashboard", title: "Club Dashboard" }, { route: "/members", name: "04-members", title: "Member Management" }, { route: "/distributions", name: "05-distributions", title: "Distribution History", }, { route: "/distributions/new", name: "06-distribution-new", title: "New Distribution (Multi-Step)", }, { route: "/stock", name: "07-stock", title: "Stock & Batch Management" }, { route: "/stock/new", name: "08-stock-new", title: "Add New Batch" }, { route: "/reports", name: "09-reports", title: "Compliance Reports" }, ] // Portal pages (no admin auth needed per middleware) const PORTAL_PAGES = [ { route: "/portal/dashboard", name: "10-portal-dashboard", title: "Member Quota Overview", }, { route: "/portal/history", name: "11-portal-history", title: "My Distribution History", }, { route: "/portal/profile", name: "12-portal-profile", title: "Profile & Settings", }, ] async function setTheme(page: Page, theme: "dark" | "light") { await page.evaluate((t) => { localStorage.setItem("theme", t) document.documentElement.classList.remove("dark", "light") document.documentElement.classList.add(t) // Also set the next-themes cookie document.cookie = `theme=${t}; path=/` }, theme) } async function capturePageScreenshot( page: Page, route: string, name: string, theme: "dark" | "light" ) { await page.goto(route, { waitUntil: "domcontentloaded" }) await page.waitForTimeout(2000) // Wait for hydration + animations await setTheme(page, theme) await page.waitForTimeout(500) // Let theme apply const filename = `${name}-${theme}.png` await page.screenshot({ path: path.join(SCREENSHOT_DIR, filename), fullPage: true, }) return filename } test.describe("CannaManage Screenshot Tour", () => { test.setTimeout(120_000) test("capture all pages in dark and light mode", async ({ page }) => { const results: { name: string title: string dark: string light: string }[] = [] // --- PUBLIC PAGES --- for (const p of PUBLIC_PAGES) { const dark = await capturePageScreenshot(page, p.route, p.name, "dark") const light = await capturePageScreenshot(page, p.route, p.name, "light") results.push({ name: p.name, title: p.title, dark, light }) } // --- LOGIN to get admin session --- await page.goto("/login", { waitUntil: "domcontentloaded" }) await page.waitForTimeout(2000) const emailField = page.locator('input[id="email"]') const passwordField = page.locator('input[id="password"]') const submitButton = page.locator('button[type="submit"]') if (await emailField.isVisible({ timeout: 5000 })) { await emailField.fill("admin@cannamanage.de") await passwordField.fill("admin123") await submitButton.click() // Wait for redirect after login await page.waitForTimeout(5000) } // Check if we got authenticated (redirected to dashboard) const isAuthenticated = page.url().includes("/dashboard") || page.url().includes("/login") if (page.url().includes("/dashboard")) { // --- ADMIN PAGES (authenticated) --- for (const p of ADMIN_PAGES) { const dark = await capturePageScreenshot(page, p.route, p.name, "dark") const light = await capturePageScreenshot( page, p.route, p.name, "light" ) results.push({ name: p.name, title: p.title, dark, light }) } } else { // Auth failed — still capture what we can see (login page with error) console.log("⚠️ Auth failed — admin pages will show login redirect") for (const p of ADMIN_PAGES) { const dark = await capturePageScreenshot(page, p.route, p.name, "dark") results.push({ name: p.name, title: `${p.title} (auth required)`, dark, light: dark, }) } } // --- PORTAL PAGES (no auth needed) --- for (const p of PORTAL_PAGES) { const dark = await capturePageScreenshot(page, p.route, p.name, "dark") const light = await capturePageScreenshot(page, p.route, p.name, "light") results.push({ name: p.name, title: p.title, dark, light }) } // Generate markdown index let md = "# CannaManage — Visual Tour (Sprint 4)\n\n" md += `**Generated:** ${new Date().toISOString().split("T")[0]}\n\n` md += "---\n\n" for (const r of results) { md += `## ${r.title}\n\n` md += `| Dark Mode | Light Mode |\n` md += `|-----------|------------|\n` md += `| ![${r.title} Dark](screenshots/${r.dark}) | ![${r.title} Light](screenshots/${r.light}) |\n\n` } // Write the markdown file const fs = await import("fs") const docsDir = path.join(__dirname, "..", "docs") if (!fs.existsSync(path.join(docsDir, "screenshots"))) { fs.mkdirSync(path.join(docsDir, "screenshots"), { recursive: true }) } fs.writeFileSync(path.join(docsDir, "visual-tour.md"), md) console.log( `\n✅ Screenshot tour complete! ${results.length} pages captured.` ) console.log(`📄 Markdown: docs/visual-tour.md`) console.log(`📸 Screenshots: docs/screenshots/`) }) })