forked from phenom/healer-man
145 lines
9.2 KiB
JavaScript
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; });
|