Files
healer-man/scripts/playtest-graphics-options.cjs
2026-10-06 22:25:34 -04:00

136 lines
7.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { DEFAULT_TEST_DUNGEON_TITLE, DEFAULT_TEST_DUNGEON_CATEGORY, openSelectedDungeonRoles } = require('./playtest-defaults.cjs');
const { chromium } = require('C:/Users/phenom/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const outputArgument = process.argv.indexOf('--output-dir');
if (outputArgument !== -1 && !process.argv[outputArgument + 1]) {
throw new Error('--output-dir requires a directory path.');
}
const output = path.resolve(outputArgument === -1
? path.join('artifacts/playtest/graphics-options', `run-${Date.now()}`)
: process.argv[outputArgument + 1]);
const distArgument = process.argv.find(value => value.startsWith('--dist='));
const distRoot = path.resolve(distArgument ? distArgument.slice('--dist='.length) : 'dist');
const distIndexPath = path.join(distRoot, 'index.html');
const distIndexHtml = fs.readFileSync(distIndexPath, 'utf8');
const productionBundle = {
status: 'frozen-existing-dist; build-validation-recorded-separately',
indexHtmlModifiedAt: fs.statSync(distIndexPath).mtime.toISOString(),
entryFiles: [...distIndexHtml.matchAll(/(?:src|href)="([^"]+\.(?:js|css))"/g)]
.map((match) => path.basename(match[1])),
};
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'healer-man-graphics-'));
const staticDir = fs.mkdtempSync(path.join(os.tmpdir(), 'healer-man-graphics-dist-'));
fs.mkdirSync(output, { recursive: true });
fs.cpSync(distRoot, staticDir, { recursive: true });
async function enterDungeon(page, category, name) {
await page.getByRole('button', { name: /^Dungeons/ }).click();
if (category) await page.locator('.dungeon-category-tab').filter({ hasText: category }).click();
await page.locator('.dungeon-select-row').filter({ hasText: name }).first().click();
await openSelectedDungeonRoles(page);
await page.locator('.dungeon-role-option--healer').click();
await page.locator('.scene-shell').waitFor();
await page.locator('.loading-screen').waitFor({ state: 'visible', timeout: 180_000 });
await page.locator('.loading-screen').waitFor({ state: 'hidden', timeout: 180_000 });
await page.locator('.dungeon-boss-intro').waitFor({ state: 'visible', timeout: 180_000 });
const selectedBosses = await page.locator('.dungeon-boss-intro .dungeon-boss-card__body h3').allInnerTexts();
await page.locator('.dungeon-boss-intro').waitFor({ state: 'hidden', timeout: 180_000 });
await page.waitForTimeout(1000);
return selectedBosses;
}
async function canvasScale(page) {
return page.locator('.scene-shell canvas').first().evaluate((canvas) =>
Number((canvas.width / canvas.clientWidth).toFixed(2)));
}
(async () => {
const { createGameServer } = await import('../server/server.mjs');
const runtime = createGameServer({ dataDir, staticDir, enableShelvedModes: false });
await new Promise((resolve) => runtime.server.listen(4198, '127.0.0.1', resolve));
const browser = await chromium.launch({ channel: 'msedge', headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
page.setDefaultTimeout(180_000);
const errors = [];
page.on('pageerror', (error) => errors.push(error.message));
try {
const manifestArgument = process.argv.find(value => value.startsWith('--manifest='));
if (manifestArgument) await page.route('**/content/manifest.json', route => route.fulfill({
body: fs.readFileSync(path.resolve(manifestArgument.slice('--manifest='.length))), contentType: 'application/json',
}));
await page.goto('http://127.0.0.1:4198', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: /Play offline/ }).click();
await page.getByRole('button', { name: /Create character/ }).click();
await page.getByRole('button', { name: /Customize & name/ }).click();
await page.getByLabel('Character name').fill('GraphicsQA');
await page.getByRole('button', { name: /Create character/ }).click();
await page.getByRole('button', { name: /Continue|Enter world/ }).first().evaluate((button) => button.click());
await page.locator('.main-menu-screen').waitFor();
const selectedStockadeBosses = await enterDungeon(page, DEFAULT_TEST_DUNGEON_CATEGORY, DEFAULT_TEST_DUNGEON_TITLE);
assert.equal(selectedStockadeBosses.length, 1, 'Cellblock Rush should select exactly one boss.');
const selectedStockadeBoss = selectedStockadeBosses[0];
const defaultScale = await canvasScale(page);
await page.screenshot({ path: path.join(output, 'wow-default.png') });
await page.keyboard.press('Escape');
await page.getByRole('dialog', { name: 'Paused' }).waitFor();
await page.getByRole('button', { name: 'Options', exact: true }).click();
await page.getByRole('dialog', { name: 'Options' }).waitFor();
const setting = (name) => page.locator('.options-panel .option-row').filter({ has: page.getByText(name, { exact: true }) }).getByRole('button').first();
for (let index = 0; index < 3; index++) await setting('Render resolution').click();
for (let index = 0; index < 2; index++) await setting('Texture filtering').click();
await setting('Shadows').click();
await setting('Ambient fill').click();
assert.equal(await setting('Render resolution').innerText(), '200%');
assert.equal(await setting('Texture filtering').innerText(), '16×');
assert.equal(await setting('Shadows').innerText(), 'High');
await page.screenshot({ path: path.join(output, 'graphics-options.png') });
await page.setViewportSize({ width: 960, height: 540 });
await page.screenshot({ path: path.join(output, 'graphics-options-thor.png') });
assert.ok(await setting('Ambient fill').isVisible());
await page.setViewportSize({ width: 1440, height: 900 });
await page.getByRole('button', { name: 'Done', exact: true }).click();
await page.getByRole('button', { name: 'Resume game' }).click();
await page.waitForTimeout(1500);
const highScale = await canvasScale(page);
assert.ok(highScale >= 1.9, `Expected 200% render scale, got ${highScale}`);
await page.screenshot({ path: path.join(output, 'wow-high.png') });
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'Main menu', exact: true }).click();
await page.locator('.main-menu-screen').waitFor();
await enterDungeon(page, 'RoM', 'Forsaken Abbey');
assert.ok(await canvasScale(page) >= 1.9);
await page.screenshot({ path: path.join(output, 'rom-high.png') });
assert.deepEqual(errors, []);
const results = {
outputDirectory: output,
productionBundle,
dungeon: DEFAULT_TEST_DUNGEON_TITLE,
selectedBoss: selectedStockadeBoss,
defaultScale,
highScale,
romScale: await canvasScale(page),
errors,
};
fs.writeFileSync(path.join(output, 'results.json'), `${JSON.stringify(results, null, 2)}\n`);
console.log(JSON.stringify(results));
} catch (error) {
await page.screenshot({ path: path.join(output, 'failure.png') });
console.error((await page.locator('body').innerText()).slice(-1500));
console.error(errors);
throw error;
} finally {
await browser.close();
await new Promise((resolve) => runtime.server.close(resolve));
assert.ok(dataDir.startsWith(path.resolve(os.tmpdir()) + path.sep));
assert.ok(staticDir.startsWith(path.resolve(os.tmpdir()) + path.sep));
fs.rmSync(dataDir, { recursive: true, force: true });
fs.rmSync(staticDir, { recursive: true, force: true });
}
})().catch((error) => { console.error(error); process.exitCode = 1; });