91 lines
5.0 KiB
JavaScript
91 lines
5.0 KiB
JavaScript
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 path = require('node:path');
|
|
|
|
const out = path.resolve('playtest-artifacts/bite-sized-spawns');
|
|
fs.mkdirSync(out, { recursive: true });
|
|
const locations = [
|
|
{ id: 'bite-sized-wailing-caverns', name: 'wailing' },
|
|
{ id: 'bite-sized-utgarde-pinnacle-skadi', name: 'utgarde' },
|
|
];
|
|
|
|
async function gameState(page) {
|
|
return page.evaluate(async () => {
|
|
const storeUrl = performance.getEntriesByType('resource').map((entry) => entry.name)
|
|
.find((url) => /\/src\/game\/store\.ts(?:\?|$)/.test(url));
|
|
if (!storeUrl) throw new Error('The active game store module was not found.');
|
|
const { useGameStore } = await import(storeUrl);
|
|
const state = useGameStore.getState();
|
|
return { assetStatus: state.assetStatus, dungeonIntroActive: state.dungeonIntroActive,
|
|
raceStartBlocked: state.raceStartBlocked,
|
|
playerPosition: state.playerPosition,
|
|
playerGrounded: state.playerGrounded, activeSpawn: state.activeSpawn };
|
|
});
|
|
}
|
|
|
|
(async () => {
|
|
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
|
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
|
const errors = [];
|
|
page.on('pageerror', (error) => errors.push(error.stack || error.message));
|
|
const results = [];
|
|
try {
|
|
await page.goto(process.env.RACE_TEST_URL || 'http://127.0.0.1:5173',
|
|
{ waitUntil: 'domcontentloaded', timeout: 120_000 });
|
|
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('SpawnTester');
|
|
await page.getByRole('button', { name: /Create character/ }).click();
|
|
await page.getByRole('button', { name: /Continue|Enter world/ }).first().click({ force: true });
|
|
await page.getByRole('button', { name: 'Continue', exact: true }).click({ force: true });
|
|
await page.locator('.main-menu-screen').waitFor();
|
|
|
|
for (const location of locations) {
|
|
await page.getByRole('button', { name: /Dungeon Race/ }).click();
|
|
await page.locator('.dungeon-race-screen__setup select').first().selectOption(location.id);
|
|
await page.getByRole('button', { name: 'Start practice' }).click();
|
|
await page.locator('.game-shell').waitFor({ timeout: 120_000 });
|
|
await page.locator('.loading-screen').waitFor({ state: 'hidden', timeout: 120_000 });
|
|
await page.locator('.dungeon-boss-intro').waitFor({ state: 'hidden', timeout: 120_000 });
|
|
await page.waitForFunction(async () => {
|
|
const storeUrl = performance.getEntriesByType('resource').map((entry) => entry.name)
|
|
.find((url) => /\/src\/game\/store\.ts(?:\?|$)/.test(url));
|
|
if (!storeUrl) return false;
|
|
const { useGameStore } = await import(storeUrl);
|
|
const state = useGameStore.getState();
|
|
return state.assetStatus === 'ready' && !state.dungeonIntroActive && !state.raceStartBlocked;
|
|
}, null, { timeout: 120_000 });
|
|
await page.waitForTimeout(5_000);
|
|
const state = await gameState(page);
|
|
await page.screenshot({ path: path.join(out, `${location.name}-after.png`) });
|
|
results.push({ location: location.name, state });
|
|
console.log(`${location.name} settled state: ${JSON.stringify(state)}`);
|
|
assert.equal(state.assetStatus, 'ready');
|
|
assert.equal(state.playerGrounded, true, `${location.name} player should be grounded`);
|
|
assert.ok(Math.abs(state.playerPosition[1] - state.activeSpawn.footPosition[1]) < 0.25,
|
|
`${location.name} player sank below the entry floor`);
|
|
assert.ok(Math.hypot(state.playerPosition[0] - state.activeSpawn.footPosition[0],
|
|
state.playerPosition[2] - state.activeSpawn.footPosition[2]) < 1,
|
|
`${location.name} player moved away from the entry point`);
|
|
await page.evaluate(async () => {
|
|
const storeUrl = performance.getEntriesByType('resource').map((entry) => entry.name)
|
|
.find((url) => /\/src\/game\/store\.ts(?:\?|$)/.test(url));
|
|
const { useGameStore } = await import(storeUrl);
|
|
useGameStore.getState().openOverlay('pause');
|
|
});
|
|
await page.getByRole('dialog', { name: 'Paused' }).getByRole('button', { name: 'Main menu' }).click();
|
|
await page.locator('.main-menu-screen').waitFor();
|
|
}
|
|
assert.deepEqual(errors, []);
|
|
const report = { results, errors, screenshots: out };
|
|
fs.writeFileSync(path.join(out, 'result.json'), `${JSON.stringify(report, null, 2)}\n`);
|
|
console.log(JSON.stringify(report, null, 2));
|
|
} catch (error) {
|
|
await page.screenshot({ path: path.join(out, 'failure.png') });
|
|
console.error((await page.locator('body').innerText()).slice(0, 3000));
|
|
throw error;
|
|
} finally { await browser.close(); }
|
|
})().catch((error) => { console.error(error); process.exitCode = 1; });
|