Files
leggenda-del-regolo/tests/music.test.mjs
T

107 lines
5.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { existsSync } from 'node:fs';
import { trackFor, Music } from '../src/engine/music.js';
import { story } from '../src/story/story.js';
test('il brano giusto per titolo, fine e scena', () => {
const s = { music: { title: 't.mp3', game: 'g.mp3', ending: 'e.mp3' }, scenes: { a: {}, b: { music: 'b.mp3' } } };
assert.equal(trackFor(s, { screen: 'splash', scene: 'a' }), null, 'sotto il cartello suona solo la sua musichetta');
assert.equal(trackFor(s, { screen: 'title', scene: 'a' }), 't.mp3');
assert.equal(trackFor(s, { screen: 'intro', scene: 'a' }), 't.mp3');
assert.equal(trackFor(s, { screen: 'ending', scene: 'a' }), 'e.mp3');
assert.equal(trackFor(s, { screen: 'game', scene: 'a' }), 'g.mp3');
assert.equal(trackFor(s, { screen: 'game', scene: 'b' }), 'b.mp3');
assert.equal(trackFor({ scenes: {} }, { screen: 'game', scene: 'x' }), null);
// Un tavolo da carte aperto ha la sua musica; un primo piano senza musica lascia quella della scena.
const tavoli = { ...s, closeups: { carte: { music: 'c.mp3' }, libro: {} } };
assert.equal(trackFor(tavoli, { screen: 'game', scene: 'b', closeup: 'carte' }), 'c.mp3');
assert.equal(trackFor(tavoli, { screen: 'game', scene: 'b', closeup: 'libro' }), 'b.mp3');
// La sera ha la sua musica all'aperto; la scena col suo brano e gli interni non cambiano.
const sera = { id: 'sera', music: 's.mp3' };
assert.equal(trackFor({ ...s, scenes: { ...s.scenes, i: { indoor: true } } }, { screen: 'game', scene: 'a', phase: sera }), 's.mp3');
assert.equal(trackFor(s, { screen: 'game', scene: 'b', phase: sera }), 'b.mp3');
assert.equal(trackFor({ ...s, scenes: { ...s.scenes, i: { indoor: true } } }, { screen: 'game', scene: 'i', phase: sera }), 'g.mp3');
});
test('volume e muto', () => {
const m = new Music({ volume: 0.5, muted: true });
assert.equal(m.setVolume(1.4), 1);
assert.equal(m.muted, false, 'alzare il volume toglie il muto');
assert.equal(m.toggle(), true);
assert.equal(m.setVolume(-1), 0);
assert.deepEqual(m.settings(), { volume: 0, muted: true });
});
test('i brani della storia esistono', () => {
const files = [...Object.values(story.music ?? {}), ...Object.values(story.scenes).map(s => s.music).filter(Boolean)];
assert.ok(files.length);
for (const f of files) assert.ok(existsSync(new URL('../' + f, import.meta.url)), `manca ${f}`);
});
test('mentre parla qualcuno la musica si abbassa, poi risale', () => {
globalThis.Audio ??= class { constructor(src) { this.src = src; this.volume = 0; this.paused = true; } play() { this.paused = false; return Promise.resolve(); } pause() { this.paused = true; } };
const m = new Music({ volume: 0.6 });
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
assert.equal(m.current.audio.volume, 0.6);
m.duck = 0.45;
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
assert.ok(Math.abs(m.current.audio.volume - 0.27) < 1e-9, 'abbassata sotto la voce');
m.duck = 1;
for (let i = 0; i < 40; i++) m.update('g.mp3', 0.1);
assert.equal(m.current.audio.volume, 0.6);
});
test('in pausa la musica sfuma fino a zero e si ferma', () => {
const played = [], paused = [];
globalThis.Audio = class { constructor(src) { this.src = src; this.volume = 0; this.paused = true; }
play() { this.paused = false; played.push(this.src); return Promise.resolve(); } pause() { this.paused = true; paused.push(this.src); } };
const m = new Music({ volume: 0.5 });
for (let i = 0; i < 40; i++) m.update('a.mp3', 0.1);
assert.equal(m.current.audio.volume, 0.5);
m.hold = true;
for (let i = 0; i < 40; i++) m.update('a.mp3', 0.1);
assert.ok(m.current.audio.paused && m.current.audio.volume === 0, 'ferma a volume zero');
m.hold = false;
m.update('a.mp3', 0.1);
assert.ok(!m.current.audio.paused && played.length === 2, 'riprende lo stesso brano');
delete globalThis.Audio;
});
test('passando a un\'altra app la musica si ferma subito, e tornando riparte', () => {
globalThis.Audio ??= class { constructor(src) { this.src = src; this.volume = 0; this.paused = true; } play() { this.paused = false; return Promise.resolve(); } pause() { this.paused = true; } };
const m = new Music({ volume: 0.6 });
for (let i = 0; i < 20; i++) m.update('a.mp3', 0.1);
m.update('b.mp3', 0.1); // a.mp3 sta ancora sfumando
const [old, now] = [m.old[0], m.current.audio];
assert.ok(!now.paused && !old.paused);
m.setAway(true);
assert.ok(now.paused && old.paused, 'ferma subito, senza aspettare i fotogrammi');
m.update('b.mp3', 0.1);
assert.ok(now.paused, 'finché la pagina è nascosta non riparte');
m.setAway(false);
m.update('b.mp3', 0.1);
assert.ok(!now.paused, 'tornati, riparte');
m.hold = true;
m.setAway(true); m.setAway(false);
m.update('b.mp3', 0.1);
assert.ok(now.paused, 'con la pausa aperta resta ferma');
});
test('su iPhone (volume di sola lettura, sempre 1) il brano vecchio si ferma lo stesso: niente organo sopra il paese', () => {
globalThis.Audio = class { constructor(src) { this.src = src; this.paused = true; this.muted = false; }
get volume() { return 1; } set volume(v) {}
play() { this.paused = false; return Promise.resolve(); } pause() { this.paused = true; } };
const m = new Music({ volume: 0.6 });
for (let i = 0; i < 20; i++) m.update('paese.mp3', 0.1);
const paese = m.current.audio;
for (let i = 0; i < 20; i++) m.update('pieve.mp3', 0.1);
assert.ok(paese.paused, 'il brano del paese, sfumato, si è fermato');
assert.deepEqual(m.old, []);
assert.ok(!m.current.audio.paused && !m.current.audio.muted, 'suona solo l\'organo');
m.muted = true;
for (let i = 0; i < 20; i++) m.update('pieve.mp3', 0.1);
assert.ok(m.current.audio.muted, 'il muto funziona anche dove il volume no');
delete globalThis.Audio;
});