Files
leggenda-del-regolo/tests/multimedia.browser.mjs
T

129 lines
7.9 KiB
JavaScript

import assert from 'node:assert/strict';
export async function checkMultimedia(browser, port, out, errors) {
for (const mobile of [false, true]) {
const page = await browser.newPage({ viewport: mobile ? { width: 844, height: 390 } : { width: 1280, height: 860 }, hasTouch: mobile, isMobile: mobile });
page.on('pageerror', e => errors.push(e.message));
await page.goto(`http://127.0.0.1:${port}/`);
await page.click('#splash'); await page.click('#new-game');
await page.click('[data-rione="campomigliaio"]'); await page.click('#skip-intro');
const settle = async () => {
for (let i = 0; i < 600; i++) {
if (await page.evaluate(() => !!game.speech)) await page.keyboard.press('Space');
// Il risveglio dell'inizio è un primo piano che va avanti da solo: si aspetta che finisca.
if (await page.evaluate(() => (game.closeup && game.story.closeups[game.closeup.id].type !== 'risveglio') || game.idle)) return;
await page.waitForTimeout(30);
}
throw new Error('la sequenza non si ferma');
};
await settle();
await page.waitForFunction(() => ambience.audible && ambience.context.state === 'running');
await page.click('#mute');
assert.equal(await page.evaluate(() => ambience.nodes.size), 0, 'muto immediato');
await page.click('#mute');
await page.click('#pause-button');
await page.locator('#effects-volume').fill('65');
assert.equal(await page.evaluate(() => JSON.parse(localStorage.getItem('leggenda-regolo-audio')).effects), 0.65);
assert.equal(await page.evaluate(() => ambience.nodes.size), 0, 'in pausa nessun suono');
await page.screenshot({ path: `${out}/media-pausa-${mobile ? 'telefono' : 'desktop'}.png` });
await page.click('#resume');
if (mobile) {
// Il libro già esistente deve continuare a sfogliarsi anche sul telefono.
await page.evaluate(() => { game.state.inventory.push('libro'); game.act('open', 'libro'); });
await page.waitForFunction(() => game.closeup?.step === 'cover');
const r = await page.locator('#scene').boundingBox();
await page.touchscreen.tap(r.x + r.width / 2, r.y + r.height * 0.6);
await page.waitForFunction(() => game.closeup.step === 'title');
await page.touchscreen.tap(r.x + r.width / 2, r.y + r.height * 0.6);
await page.waitForFunction(() => game.closeup.step === 'end');
await page.screenshot({ path: `${out}/media-libro-telefono.png` });
await page.tap('#closeup-close'); await settle();
}
await page.evaluate(() => {
game.state.flags.indizio_regolo = true;
game.state.flags.casetta_aperta = true;
game.state.inventory.push('torcia');
game.go('fortezza', [462, 310]);
});
await settle();
const at = async ([x, y]) => {
const r = await page.locator('#scene').boundingBox();
return [r.x + x / 640 * r.width, r.y + y / 360 * r.height];
};
const openTorch = async () => {
await page.click('#verbs [data-verb="use"]'); await page.click('#inventory [data-item="torcia"]');
await page.mouse.click(...await at([501, 285]));
await settle();
await page.waitForFunction(() => game.closeup?.id === 'fessura');
};
await openTorch();
await page.keyboard.press('Escape'); await settle();
assert.equal(await page.evaluate(() => game.state.ended), false, 'uscire presto non vince');
await openTorch();
const saved = await page.evaluate(() => game.savable());
await page.click('#pause-button');
const age = await page.evaluate(() => game.closeup.age);
await page.waitForTimeout(150);
assert.equal(await page.evaluate(() => game.closeup.age), age, 'il primo piano è fermo in pausa');
await page.click('#resume');
const origin = await page.evaluate(() => [game.state.x, game.state.y]);
if (mobile) {
const cdp = await page.context().newCDPSession(page);
const [x, y] = await at([320, 180]), [tx, ty] = await at([300, 90]);
await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x, y }] });
await cdp.send('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{ x: tx, y: ty }] });
await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
const beam = await page.evaluate(() => game.closeup.beam);
assert.ok(Math.abs(beam[0] - 300) < 2 && Math.abs(beam[1] - 90) < 2, 'trascinare il dito sposta la torcia');
await cdp.detach();
}
for (let i = 0; i < 3; i++) {
const target = await page.evaluate(i => game.story.closeups.fessura.targets[i].at, i);
if (mobile) await page.touchscreen.tap(...await at(target));
else if (i === 0) await page.keyboard.press('Enter');
else await page.mouse.move(...await at(target));
await page.waitForFunction(() => game.closeup?.step === 'reveal');
await page.screenshot({ path: `${out}/media-torcia-${mobile ? 'telefono' : 'desktop'}-${i}.png` });
for (let n = 0; n < 200; n++) {
if (await page.evaluate(i => game.closeup.found > i, i)) break;
if (await page.evaluate(() => !!game.speech)) await page.keyboard.press('Space');
await page.waitForTimeout(25);
}
assert.equal(await page.evaluate(() => game.closeup.found), i + 1);
}
assert.deepEqual(await page.evaluate(() => [game.state.x, game.state.y]), origin, 'il puntatore non muove Andrea');
assert.deepEqual(await page.evaluate(() => game.savable()), saved, 'durante il primo piano resta il punto fermo');
if (mobile) await page.tap('#closeup-close'); else await page.keyboard.press('Enter');
await settle();
await page.waitForSelector('#ending:not([hidden])');
assert.ok(await page.evaluate(() => game.checkpoint.ended));
assert.equal(await page.evaluate(() => ambience.nodes.size), 0, 'alla fine non resta il vento della fessura');
// Misura il segnale realmente prodotto da Web Audio: udibile, finito, senza saturazione.
if (!mobile) {
const levels = await page.evaluate(async () => {
const { Soundscape } = await import('/src/engine/soundscape.js');
const results = {};
for (const kind of ['page', 'card-slide', 'card-tap', 'card-shuffle', 'wood', 'drip', 'bird', 'cricket', 'bell', 'glass', 'water', 'wind']) {
const ctx = new OfflineAudioContext(2, 44100 * 2, 44100);
const s = new Soundscape({ createContext: () => ctx });
s.unlock(); s.configure({ active: true });
if (kind === 'water' || kind === 'wind') s.update(kind === 'water' ? 'acqua' : 'fortezza', 'sera', 0.1);
else s.play(kind);
const data = (await ctx.startRendering()).getChannelData(0);
let peak = 0, sum = 0;
for (const v of data) { peak = Math.max(peak, Math.abs(v)); sum += v * v; }
results[kind] = { peak, rms: Math.sqrt(sum / data.length) };
}
return results;
});
for (const [kind, { peak, rms }] of Object.entries(levels)) {
assert.ok(rms > 0.00001 && peak < 0.5, `${kind}: audio non valido ${JSON.stringify({ peak, rms })}`);
}
}
await page.reload();
await page.waitForFunction(() => window.ambience?.volume === 0.65);
await page.close();
}
console.log('Multimedia OK: torcia con mouse, tastiera e touch; uscita e pausa; audio sintetizzato e volume salvato.');
}