const { DEFAULT_BOSS_TEST_BOSS } = require('./playtest-defaults.cjs'); const { chromium } = require('C:/Users/phenom/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright'); const fs = require('node:fs'); const path = require('node:path'); const assert = require('node:assert/strict'); const crypto = require('node:crypto'); const selectedBossName = process.env.BOSS_TEST_BOSS || DEFAULT_BOSS_TEST_BOSS; const historicalWailingBosses = [ 'Lady Anacondra', 'Lord Cobrahn', 'Kresh', 'Lord Pythas', 'Skum', 'Lord Serpentis', 'Verdan the Everliving', 'Mutanus the Devourer', ]; if (historicalWailingBosses.includes(selectedBossName) && !process.argv.includes('--historical-wailing')) { throw new Error('A Wailing boss override requires an explicit owner request and --historical-wailing. Isolated Boss Testing defaults to Hamhock.'); } const directory = path.resolve(process.env.BOSS_TEST_ARTIFACT_DIR || path.join( 'playtest-artifacts', selectedBossName === 'Celebras the Cursed' ? 'boss-testing-scaling' : 'boss-testing', )); fs.mkdirSync(directory, { recursive: true }); const base = process.env.BOSS_TEST_URL || 'http://127.0.0.1:4187'; (async () => { const { createGameServer } = await import('../server/server.mjs'); const runtime = createGameServer({ dataDir: path.join(directory, 'test-database'), enableShelvedModes: false }); await new Promise((resolve) => runtime.server.listen(4187, '127.0.0.1', resolve)); console.log('Started isolated production test server'); const browser = await chromium.launch({ channel: 'msedge', headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } }); const page = await context.newPage(); page.setDefaultTimeout(60000); const errors = []; const assetFiles = fs.readdirSync(path.resolve('dist/assets')); const mainModule = `/assets/${assetFiles.find((file) => /^index-.*\.js$/.test(file))}`; const combatModule = `/assets/${assetFiles.find((file) => /^combatStore-.*\.js$/.test(file))}`; await page.addInitScript(({ mainModule, combatModule }) => { window.bossTestingStores = async () => { const main = Object.values(await import(mainModule)); const combat = Object.values(await import(combatModule)); return { useGameStore: main.find((value) => value?.getState && 'bossTest' in value.getState()), useShellStore: main.find((value) => value?.getState && 'characters' in value.getState()), useCombatStore: combat.find((value) => value?.getState && 'damageMob' in value.getState()), }; }; }, { mainModule, combatModule }); page.on('pageerror', (error) => errors.push(error.message)); const capture = async (name) => { await page.screenshot({ path: path.join(directory, `${name}.png`) }); console.log(`Captured ${name}`); }; const canvasHash = async () => { const canvas = page.locator('canvas'); await canvas.evaluate((element) => { const hidden = []; let current = element; while (current.parentElement) { for (const sibling of current.parentElement.children) { if (sibling === current || !(sibling instanceof HTMLElement)) continue; hidden.push([sibling, sibling.style.visibility]); sibling.style.visibility = 'hidden'; } current = current.parentElement; } window.__restoreCanvasCapture = () => { for (const [node, visibility] of hidden) node.style.visibility = visibility; delete window.__restoreCanvasCapture; }; }); try { return crypto.createHash('sha256').update(await canvas.screenshot()).digest('hex'); } finally { await page.evaluate(() => window.__restoreCanvasCapture?.()); } }; try { await page.goto(base, { waitUntil: 'domcontentloaded' }); console.log('Production menu loaded'); 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('Bosstester'); await page.getByRole('button', { name: /Create character/ }).click(); await page.getByRole('button', { name: /Continue|Enter world/ }).first().click(); await page.locator('.main-menu-screen').waitFor(); await capture('01-main-menu'); await page.getByRole('button', { name: /Boss Testing/ }).click(); await page.getByRole('heading', { name: 'Boss Testing', exact: true }).waitFor(); const picker = page.getByLabel(/Choose a boss/); const option = await picker.locator('option').evaluateAll((options, name) => options.find((entry) => entry.textContent.startsWith(`${name} ยท`))?.value, selectedBossName); assert(option, `${selectedBossName} is selectable`); await picker.selectOption(option); await page.getByRole('button', { name: 'Test selected boss', exact: true }).waitFor({ state: 'visible' }); await capture('02-boss-selection'); await page.getByRole('button', { name: 'Test selected boss', exact: true }).click(); if (await page.getByRole('alert').count()) throw new Error(await page.getByRole('alert').innerText()); await page.locator('canvas').waitFor(); await page.waitForFunction(async () => { const { useGameStore, useCombatStore } = await window.bossTestingStores(); const game = useGameStore.getState(); return game.assetStatus === 'ready' && game.bossTest?.runtimeIds.every((id) => useCombatStore.getState().mobs[id]); }, null, { timeout: 90000 }); await page.waitForTimeout(4000); const bossFrameBefore = await canvasHash(); await page.waitForTimeout(500); assert.notEqual( await canvasHash(), bossFrameBefore, 'Boss Testing redraws the WebGL scene independently from its HUD and nameplates', ); await capture('03-empty-room'); const before = await page.evaluate(async () => { const { useGameStore, useCombatStore, useShellStore } = await window.bossTestingStores(); const game = useGameStore.getState(); const combat = useCombatStore.getState(); const boss = game.bossTest; return { boss, xp: combat.xp, saved: JSON.stringify(useShellStore.getState().characters), count: Object.keys(combat.mobs).length, classId: combat.classId, level: combat.level, bossLevel: combat.mobs[boss.runtimeIds[0]].level, bossMaxHealth: combat.mobs[boss.runtimeIds[0]].maxHealth }; }); assert.equal(before.count, 1, 'Only the selected boss spawns initially'); if (selectedBossName === 'Celebras the Cursed') { assert.equal(before.classId, 'priest'); assert.equal(before.level, 1); assert.equal(before.bossLevel, 2); assert.equal(before.bossMaxHealth, 575, 'Level-one Celebras uses scaled native health without campaign floors'); } await page.keyboard.press('Escape'); await page.getByRole('button', { name: 'Boss readiness & report', exact: true }).click(); await page.getByRole('button', { name: 'Mark not ready', exact: true }).click(); await page.getByRole('status').filter({ hasText: 'Marked not ready.' }).waitFor(); await page.getByRole('button', { name: 'Mark needs testing', exact: true }).click(); await page.getByRole('status').filter({ hasText: 'Marked needs testing.' }).waitFor(); await capture('04-pause-review'); await page.getByRole('button', { name: 'Submit a boss report', exact: true }).click(); await page.getByLabel('Describe the issue', { exact: true }).fill('Boss Testing playtest: verify mechanic telegraphs.'); await page.getByRole('button', { name: 'Submit report', exact: true }).click(); await page.getByText('Report submitted.', { exact: false }).waitFor(); await capture('05-boss-report'); await page.getByRole('button', { name: 'Back to boss review', exact: true }).click(); await page.getByRole('button', { name: 'Fight this boss again', exact: true }).click(); await page.waitForTimeout(1000); await page.evaluate(async () => { const { useGameStore, useCombatStore } = await window.bossTestingStores(); const game = useGameStore.getState(); const combat = useCombatStore.getState(); combat.selectMob(game.bossTest.runtimeIds[0]); combat.damageMob(game.bossTest.runtimeIds[0], 1000000, Date.now()); }); await page.getByText('Boss defeated', { exact: true }).waitFor(); await page.getByRole('button', { name: 'Mark ready', exact: true }).click(); await page.getByRole('status').filter({ hasText: 'Marked ready.' }).waitFor(); await capture('06-victory-review'); const after = await page.evaluate(async () => { const { useCombatStore, useShellStore } = await window.bossTestingStores(); return { xp: useCombatStore.getState().xp, saved: JSON.stringify(useShellStore.getState().characters) }; }); assert.equal(after.xp, before.xp); assert.equal(after.saved, before.saved); const reports = runtime.database.listDungeonReports().reports; assert.equal(reports[0].bossKey, before.boss.key); assert.equal(runtime.database.listBossTestingReviews().reviews[0].status, 'ready'); await page.evaluate(async (previousKey) => { window.previousBossTestingKey = previousKey; window.liveBossTestingStores = await window.bossTestingStores(); }, before.boss.key); await page.getByRole('button', { name: 'Fight another random boss', exact: true }).click(); await page.waitForFunction(() => { const { useGameStore, useCombatStore } = window.liveBossTestingStores; const game = useGameStore.getState(); return game.assetStatus === 'ready' && game.bossTest?.key !== window.previousBossTestingKey && game.bossTest?.runtimeIds.every((id) => useCombatStore.getState().mobs[id]); }, null, { timeout: 120000 }); const nextBoss = await page.evaluate(() => { const { useGameStore } = window.liveBossTestingStores; return useGameStore.getState().bossTest; }); assert.notEqual(nextBoss.key, before.boss.key, 'Post-fight random selection must choose another boss'); await page.waitForTimeout(1000); await capture('06b-another-random-boss'); await page.keyboard.press('Escape'); await page.getByRole('button', { name: 'Return to main menu', exact: true }).click(); await page.locator('.main-menu-screen').waitFor(); await page.getByRole('button', { name: /Boss Testing/ }).click(); await picker.selectOption(option); await page.getByRole('article').getByText('Ready', { exact: true }).waitFor(); await page.setViewportSize({ width: 390, height: 844 }); await capture('07-mobile-selection'); assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1), 'No mobile horizontal overflow'); await page.setViewportSize({ width: 1440, height: 1000 }); await page.getByLabel('Search bosses').fill(selectedBossName); await page.getByRole('button', { name: 'Fight a random boss', exact: true }).click(); await page.locator('canvas').waitFor(); await page.waitForTimeout(3000); await capture('08-random-fight'); await page.keyboard.press('Escape'); await page.getByRole('button', { name: 'Return to main menu', exact: true }).click(); await page.getByRole('button', { name: /Boss Testing/ }).click(); const missing = await picker.locator('option').evaluateAll((options) => options.find((entry) => /Taragaman the Hungerer.*Ragefire Chasm/.test(entry.textContent))?.value); assert(missing, 'An uninstalled boss is selectable'); await picker.selectOption(missing); await capture('09-automatic-download'); await page.waitForFunction(() => { const button = [...document.querySelectorAll('button')].find((entry) => entry.textContent === 'Test selected boss'); const error = document.querySelector('[role="alert"]'); if (error) throw new Error(error.textContent); return button && !button.disabled; }, null, { timeout: 120000 }); const installed = await page.evaluate(() => JSON.parse(localStorage.getItem('healer-man.content-state.v2') || '{}')); assert(installed.packs['dungeon:ragefire-chasm'], 'The selected boss pack downloads automatically'); await page.getByRole('button', { name: 'Test selected boss', exact: true }).click(); await page.waitForFunction(async () => { const { useGameStore, useCombatStore } = await window.bossTestingStores(); const game = useGameStore.getState(); return game.assetStatus === 'ready' && game.bossTest?.runtimeIds.every((id) => useCombatStore.getState().mobs[id]); }, null, { timeout: 90000 }); await page.waitForTimeout(1500); await capture('10-downloaded-boss-room'); const bottom = await page.context().newPage(); bottom.on('pageerror', (error) => errors.push(error.message)); await bottom.setViewportSize({ width: 1240, height: 1080 }); await bottom.goto(`${base}/?display=bottom&nativeLayout=1`, { waitUntil: 'domcontentloaded' }); await bottom.getByRole('button', { name: 'Boss review', exact: true }).waitFor(); await bottom.screenshot({ path: path.join(directory, '11-bottom-display.png') }); await bottom.getByRole('button', { name: 'Boss review', exact: true }).click(); await page.getByRole('heading', { name: 'Taragaman the Hungerer', exact: true }).waitFor(); await capture('12-bottom-display-review'); await bottom.close(); assert.deepEqual(errors, [], 'No browser runtime errors'); fs.writeFileSync(path.join(directory, 'results.json'), `${JSON.stringify({ passed: true, boss: before.boss, classId: before.classId, characterLevel: before.level, bossLevel: before.bossLevel, bossMaxHealth: before.bossMaxHealth, initialMobCount: before.count, nextRandomBoss: nextBoss, xpUnchanged: true, profileUnchanged: true, automaticDownload: 'dungeon:ragefire-chasm', report: reports[0], errors }, null, 2)}\n`); console.log('Boss Testing live flow passed'); } catch (error) { await capture('failure'); console.log((await page.locator('body').innerText()).slice(-2000)); console.log('Browser errors:', errors); throw error; } finally { await browser.close(); await new Promise((resolve) => runtime.server.close(resolve)); } })().catch((error) => { console.error(error); process.exitCode = 1; });