157 lines
6.2 KiB
JavaScript
157 lines
6.2 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import { guideDisplayName, guideOverlayPosition, isTrackedGuideInputName, syncConnectedAudioGuides, syncConnectedGuides, syncConnectedImageGuides } from '../../web/ui/guide_sync.js';
|
|
|
|
function stateWithGuides(guides = []) {
|
|
return {
|
|
timeline: { frame_count: 124, selected_frame: 48 },
|
|
guides,
|
|
};
|
|
}
|
|
|
|
test('creates the first image guide at native H3 frame 5 by default', () => {
|
|
const state = stateWithGuides();
|
|
const changed = syncConnectedImageGuides(state, [0], () => 'guide-0');
|
|
|
|
assert.equal(changed, true);
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'guide-0', enabled: true, frame: 5, image_slot: 0, audio_slot: null,
|
|
}]);
|
|
});
|
|
|
|
test('keeps an existing image guide placement and does not duplicate it', () => {
|
|
const state = stateWithGuides([{ id: 'saved', enabled: true, frame: 12, image_slot: 0, audio_slot: null }]);
|
|
const changed = syncConnectedImageGuides(state, [0], () => 'unused');
|
|
|
|
assert.equal(changed, false);
|
|
assert.equal(state.guides.length, 1);
|
|
assert.equal(state.guides[0].frame, 12);
|
|
});
|
|
|
|
test('removes guides for disconnected image slots and leaves unrelated audio guides', () => {
|
|
const state = stateWithGuides([
|
|
{ id: 'image', enabled: true, frame: 12, image_slot: 0, audio_slot: null },
|
|
{ id: 'audio', enabled: true, frame: 24, image_slot: null, audio_slot: 0 },
|
|
]);
|
|
const changed = syncConnectedImageGuides(state, [], () => 'unused');
|
|
|
|
assert.equal(changed, true);
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'audio', enabled: true, frame: 24, image_slot: null, audio_slot: 0,
|
|
}]);
|
|
});
|
|
|
|
test('reconnected slots create a fresh guide at the first free H3 frame', () => {
|
|
const state = stateWithGuides([{ id: 'audio', enabled: true, frame: 24, image_slot: null, audio_slot: 0 }]);
|
|
const changed = syncConnectedImageGuides(state, [2], () => 'guide-2');
|
|
|
|
assert.equal(changed, true);
|
|
assert.equal(state.guides.at(-1).image_slot, 2);
|
|
assert.equal(state.guides.at(-1).frame, 5);
|
|
});
|
|
|
|
test('new guides advance through the native H3 frame grid', () => {
|
|
const state = stateWithGuides([{ id: 'saved', enabled: true, frame: 5, image_slot: 0, audio_slot: null }]);
|
|
syncConnectedImageGuides(state, [1], () => 'guide-1');
|
|
assert.equal(state.guides.at(-1).frame, 22);
|
|
});
|
|
|
|
test('displays guide socket numbers instead of internal ids', () => {
|
|
assert.equal(guideDisplayName({ id: 'guide-cb39813f', image_slot: 0, audio_slot: null }), '0');
|
|
assert.equal(guideDisplayName({ id: 'guide-ec3ce479', image_slot: 3, audio_slot: null }), '3');
|
|
assert.equal(guideDisplayName({ id: 'audio-only', image_slot: null, audio_slot: 2 }), '2');
|
|
});
|
|
|
|
test('keeps multiple guide overlays visible when the filmstrip is condensed', () => {
|
|
assert.deepEqual(guideOverlayPosition(3), { normal: '31px', compact: '19px' });
|
|
});
|
|
|
|
|
|
test('creates one audio-only guide for a connected audio slot', () => {
|
|
const state = stateWithGuides();
|
|
const changed = syncConnectedAudioGuides(state, [0], () => 'audio-guide-0');
|
|
assert.equal(changed, true);
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'audio-guide-0', enabled: true, frame: 5, image_slot: null, audio_slot: 0,
|
|
}]);
|
|
});
|
|
|
|
test('clears a disconnected audio slot but preserves the visual part of an AV guide', () => {
|
|
const state = stateWithGuides([{
|
|
id: 'av', enabled: true, frame: 12, image_slot: 0, audio_slot: 0,
|
|
}]);
|
|
const changed = syncConnectedAudioGuides(state, [], () => 'unused');
|
|
assert.equal(changed, true);
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'av', enabled: true, frame: 12, image_slot: 0, audio_slot: null,
|
|
}]);
|
|
});
|
|
|
|
test('removes a disconnected audio-only guide and does not duplicate connected audio slots', () => {
|
|
const state = stateWithGuides([
|
|
{ id: 'audio0', enabled: true, frame: 12, image_slot: null, audio_slot: 0 },
|
|
{ id: 'audio1', enabled: true, frame: 24, image_slot: null, audio_slot: 1 },
|
|
]);
|
|
const changed = syncConnectedAudioGuides(state, [1], () => 'unused');
|
|
assert.equal(changed, true);
|
|
assert.equal(state.guides.length, 1);
|
|
assert.equal(state.guides[0].audio_slot, 1);
|
|
});
|
|
|
|
|
|
test('preview refresh tracking includes audio guide sockets', () => {
|
|
assert.equal(isTrackedGuideInputName('guide_audios.guide_audio_0'), true);
|
|
assert.equal(isTrackedGuideInputName('guide_images.guide_image_0'), true);
|
|
assert.equal(isTrackedGuideInputName('reference_video'), false);
|
|
assert.equal(isTrackedGuideInputName('reference_media'), true);
|
|
assert.equal(isTrackedGuideInputName('audio_reference'), true);
|
|
assert.equal(isTrackedGuideInputName('first_frame'), true);
|
|
assert.equal(isTrackedGuideInputName('last_frame'), true);
|
|
assert.equal(isTrackedGuideInputName('positive'), false);
|
|
});
|
|
|
|
|
|
test('combined media sync creates both newly connected image and audio guides', () => {
|
|
const state = stateWithGuides();
|
|
const changed = syncConnectedGuides(
|
|
state,
|
|
[0],
|
|
[2],
|
|
(slot) => `guide-${slot}`,
|
|
(slot) => `audio-guide-${slot}`,
|
|
);
|
|
|
|
assert.equal(changed, true);
|
|
assert.deepEqual(state.guides, [
|
|
{ id: 'guide-0', enabled: true, frame: 5, image_slot: 0, audio_slot: null },
|
|
{ id: 'audio-guide-2', enabled: true, frame: 22, image_slot: null, audio_slot: 2 },
|
|
]);
|
|
});
|
|
|
|
test('merges matching image and audio slots into one AV guide', () => {
|
|
const state = stateWithGuides();
|
|
syncConnectedGuides(state, [0], [0], (slot) => `guide-${slot}`, (slot) => `audio-guide-${slot}`);
|
|
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'guide-0', enabled: true, frame: 5, image_slot: 0, audio_slot: 0,
|
|
}]);
|
|
});
|
|
|
|
test('merges matching audio and image slots regardless of connection order', () => {
|
|
const state = stateWithGuides();
|
|
syncConnectedGuides(state, [], [0], (slot) => `guide-${slot}`, (slot) => `audio-guide-${slot}`);
|
|
syncConnectedGuides(state, [0], [0], (slot) => `guide-${slot}`, (slot) => `audio-guide-${slot}`);
|
|
|
|
assert.deepEqual(state.guides, [{
|
|
id: 'audio-guide-0', enabled: true, frame: 5, image_slot: 0, audio_slot: 0,
|
|
}]);
|
|
});
|
|
|
|
test('uses the selected frame when H3 snapping is disabled', () => {
|
|
const state = stateWithGuides();
|
|
syncConnectedGuides(state, [0], [2], (slot) => `guide-${slot}`, (slot) => `audio-guide-${slot}`, 'frame');
|
|
|
|
assert.deepEqual(state.guides.map((guide) => guide.frame), [48, 48]);
|
|
});
|