Files
healer-man/scripts/playtest-healer-mana-recovery.cjs
2026-10-07 13:05:04 -04:00

145 lines
9.2 KiB
JavaScript

const { selectDefaultTestDungeon, 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 directory = path.resolve('artifacts/playtest/healer-mana-recovery');
const runtimeData = fs.mkdtempSync(path.join(os.tmpdir(), 'healer-man-rom-status-'));
fs.mkdirSync(directory, { recursive: true });
(async () => {
const { createGameServer } = await import('../server/server.mjs');
const runtime = createGameServer({ dataDir: runtimeData, 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));
const moduleUrls = fs.readdirSync('dist/assets')
.filter((file) => /^(?:index|GameRuntime|combatStore)-.*\.js$/.test(file))
.map((file) => `/assets/${file}`);
await page.addInitScript((urls) => {
window.romStatusStores = async () => {
const exports = (await Promise.all(urls.map((url) => import(url)))).flatMap(Object.values);
const stores = exports.filter((value) => value && typeof value.getState === 'function');
return {
game: stores.find((store) => typeof store.getState()?.relocatePlayer === 'function'),
shell: stores.find((store) => typeof store.getState()?.saveActiveProgression === 'function'),
combat: stores.find((store) => typeof store.getState()?.castAbility === 'function'),
party: stores.find((store) => typeof store.getState()?.setCommand === 'function'
&& Array.isArray(store.getState()?.members)),
};
};
}, moduleUrls);
try {
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.locator('.creator-category-tabs button').filter({ hasText: 'RoM' }).click();
await page.locator('.class-option').filter({ hasText: 'Priest' }).click();
await page.getByRole('button', { name: /Customize & name/ }).click();
await page.getByLabel('Character name').fill('ManaAuraQA');
await page.getByRole('button', { name: /Create character/ }).click();
await page.locator('.character-select-screen').waitFor();
await page.evaluate(async () => {
const { shell } = await window.romStatusStores();
const state = shell.getState();
const character = state.characters.find((entry) => entry.id === state.selectedCharacterId);
if (!character) throw new Error('Priest character was not selected.');
shell.setState({ activeCharacter: character });
shell.getState().saveActiveProgression(30, 0, character.talentRanks);
shell.setState({ activeCharacter: null });
shell.getState().openMainMenu();
});
await page.locator('.main-menu-screen').waitFor();
await page.evaluate(async () => {
const { shell } = await window.romStatusStores();
shell.getState().openDungeons();
});
await page.locator('.dungeon-select-screen').waitFor();
await selectDefaultTestDungeon(page);
await openSelectedDungeonRoles(page);
await page.locator('.dungeon-role-option--healer').click();
await page.locator('.party-frames').waitFor();
await page.locator('.loading-screen').waitFor({ state: 'detached' });
await page.evaluate(async () => {
const { game } = await window.romStatusStores();
game.getState().closeOverlay();
});
await page.locator('.dungeon-boss-intro').waitFor({ state: 'detached' });
await page.waitForFunction(async () => {
const { combat } = await window.romStatusStores();
return combat.getState().level >= 30 && Object.values(combat.getState().mobs)
.some((mob) => !mob.dead && mob.homePosition);
});
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'Boss guide', exact: true }).click();
await page.getByRole('heading', { name: 'Boss Guide', exact: true }).waitFor();
const bossGuide = await page.locator('.boss-guide-panel__bosses').innerText();
const selectedBoss = await page.locator('.boss-guide-panel__bosses button.is-active strong').innerText();
await page.screenshot({ path: path.join(directory, 'selected-boss.png') });
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'Resume game' }).click();
const metadata = await page.evaluate(async () => {
const { combat, game, party } = await window.romStatusStores();
const state = combat.getState();
return { dungeonId: game.getState().activeDungeonId,
boss: Object.values(state.mobs).filter((mob) => mob.boss).map((mob) => mob.name),
classId: state.classId, level: state.level, mana: state.resourcePools.mana,
maximum: state.maxResourcePools.mana, members: party.getState().members.map((member) => ({ name: member.name, role: member.role })),
aura: state.auras.filter((aura) => aura.definition.id === 'healer-mana-recovery'), assets: performance.getEntriesByType('resource').filter((entry) => /assets\/.*\.js/.test(entry.name)).map((entry) => entry.name) };
});
metadata.boss = [selectedBoss];
assert.equal(metadata.dungeonId, 'bite-sized-stormwind-stockade');
assert(metadata.aura.length > 0, 'Recovery buff is visible out of combat');
await page.screenshot({ path: path.join(directory, 'buff-enabled.png') });
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'Options', exact: true }).click();
const row = page.locator('.option-row').filter({ hasText: 'Healer mana recovery' });
await row.scrollIntoViewIfNeeded();
assert(await row.getByRole('button', { name: 'On', exact: true }).count());
await page.screenshot({ path: path.join(directory, 'menu-enabled.png') });
await page.setViewportSize({ width: 960, height: 540 });
await row.scrollIntoViewIfNeeded();
await page.screenshot({ path: path.join(directory, 'menu-compact.png') });
await page.setViewportSize({ width: 1440, height: 900 });
await row.getByRole('button', { name: 'On', exact: true }).click();
await row.getByRole('button', { name: 'Off', exact: true }).waitFor();
const off = await page.evaluate(async () => {
const { combat, shell } = await window.romStatusStores();
return { enabled: combat.getState().settings.healerManaRecovery,
auraCount: combat.getState().auras.filter((aura) => aura.definition.id === 'healer-mana-recovery').length,
saved: shell.getState().activeCharacter?.settings?.healerManaRecovery };
});
assert.equal(off.enabled, false); assert.equal(off.auraCount, 0); assert.equal(off.saved, false);
await page.screenshot({ path: path.join(directory, 'menu-disabled.png') });
await row.getByRole('button', { name: 'Off', exact: true }).click();
await page.getByRole('button', { name: 'Return to pause menu' }).click();
await page.getByRole('button', { name: 'Resume game' }).click();
const labels = await page.locator('button.action-slot--friendly').evaluateAll((buttons) => buttons.map((button) => button.getAttribute('aria-label')));
let healButton = page.getByRole('button', { name: /^Urgent Heal,/ }).first();
if (!await healButton.count()) healButton = page.locator('button.action-slot--friendly').first();
const before = await page.evaluate(async () => { const { combat } = await window.romStatusStores(); return combat.getState().resource; });
await healButton.click();
const samples = [];
for (let index = 0; index < 40; index++) {
await page.waitForTimeout(100);
samples.push(await page.evaluate(async () => { const { combat } = await window.romStatusStores(); const state = combat.getState(); return { now: Date.now(), resource: state.resource, max: state.maxResource, casting: state.activeCast?.abilityId, engaged: Object.values(state.mobs).some((mob) => mob.engaged && !mob.dead) }; }));
}
assert(samples.some((sample) => sample.resource < before), 'Ordinary healing cast spends mana');
const lowIndex = samples.findIndex((sample) => sample.resource === Math.min(...samples.map((entry) => entry.resource)));
assert(samples.slice(lowIndex + 1).some((sample) => sample.resource > samples[lowIndex].resource), 'Mana recovers between fights');
await page.screenshot({ path: path.join(directory, 'after-heal.png') });
fs.writeFileSync(path.join(directory, 'result.json'), JSON.stringify({ metadata, bossGuide, off, labels, before, samples, errors }, null, 2)+'\n');
assert.deepEqual(errors, []);
console.log(JSON.stringify({ dungeonId: metadata.dungeonId, boss: metadata.boss, off, before, minimum: Math.min(...samples.map((entry) => entry.resource)), after: samples.at(-1).resource, errors, directory }, null, 2));
} finally {
await browser.close();
await new Promise((resolve) => runtime.server.close(resolve));
}
})().catch((error) => { console.error(error); process.exitCode = 1; });