81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { get } from 'svelte/store';
|
|
// Import the store module directly: the feature barrel re-exports .svelte
|
|
// components, which the unit runner (no Svelte plugin) cannot transform.
|
|
import { timelineStore, positionAt } from '$features/timeline/store';
|
|
|
|
beforeEach(() => {
|
|
timelineStore.pause();
|
|
timelineStore.setRange(0, 0);
|
|
timelineStore.setSpeed(1);
|
|
timelineStore.setMarkers([]);
|
|
});
|
|
|
|
describe('timelineStore', () => {
|
|
it('clamps seek to the current range', () => {
|
|
timelineStore.setRange(0, 10_000);
|
|
timelineStore.seek(99_999);
|
|
expect(get(timelineStore).time).toBe(10_000);
|
|
timelineStore.seek(-5);
|
|
expect(get(timelineStore).time).toBe(0);
|
|
});
|
|
|
|
it('clamps the current time when the range shrinks', () => {
|
|
timelineStore.setRange(0, 10_000);
|
|
timelineStore.seek(9_000);
|
|
timelineStore.setRange(0, 4_000);
|
|
expect(get(timelineStore).time).toBe(4_000);
|
|
});
|
|
|
|
it('ignores play when there is no duration', () => {
|
|
timelineStore.play();
|
|
expect(get(timelineStore).playing).toBe(false);
|
|
});
|
|
|
|
it('plays and pauses when a duration exists', () => {
|
|
timelineStore.setRange(0, 10_000);
|
|
timelineStore.play();
|
|
expect(get(timelineStore).playing).toBe(true);
|
|
timelineStore.pause();
|
|
expect(get(timelineStore).playing).toBe(false);
|
|
});
|
|
|
|
it('rewinds to min on reset', () => {
|
|
timelineStore.setRange(0, 10_000);
|
|
timelineStore.seek(7_000);
|
|
timelineStore.reset();
|
|
expect(get(timelineStore).time).toBe(0);
|
|
expect(get(timelineStore).playing).toBe(false);
|
|
});
|
|
|
|
it('stores the playback speed', () => {
|
|
timelineStore.setSpeed(5);
|
|
expect(get(timelineStore).speed).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('positionAt', () => {
|
|
const path: [number, number, ...number[]][] = [
|
|
[60, 100],
|
|
[62, 102],
|
|
[64, 104]
|
|
];
|
|
|
|
it('returns the first point at t=0 and the last at t=duration', () => {
|
|
expect(positionAt(path, 0, 1000)).toEqual([60, 100]);
|
|
expect(positionAt(path, 1000, 1000)).toEqual([64, 104]);
|
|
});
|
|
|
|
it('interpolates linearly between samples', () => {
|
|
// Quarter of the way = halfway along the first segment.
|
|
const p = positionAt(path, 250, 1000)!;
|
|
expect(p[0]).toBeCloseTo(61, 6);
|
|
expect(p[1]).toBeCloseTo(101, 6);
|
|
});
|
|
|
|
it('clamps outside the flight window and handles an empty path', () => {
|
|
expect(positionAt(path, -100, 1000)).toEqual([60, 100]);
|
|
expect(positionAt(path, 99_999, 1000)).toEqual([64, 104]);
|
|
expect(positionAt([], 0, 1000)).toBeNull();
|
|
});
|
|
});
|