import { chromium } from 'playwright'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import { mkdirSync } from 'fs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const SHOT_DIR = resolve(__dirname, 'assets', 'screenshots'); const APP = 'http://localhost:5173'; const DM_EMAIL = 'dm@darkwatch.test'; const DM_PASS = 'password'; const ONLY = process.argv.includes('--only') ? process.argv[process.argv.indexOf('--only') + 1] : null; mkdirSync(SHOT_DIR, { recursive: true }); // ── helpers ──────────────────────────────────────────────────────────────── async function shot(page, name, fn) { if (ONLY && name !== ONLY) return; console.log(` capturing ${name}...`); await fn(page); await page.screenshot({ path: resolve(SHOT_DIR, `${name}.png`), fullPage: false, }); console.log(` ✓ ${name}.png`); } async function login(page) { await page.goto(`${APP}/login`); await page.fill('input[type="email"]', DM_EMAIL); await page.fill('input[type="password"]', DM_PASS); await page.click('button[type="submit"]'); await page.waitForURL(`${APP}/`); } async function openCampaign(page) { // Seeded campaign is named "Tomb of the Serpent King" await page.click('text=Tomb of the Serpent King'); await page.waitForURL(/\/campaign\/\d+/); await page.waitForTimeout(1500); // socket connect + data load } // ── captures ─────────────────────────────────────────────────────────────── async function captureCharacterSheet(page) { // DM view — click the first character card to open full sheet // Selector: first .characterCard element in the card grid // Adjust class name if different in the actual DOM const card = page.locator('[class*="characterCard"]').first(); await card.click(); await page.waitForTimeout(500); } async function captureCharacterCreation(page) { // Open character creation wizard and advance to step 2 (stat rolling) await page.click('button:has-text("New Character")'); await page.waitForTimeout(400); // Fill required fields on step 1 so Next is enabled const nameInput = page.locator('input[placeholder*="name" i]').first(); await nameInput.fill('Screenshot Hero'); // Advance to step 2 await page.click('button:has-text("Next")'); await page.waitForTimeout(400); } async function captureDiceRoll(page) { // Roll a d20 — find the d20 button in the dice panel await page.click('button:has-text("d20")'); await page.waitForTimeout(2000); // wait for 3D animation to settle } async function captureSpellcasting(page) { // Open the Spells tab/panel for the first Wizard or Priest character // The spell panel may be a tab within the character sheet await page.click('text=Spells'); await page.waitForTimeout(400); } async function captureAtmosphere(page) { // Open atmosphere panel and enable fog await page.click('button[title*="tmosphere" i], button:has-text("Atmosphere")'); await page.waitForTimeout(300); await page.click('button:has-text("Fog")'); await page.waitForTimeout(600); // wait for fog to render } async function captureInitiativeActive(page) { // Click the Combat button to open the start combat modal await page.click('button:has-text("Combat")'); await page.waitForTimeout(300); // Start combat with whatever defaults are present await page.click('button:has-text("Start Combat")'); await page.waitForTimeout(500); // Roll initiative for enemies (DM roll) const rollBtn = page.locator('button:has-text("Roll")').first(); await rollBtn.click(); await page.waitForTimeout(400); } async function captureDmCards(page) { // The DM compact card grid is the default campaign view. // Navigate back to campaign root to ensure no modals are open. const url = page.url(); await page.goto(url); await page.waitForTimeout(1500); // let socket reconnect } // ── main ─────────────────────────────────────────────────────────────────── async function run() { console.log('Starting Darkwatch screenshot capture...'); console.log(`App: ${APP}`); if (ONLY) console.log(`Only: ${ONLY}`); console.log(''); const browser = await chromium.launch(); const context = await browser.newContext({ viewport: { width: 1280, height: 800 }, }); const page = await context.newPage(); await login(page); console.log('✓ Logged in as DM'); await openCampaign(page); console.log('✓ Campaign open'); console.log(''); await shot(page, 'dm-cards', captureDmCards); await shot(page, 'character-sheet', captureCharacterSheet); await shot(page, 'character-creation',captureCharacterCreation); await shot(page, 'dice-roll', captureDiceRoll); await shot(page, 'spellcasting', captureSpellcasting); await shot(page, 'atmosphere', captureAtmosphere); await shot(page, 'initiative-active', captureInitiativeActive); await browser.close(); console.log(''); console.log('Done! Screenshots saved to site/assets/screenshots/'); } run().catch(err => { console.error(err); process.exit(1); });