const { test } = require('node:test'); const assert = require('node:assert/strict'); const { readFileSync } = require('node:fs'); const { join } = require('node:path'); const vm = require('node:vm'); const app = readFileSync(join(__dirname, 'app.js'), 'utf8'); const views = readFileSync(join(__dirname, 'views-core.js'), 'utf8'); function parsePreset(text) { const declaration = views.match(/function parseJamesDspPreset\(text\) \{[\s\S]*?\n\}/)?.[0]; assert.ok(declaration, 'preset parser is present'); const context = vm.createContext({}); vm.runInContext(`${declaration}; globalThis.parse = parseJamesDspPreset;`, context); return context.parse(text); } test('the persisted engine is Standard by default and upgrades missing JamesDSP settings safely', () => { assert.match(app, /jamesDsp: \{ enabled: false, downloadForOffline: false, preset: 'flat', params: \{\}, irRef: null \}/); assert.match(app, /downloadForOffline: saved\.downloadForOffline === true/); assert.match(app, /enabled: saved\.enabled === true/); assert.match(app, /preset: typeof saved\.preset === 'string' \? saved\.preset : 'flat'/); assert.match(app, /settings: normalizeJamesDspSettings\(\{ \.\.\.DEFAULT_SETTINGS, \.\.\.\(loaded\.settings \|\| \{\}\) \}\)/); assert.match(app, /data\.settings = normalizeJamesDspSettings\(\{ \.\.\.DEFAULT_SETTINGS, \.\.\.payload\.settings \}\)/); }); test('live JamesDSP shares the existing iOS gate and never stacks over EqRender copies', () => { assert.match(app, /const available = \(\) => !!AC && \(!isIOS \|\| data\.settings\.liveAudioIOS === true\)/); assert.match(app, /if \(data\.settings\?\.jamesDsp\?\.enabled\) return null/); assert.match(app, /const active = \(\) => EQ\.isIOS\(\) && WEB && !data\.settings\?\.jamesDsp\?\.enabled/); assert.match(app, /if \(rendered\(\)\) throw new Error\('This song is already playing a Standard processed copy\./); }); test('EQ creates no second context or element source and passes Standard fallback routing to the controller', () => { assert.equal((app.match(/\.createMediaElementSource\(/g) || []).length, 0); assert.match(app, /st\.ctx = AudioGraph\.context\(\)/); assert.match(app, /AudioGraph\.connect\(el, st\.filters\[0\]\)/); assert.match(app, /standardInput: st\.filters\[0\]/); assert.match(app, /dspController\.setLevelDb\(st\.levelDb\)/); assert.match(app, /if \(data\.settings\.jamesDsp\?\.enabled\) \{[\s\S]{0,450}startDsp\(\)/); assert.match(app, /request !== dspRequest \|\| !data\.settings\.jamesDsp\?\.enabled/); }); test('Playback settings expose persisted presets, bands, safe import/export, IR, and A/B bypass', () => { assert.match(views, /sel\('setSoundEngine'/); assert.match(views, /sel\('setDspPreset'/); for (const id of ['setDspCompressor', 'dspPresetImportBtn', 'dspPresetExportBtn', 'dspResetBtn', 'dspIrFile']) assert.ok(views.includes(`id="${id}"`), id); assert.match(views, /\[25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, 16000\]/); assert.match(views, /imported\?\.texts/); assert.doesNotMatch(views, /liveprog|ddc|arbitrary/); assert.match(views, /navigator\.hardwareConcurrency/); assert.match(views, /Long impulse responses are disabled/); assert.doesNotMatch(views, /44\.1 or 48 kHz/); assert.match(views, /setDspSpatialMode/); assert.match(views, /data-dsp-param="convolver\.predelayMs"/); assert.match(views, /data-dsp-param="convolver\.mix"/); assert.match(app, /Bypass JamesDSP/); assert.match(app, /toggleDspBypass/); }); test('preset import validates ranges and excludes text-effect parameters', () => { assert.deepEqual(JSON.parse(JSON.stringify(parsePreset(JSON.stringify({ version: 1, preset: 'warm', params: { 'eq.band0': 3, 'bass.enabled': 1 } })))), { preset: 'warm', params: { 'eq.band0': 3, 'bass.enabled': 1 } }); assert.throws(() => parsePreset({ version: 1, preset: 'flat', params: { 'liveprog.enabled': 1 } }), /text effects are disabled|Unsupported preset setting/); assert.throws(() => parsePreset({ version: 1, params: { 'eq.band0': 50 } }), /Unsupported preset setting/); assert.throws(() => parsePreset({ version: 1, texts: { effect: 'unsafe' }, params: {} }), /text effects are disabled/); });