From aecf040b87b2a2e395af551626bfe6f1f0551f3e Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Mon, 13 Jul 2026 05:28:44 +0200 Subject: [PATCH] feat: Add comprehensive unit tests for Ableton automation export logic --- tests/ableton-automation.test.ts | 79 ++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/ableton-automation.test.ts diff --git a/tests/ableton-automation.test.ts b/tests/ableton-automation.test.ts new file mode 100644 index 0000000..ebf299d --- /dev/null +++ b/tests/ableton-automation.test.ts @@ -0,0 +1,79 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { FaderParam } from '../src/types/types'; +import { + buildArrangementEnvelopes, + buildClipEnvelopes, + getFaderValueAt, +} from '../src/lib/exporters/ableton/automation'; +import { AblGroupPatternOccurrence, AblTrack } from '../src/lib/transformers/ableton'; + +const faderParams = { + [FaderParam.LVL]: 1, + [FaderParam.PTC]: 0.5, + [FaderParam.TIM]: 0.5, + [FaderParam.LPF]: 1, + [FaderParam.HPF]: 0, + [FaderParam.FX]: 0, + [FaderParam.ATK]: 0, + [FaderParam.REL]: 0.25, + [FaderParam.PAN]: 0.5, + [FaderParam.TUNE]: 0.5, + [FaderParam.VEL]: -1, + [FaderParam.MOD]: 0, +}; + +const occurrence: AblGroupPatternOccurrence = { + group: 'a', + bars: 1, + sceneBars: 2, + sceneIndex: 0, + sceneName: '01', + offset: 0, + timeSignature: { numerator: 4, denominator: 4 }, + faderParams, + faderAutomation: [ + { parameter: FaderParam.REL, position: 96, value: 0.75, flags: 0 }, + { parameter: FaderParam.VEL, position: 192, value: 0.5, flags: 0 }, + ], +}; + +const track = { + volume: 0.5, + soundLength: 2, +} as AblTrack; + +test('uses original note velocity until a VEL point is reached', () => { + assert.equal(getFaderValueAt(occurrence, FaderParam.VEL, 96), undefined); + assert.equal(getFaderValueAt(occurrence, FaderParam.VEL, 192), 0.5); +}); + +test('tiles arrangement automation across the scene length', () => { + const envelopes = buildArrangementEnvelopes( + [occurrence], + { [FaderParam.REL]: [{ arrangement: 22000 }] }, + track, + false, + ); + const events = envelopes[0].Automation.Events.FloatEvent; + + assert.equal(envelopes[0].EnvelopeTarget.PointeeId['@Value'], 22000); + assert.equal(events[0]['@Time'], -63072000); + assert.deepEqual( + events.filter((event: any) => event['@Time'] === 1 || event['@Time'] === 5).map((event: any) => event['@Time']), + [1, 5], + ); +}); + +test('uses absolute automation targets for Session clip envelopes', () => { + const envelopes = buildClipEnvelopes( + occurrence, + { [FaderParam.REL]: [{ session: 23000 }] }, + track, + false, + ); + + assert.equal(envelopes[0].EnvelopeTarget.PointeeId['@Value'], 23000); + assert.equal(envelopes[0].Automation.Events.FloatEvent[2]['@Time'], 1); + assert.deepEqual(envelopes[0].LoopSlot, { Value: {} }); +});