import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; const here = path.dirname(fileURLToPath(import.meta.url)); /** Регресс: RAF в ControlApp бампил оба audio-tick на каждом кадре → полный ре-рендер пульта. */ void test('ControlApp: нет per-frame RAF setState для аудио scrub', () => { const app = fs.readFileSync(path.join(here, 'ControlApp.tsx'), 'utf8'); const card = fs.readFileSync(path.join(here, 'ControlAudioCard.tsx'), 'utf8'); assert.doesNotMatch(app, /\banyPlaying\b/); // Старый паттерн: RAF tick → оба set*AudioStateTick. assert.doesNotMatch( app, /const tick = \(\) => \{\s*setSceneAudioStateTick/, 'корневой RAF-тик аудио удалён', ); assert.doesNotMatch(app, /requestAnimationFrame\s*\(\s*tick\s*\)/); assert.ok(app.includes('ControlAudioCard')); assert.ok(card.includes('requestAnimationFrame'), 'scrub крутится локально в карточке'); assert.ok(card.includes('scrubFillRef'), 'прогресс пишется в DOM, не через setState корня'); assert.ok(card.includes('setGainUi'), 'громкость обновляет только карточку'); });