Files
battleship/test/web/test_game_sounds.js

64 lines
3.7 KiB
JavaScript

const assert = require('node:assert/strict');
const test = require('node:test');
const { createSoundDirector, COUNTS, CATALOG } = require('../../data/game_sounds.js');
function recorder() { const played = []; return { played, playPreset: preset => { played.push(preset); return true; } }; }
test('every required event family has its planned minimum of original synthesis presets', () => {
Object.entries(COUNTS).forEach(([family, count]) => {
assert.equal(CATALOG[family].length, count);
assert.equal(new Set(CATALOG[family].map(preset => preset.id)).size, count);
});
});
test('selection is randomized without immediate repeats and keeps bounded history', () => {
const output = recorder(); let sample = 0;
const director = createSoundDirector({ engine: output, random: () => (sample++ % 11) / 11, historyLimit: 3 });
const selected = [];
for (let index = 0; index < 30; index += 1) selected.push(director.play('shot').preset.id);
selected.slice(1).forEach((id, index) => assert.notEqual(id, selected[index]));
assert.ok(director.getHistory('shot').length <= 3);
});
test('preset parameter ranges remain safe through many randomized selections', () => {
const output = recorder(); const director = createSoundDirector({ engine: output, random: Math.random });
for (let index = 0; index < 3000; index += 1) director.play(Object.keys(COUNTS)[index % Object.keys(COUNTS).length]);
output.played.forEach(preset => {
assert.ok(preset.frequency >= 100 && preset.frequency <= 1000);
assert.ok(preset.endFrequency >= 100 && preset.endFrequency <= 1000);
assert.ok(preset.durationMs >= 100 && preset.durationMs <= 900);
assert.ok(preset.filterHz >= 900 && preset.filterHz <= 3500);
assert.ok(Math.abs(preset.pan) <= 0.4 && preset.gain <= 0.75);
});
});
test('state versions, perspectives, priority, waiting rate limit, and reconnect summaries are safe', () => {
const output = recorder(); let clock = 0;
const director = createSoundDirector({ engine: output, random: () => 0, now: () => clock, waitingIntervalMs: 12000 });
assert.ok(director.getPriority('victory') > director.getPriority('sunk'));
assert.ok(director.getPriority('sunk') > director.getPriority('hit'));
assert.ok(director.getPriority('hit') > director.getPriority('miss'));
assert.equal(director.play('watch-hit', { version: 4 }).preset.family, 'hit');
assert.equal(director.play('damage', { version: 4 }), false);
assert.equal(director.play('watch-sunk', { version: 5 }).preset.family, 'sunk');
assert.equal(director.play('waiting').preset.family, 'waiting');
assert.equal(director.play('waiting'), false);
clock = 12001;
assert.equal(director.play('waiting').preset.family, 'waiting');
assert.equal(director.play('summary', { version: 6, summary: true }).preset.family, 'victory');
});
test('combos escalate, reduced intensity suppresses extras, and recovery remains calm', () => {
const output = recorder(); const director = createSoundDirector({ engine: output, random: () => 0.3 });
assert.equal(director.play('hit', { combo: 2 }).combo, 'combo');
assert.equal(director.play('hit', { combo: 3 }).combo, 'mega');
assert.equal(director.play('sunk', { combo: 4 }).combo, 'ultra');
const count = output.played.length;
assert.equal(director.play('hit', { combo: 4, reduced: true }).combo, 'ultra');
assert.equal(output.played.length, count + 1);
assert.equal(director.play('waiting', { reduced: true }), false);
const recovery = ['recovery-open', 'recovery-cancel', 'recovery-leave', 'recovery-profile', 'recovery-game'].map(event => director.play(event).preset);
assert.equal(new Set(recovery.map(preset => preset.id)).size, 5);
recovery.forEach(preset => assert.ok(preset.family === 'recovery' && preset.gain <= 0.16 && preset.durationMs <= 300));
});