Adds site/package.json and site/screenshots.js to automate capturing 7 named screenshots of the running Darkwatch app via Playwright/Chromium. Also adds site/node_modules/ to .gitignore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
147 lines
5.3 KiB
JavaScript
147 lines
5.3 KiB
JavaScript
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);
|
|
});
|