feat: add Playwright screenshot script for brochure
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>
This commit is contained in:
parent
c11d6721f9
commit
e1cf22cae4
4 changed files with 219 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@ server/data/
|
|||
server/.env
|
||||
*.db
|
||||
.claude/settings.local.json
|
||||
site/node_modules/
|
||||
|
|
|
|||
60
site/package-lock.json
generated
Normal file
60
site/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "darkwatch-brochure-screenshots",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "darkwatch-brochure-screenshots",
|
||||
"devDependencies": {
|
||||
"playwright": "^1.43.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
site/package.json
Normal file
11
site/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "darkwatch-brochure-screenshots",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"screenshots": "node screenshots.js",
|
||||
"screenshots:only": "node screenshots.js --only"
|
||||
},
|
||||
"devDependencies": {
|
||||
"playwright": "^1.43.0"
|
||||
}
|
||||
}
|
||||
147
site/screenshots.js
Normal file
147
site/screenshots.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
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);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue