feat: persist track mute/solo status

This commit is contained in:
Xiaohan-Tian
2026-05-17 13:06:47 -07:00
parent cbd66ab4f6
commit 9c197ecd08
13 changed files with 285 additions and 21 deletions
@@ -0,0 +1,87 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { KGCore } from '../../KGCore';
import { KGProject } from '../../KGProject';
import { KGAudioInterface } from '../../audio-interface/KGAudioInterface';
import { KGTrack } from '../../track/KGTrack';
import { UpdateTrackCommand } from './UpdateTrackCommand';
vi.mock('../../KGCore', () => ({
KGCore: {
instance: vi.fn(),
},
}));
vi.mock('../../audio-interface/KGAudioInterface', () => ({
KGAudioInterface: {
instance: vi.fn(),
},
}));
describe('UpdateTrackCommand', () => {
let track: KGTrack;
let project: KGProject;
const mockCore = {
getCurrentProject: vi.fn(),
};
const mockAudioInterface = {
setTrackVolume: vi.fn(),
setTrackInstrument: vi.fn(),
setTrackMute: vi.fn(),
setTrackSolo: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
track = new KGTrack('Track 1', 1);
project = new KGProject('Test', 32, 0, 125, undefined, undefined, undefined, undefined, undefined, 1, [track], 11);
mockCore.getCurrentProject.mockReturnValue(project);
vi.mocked(KGCore.instance).mockReturnValue(mockCore as unknown as KGCore);
vi.mocked(KGAudioInterface.instance).mockReturnValue(mockAudioInterface as unknown as KGAudioInterface);
});
it('updates muted state and propagates to the audio interface', () => {
const command = new UpdateTrackCommand(1, { muted: true });
command.execute();
expect(track.getMuted()).toBe(true);
expect(mockAudioInterface.setTrackMute).toHaveBeenCalledWith('1', true);
expect(command.getChangedProperties()).toEqual(new Set(['muted']));
});
it('updates solo state and propagates to the audio interface', () => {
const command = new UpdateTrackCommand(1, { solo: true });
command.execute();
expect(track.getSolo()).toBe(true);
expect(mockAudioInterface.setTrackSolo).toHaveBeenCalledWith('1', true);
expect(command.getChangedProperties()).toEqual(new Set(['solo']));
});
it('restores muted and solo state on undo', () => {
track.setMuted(true);
track.setSolo(true);
const command = new UpdateTrackCommand(1, { muted: false, solo: false });
command.execute();
command.undo();
expect(track.getMuted()).toBe(true);
expect(track.getSolo()).toBe(true);
expect(mockAudioInterface.setTrackMute).toHaveBeenLastCalledWith('1', true);
expect(mockAudioInterface.setTrackSolo).toHaveBeenLastCalledWith('1', true);
});
it('treats unchanged mute and solo values as no-ops', () => {
const command = new UpdateTrackCommand(1, { muted: false, solo: false });
command.execute();
expect(track.getMuted()).toBe(false);
expect(track.getSolo()).toBe(false);
expect(mockAudioInterface.setTrackMute).not.toHaveBeenCalled();
expect(mockAudioInterface.setTrackSolo).not.toHaveBeenCalled();
expect(command.getChangedProperties()).toEqual(new Set());
});
});