Release Healer Man 0.1.43
This commit is contained in:
1 parent
c7e8163790
commit
ccc01396cd
86 files changed
+2951
-198
No files matched your search
@@ -0,0 +1,168 @@
|
||||
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 directory = path.resolve('playtest-artifacts/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}`);
|
||||
};
|
||||
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) => options.find((entry) => /Lady Anacondra.*Wailing Caverns/.test(entry.textContent))?.value);
|
||||
assert(option, 'Lady Anacondra 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);
|
||||
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 };
|
||||
});
|
||||
assert.equal(before.count, 1, 'Only the selected boss spawns initially');
|
||||
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.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('Lady Anacondra');
|
||||
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, initialMobCount: before.count, 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; });
|
||||
@@ -0,0 +1,249 @@
|
||||
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 directory = path.resolve('playtest-artifacts/thor-ui');
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
const base = process.env.THOR_TEST_URL || 'http://127.0.0.1:5176';
|
||||
const results = [];
|
||||
(async () => {
|
||||
console.log('Launching Thor UI browser');
|
||||
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
||||
console.log('Browser launched');
|
||||
const context = await browser.newContext({ hasTouch: true, userAgent: 'Mozilla/5.0 (Linux; Android 13; AYN Thor) AppleWebKit/537.36 Chrome/130.0.0.0 Mobile Safari/537.36' });
|
||||
await context.addInitScript(() => performance.setResourceTimingBufferSize(20000));
|
||||
const top = await context.newPage();
|
||||
const bottom = await context.newPage();
|
||||
console.log('Two pages ready');
|
||||
const errors = [];
|
||||
for (const [page, width, height, diagonal] of [[top, 1920, 1080, 6], [bottom, 1240, 1080, 3.92]]) {
|
||||
const ppi = Math.hypot(width, height) / diagonal;
|
||||
const viewport = { width: Math.round(width / ppi * 160), height: Math.round(height / ppi * 160) };
|
||||
await page.setViewportSize(viewport);
|
||||
const cdp = await context.newCDPSession(page);
|
||||
await cdp.send('Emulation.setDeviceMetricsOverride', { ...viewport, deviceScaleFactor: ppi / 160, mobile: false });
|
||||
console.log('Configured panel', viewport);
|
||||
page.setDefaultTimeout(60000);
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
}
|
||||
const capture = async (name) => {
|
||||
for (const [surface, page] of [['top', top], ['bottom', bottom]]) {
|
||||
await page.screenshot({ path: path.join(directory, `${name}-${surface}.png`) });
|
||||
const overflow = await page.evaluate(() => {
|
||||
const root = document.querySelector('#root');
|
||||
const buttons = [...document.querySelectorAll('button')].filter((node) => node.getBoundingClientRect().width && getComputedStyle(node).visibility !== 'hidden');
|
||||
const clipped = buttons.filter((node) => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
if (rect.right <= innerWidth + 1 && rect.left >= -1 && rect.bottom <= innerHeight + 1 && rect.top >= -1) return false;
|
||||
for (let parent = node.parentElement; parent && parent !== root; parent = parent.parentElement) {
|
||||
const css = getComputedStyle(parent);
|
||||
if (/(auto|scroll)/.test(css.overflow + css.overflowY + css.overflowX)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return { width: innerWidth, height: innerHeight, clipped: clipped.map((node) => node.textContent.trim().slice(0, 60)) };
|
||||
});
|
||||
results.push({ name, surface, ...overflow });
|
||||
}
|
||||
console.log(`Captured ${name}`);
|
||||
};
|
||||
try {
|
||||
console.log('Opening top');
|
||||
await top.goto(`${base}/?display=top&nativeLayout=1`, { waitUntil: 'domcontentloaded' });
|
||||
console.log('Top DOM ready');
|
||||
await top.getByLabel('Username', { exact: true }).waitFor();
|
||||
await bottom.goto(`${base}/?display=bottom&nativeLayout=1`, { waitUntil: 'domcontentloaded' });
|
||||
await bottom.locator('.companion-panel').waitFor();
|
||||
await capture('01-login');
|
||||
await top.getByRole('button', { name: /Play offline/ }).click();
|
||||
await top.locator('.character-select-screen').waitFor();
|
||||
await capture('02-roster-empty');
|
||||
await top.getByRole('button', { name: /Create character/ }).click();
|
||||
await top.locator('.character-create-screen').waitFor();
|
||||
await capture('03-character-create');
|
||||
await top.getByRole('button', { name: /Customize & name/ }).tap();
|
||||
await top.getByLabel('Character name').fill('Thortester');
|
||||
await capture('03b-appearance');
|
||||
await top.getByRole('button', { name: /Create character/ }).tap();
|
||||
await top.locator('.character-select-screen').waitFor();
|
||||
await capture('04-roster');
|
||||
await top.bringToFront();
|
||||
await top.waitForTimeout(2000);
|
||||
await capture('04b-roster-rendered');
|
||||
await top.getByRole('button', { name: /Continue|Enter world/ }).first().click();
|
||||
await top.locator('.main-menu-screen').waitFor();
|
||||
await capture('05-main-menu');
|
||||
await top.getByRole('button', { name: /Class Hall/ }).tap();
|
||||
await top.locator('.class-hall-screen').waitFor();
|
||||
await capture('05b-class-hall');
|
||||
await top.locator('.class-hall-header button').tap();
|
||||
for (const name of ['Talents', 'Keybinds', 'Button assignments']) {
|
||||
await top.getByRole('button', { name: new RegExp(name) }).tap();
|
||||
await top.locator('.main-menu-character-editor').waitFor();
|
||||
await top.locator('.main-menu-character-editor .game-panel').waitFor();
|
||||
await capture(`05-editor-${name.toLowerCase().replaceAll(' ', '-')}`);
|
||||
await top.getByRole('button', { name: 'Back to main menu', exact: true }).tap();
|
||||
}
|
||||
await top.getByRole('button', { name: /View Reports/ }).tap();
|
||||
await top.getByRole('heading', { name: 'View Reports' }).waitFor();
|
||||
await capture('05c-reports');
|
||||
await top.getByRole('button', { name: /Back/ }).first().tap();
|
||||
await top.getByRole('button', { name: /Downloads/ }).tap();
|
||||
await top.locator('.content-manager-screen').waitFor();
|
||||
await capture('05d-downloads');
|
||||
await top.getByRole('button', { name: 'Back', exact: true }).tap();
|
||||
await top.getByRole('button', { name: /^Dungeons/ }).click();
|
||||
await top.locator('.dungeon-select-screen').waitFor();
|
||||
await capture('06-dungeon-picker');
|
||||
await top.getByRole('button', { name: /Choose role/ }).click();
|
||||
await capture('07-role-picker');
|
||||
await top.locator('.dungeon-role-option').first().click();
|
||||
await top.locator('.game-shell').waitFor();
|
||||
await bottom.locator('.companion-hud-shell').waitFor();
|
||||
await top.waitForFunction(async () => { const { useGameStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/store.ts').name); return ['ready', 'proxy', 'error'].includes(useGameStore.getState().assetStatus); }, null, { timeout: 120000 });
|
||||
await top.locator('.loading-screen').waitFor({ state: 'hidden', timeout: 120000 });
|
||||
await top.waitForTimeout(1500);
|
||||
await capture('08-dungeon');
|
||||
assert.equal(await bottom.locator('[data-hud-element="party"]').count(), 0);
|
||||
assert.equal(await top.locator('[data-hud-element="actions"]').count(), 0);
|
||||
assert.equal(await top.locator('[data-hud-element="player"]').count(), 0);
|
||||
assert.equal(await bottom.locator('.action-bar').count(), 1);
|
||||
assert.equal(await bottom.locator('.player-status-panel').count(), 1);
|
||||
await bottom.locator('.aura--inspectable').first().tap();
|
||||
await bottom.getByRole('tooltip').waitFor();
|
||||
await capture('08b-buff-inspection');
|
||||
await bottom.getByRole('button', { name: /^Fade,/ }).tap();
|
||||
await top.waitForFunction(async () => {
|
||||
const { useCombatStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/combatStore.ts').name);
|
||||
return Object.values(useCombatStore.getState().cooldowns).some((endsAt) => endsAt > Date.now());
|
||||
});
|
||||
for (const overlay of ['pause', 'map', 'party', 'boss-guide', 'spellbook', 'talents', 'bindings', 'inventory', 'boss-cards', 'options', 'keybinds', 'report-issue']) {
|
||||
await top.evaluate(async (overlay) => { const { useGameStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/store.ts').name); useGameStore.getState().openOverlay(overlay); }, overlay);
|
||||
await top.waitForTimeout(500);
|
||||
await capture(`09-${overlay}`);
|
||||
}
|
||||
await bottom.getByRole('button', { name: 'Edit UI', exact: true }).tap();
|
||||
await top.locator('.ui-layout-editor').waitFor();
|
||||
assert.equal(await top.getByLabel('Party frames screen').inputValue(), 'top');
|
||||
assert.equal(await top.getByLabel('Buffs and debuffs screen').inputValue(), 'bottom');
|
||||
await top.getByLabel('Party frames screen').selectOption('bottom');
|
||||
await bottom.getByRole('button', { name: 'Move Party frames', exact: true }).waitFor();
|
||||
assert.equal(await top.locator('[data-hud-element="party"]').count(), 0);
|
||||
await capture('10-editor-party-bottom');
|
||||
const handle = bottom.getByRole('button', { name: 'Move Party frames', exact: true });
|
||||
const rect = await handle.boundingBox();
|
||||
const cdp = await context.newCDPSession(bottom);
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x: rect.x + 10, y: rect.y + 10 }] });
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{ x: rect.x + 90, y: rect.y + 60 }] });
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
|
||||
await top.waitForFunction(async () => {
|
||||
const { useUiLayoutStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/uiLayoutStore.ts').name);
|
||||
return useUiLayoutStore.getState().draft.elements.party.position !== null;
|
||||
});
|
||||
await top.getByLabel('Party frames screen').selectOption('hidden');
|
||||
await bottom.waitForFunction(() => !document.querySelector('[data-hud-element="party"]'));
|
||||
await top.getByLabel('Party frames screen').selectOption('bottom');
|
||||
await top.getByRole('button', { name: 'Cancel edits', exact: true }).tap();
|
||||
await top.locator('.ui-layout-editor').waitFor({ state: 'hidden' });
|
||||
await bottom.waitForFunction(() => !document.querySelector('[data-hud-element="party"]'));
|
||||
await bottom.getByRole('button', { name: 'Edit UI', exact: true }).tap();
|
||||
await top.getByLabel('Party frames screen').selectOption('bottom');
|
||||
await capture('11-editor');
|
||||
await top.getByRole('button', { name: 'Save UI', exact: true }).tap();
|
||||
await top.waitForFunction(() => !document.querySelector('.ui-layout-editor'));
|
||||
await capture('12-saved');
|
||||
await top.reload({ waitUntil: 'domcontentloaded' });
|
||||
await top.locator('.character-select-screen').waitFor();
|
||||
await top.getByRole('button', { name: /Continue/ }).tap();
|
||||
await top.getByRole('button', { name: /^Dungeons/ }).tap();
|
||||
await top.getByRole('button', { name: /Choose role/ }).tap();
|
||||
await top.locator('.dungeon-role-option').first().tap();
|
||||
await bottom.locator('[data-hud-element="party"] .party-frame').first().waitFor();
|
||||
assert.equal(await top.locator('[data-hud-element="party"]').count(), 0);
|
||||
await bottom.locator('.party-frame--player').tap();
|
||||
await top.waitForFunction(async () => {
|
||||
const { usePartyStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/partyStore.ts').name);
|
||||
return usePartyStore.getState().selfSelected;
|
||||
});
|
||||
const partyBounds = await bottom.locator('.party-frames').boundingBox();
|
||||
const menuBounds = await bottom.locator('.companion-hud-menus').boundingBox();
|
||||
assert.ok(partyBounds.y + partyBounds.height <= menuBounds.y, 'Relocated party frames must fit above the bottom navigation');
|
||||
await capture('13-persisted-party-bottom');
|
||||
await bottom.getByRole('button', { name: 'Edit UI', exact: true }).tap();
|
||||
await top.getByRole('button', { name: 'Reset all elements', exact: true }).tap();
|
||||
await top.getByRole('button', { name: 'Save UI', exact: true }).tap();
|
||||
await capture('14-final-defaults');
|
||||
await top.locator('.loading-screen').waitFor({ state: 'hidden', timeout: 120000 });
|
||||
await top.evaluate(async () => {
|
||||
const load = (pathname) => import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === pathname).name);
|
||||
const { useGameStore } = await load('/src/game/store.ts');
|
||||
const { useCombatStore } = await load('/src/game/combatStore.ts');
|
||||
useGameStore.setState({ paused: true, overlay: null });
|
||||
useCombatStore.setState({ health: 0 });
|
||||
});
|
||||
await top.locator('.defeated-prompt').waitFor();
|
||||
assert.equal(await bottom.locator('.defeated-prompt').count(), 0);
|
||||
await capture('14b-defeated');
|
||||
await top.getByRole('button', { name: 'Respawn at entrance', exact: true }).tap();
|
||||
await top.locator('.defeated-prompt').waitFor({ state: 'hidden' });
|
||||
// Isolated offline presentation fixture: exercise the completed-run UI without a live boss clear.
|
||||
await top.evaluate(async () => {
|
||||
const load = (pathname) => import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === pathname).name);
|
||||
const { useGameStore } = await load('/src/game/store.ts');
|
||||
const { useDungeonRunStore } = await load('/src/game/dungeonRunStore.ts');
|
||||
const { usePartyStore } = await load('/src/game/partyStore.ts');
|
||||
const { useCombatStore } = await load('/src/game/combatStore.ts');
|
||||
const progress = useDungeonRunStore.getState().progress;
|
||||
const now = performance.now();
|
||||
const party = [{ name: 'Thortester', classId: useCombatStore.getState().classId, level: 1, role: 'healer', npc: false },
|
||||
...usePartyStore.getState().members.map((member) => ({ name: member.name, classId: member.classId, level: member.level, role: member.role, npc: true }))];
|
||||
useGameStore.setState({ paused: true, overlay: null });
|
||||
useDungeonRunStore.setState({ progress: { ...progress, startedAt: now - 60000, finishedAt: now, durationMs: 60000, party } });
|
||||
});
|
||||
await top.locator('.dungeon-complete-banner').waitFor();
|
||||
assert.equal(await bottom.locator('.dungeon-complete-banner').count(), 0);
|
||||
await capture('14c-complete');
|
||||
await top.locator('.dungeon-results').waitFor();
|
||||
await capture('14d-results');
|
||||
await top.getByRole('button', { name: /Stay and loot/ }).tap();
|
||||
await top.getByRole('button', { name: 'Run results', exact: true }).tap();
|
||||
await top.locator('.dungeon-results').waitFor();
|
||||
await top.getByRole('button', { name: /Stay and loot/ }).tap();
|
||||
await bottom.getByRole('button', { name: 'Edit UI', exact: true }).tap();
|
||||
await top.getByLabel('Dungeon completion screen').selectOption('bottom');
|
||||
await top.getByRole('button', { name: 'Save UI', exact: true }).tap();
|
||||
await bottom.locator('.dungeon-complete-banner').waitFor();
|
||||
assert.equal(await top.locator('.dungeon-complete-banner').count(), 0);
|
||||
await capture('14e-complete-bottom');
|
||||
await bottom.getByRole('button', { name: 'Finish dungeon', exact: true }).tap();
|
||||
await top.locator('.main-menu-screen').waitFor();
|
||||
await top.evaluate(async () => {
|
||||
const { useUiLayoutStore } = await import(performance.getEntriesByType('resource').findLast((entry) => new URL(entry.name).pathname === '/src/game/uiLayoutStore.ts').name);
|
||||
const state = useUiLayoutStore.getState(); state.begin(); state.updateDraft((draft) => ({ ...draft, elements: { ...draft.elements, completion: { visible: true, position: null, width: null } } })); state.finish(true);
|
||||
});
|
||||
const previewCdp = await context.newCDPSession(top);
|
||||
await top.setViewportSize({ width: 1200, height: 1100 });
|
||||
await previewCdp.send('Emulation.setDeviceMetricsOverride', { width: 1200, height: 1100, deviceScaleFactor: 1, mobile: false });
|
||||
await top.goto(`${base}/?layout=thor-preview`, { waitUntil: 'domcontentloaded' });
|
||||
await top.getByRole('button', { name: /Continue/ }).first().tap();
|
||||
await top.locator('.thor-preview-top').getByRole('button', { name: /^Dungeons/ }).tap();
|
||||
await top.locator('.thor-preview-top').getByRole('button', { name: /Choose role/ }).tap();
|
||||
await top.locator('.thor-preview-top .dungeon-role-option').first().tap();
|
||||
await top.locator('.thor-preview-top .loading-screen').waitFor({ state: 'hidden', timeout: 120000 });
|
||||
await top.waitForTimeout(1000);
|
||||
assert.equal((await top.locator('.thor-preview-top').boundingBox()).width, 837);
|
||||
assert.equal((await top.locator('.thor-preview-bottom').boundingBox()).width, 473);
|
||||
assert.equal(await top.locator('.thor-preview-bottom [data-hud-element="party"]').count(), 0);
|
||||
assert.equal(await top.locator('.thor-preview-top .action-bar').count(), 0);
|
||||
await top.screenshot({ path: path.join(directory, '15-combined-preview.png'), fullPage: true });
|
||||
results.push({ name: '15-combined-preview', surface: 'both', clipped: [] });
|
||||
assert.deepEqual(results.filter((result) => result.clipped.length), [], 'Controls must fit each screen or an intentional scroll region');
|
||||
assert.equal(errors.length, 0, errors.join('\n'));
|
||||
fs.writeFileSync(path.join(directory, 'results.json'), `${JSON.stringify({ results, errors }, null, 2)}\n`);
|
||||
} finally {
|
||||
console.log('Browser errors', errors);
|
||||
await top.screenshot({ path: path.join(directory, 'last-top.png') });
|
||||
fs.writeFileSync(path.join(directory, 'results.json'), `${JSON.stringify({ results, errors }, null, 2)}\n`);
|
||||
await browser.close();
|
||||
}
|
||||
})().catch((error) => { console.error(error); process.exitCode = 1; });
|
||||
Reference in new issue
Block a user