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; });