97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
|
|
import {
|
|
clampFrame,
|
|
naturalSortFiles,
|
|
naturalSortBy,
|
|
IMAGE_QUAD_LABELS,
|
|
frameLabel,
|
|
} from './imageSequenceModel';
|
|
|
|
// A minimal File stand-in — we only ever read `.name`. Avoids relying on the DOM
|
|
// File constructor under the `node` vitest env.
|
|
function mkFile(name: string): File {
|
|
return { name } as File;
|
|
}
|
|
|
|
describe('clampFrame', () => {
|
|
it('returns an in-range index unchanged', () => {
|
|
expect(clampFrame(0, 10)).toBe(0);
|
|
expect(clampFrame(4, 10)).toBe(4);
|
|
expect(clampFrame(9, 10)).toBe(9);
|
|
});
|
|
|
|
it('clamps an over-range index down to count - 1', () => {
|
|
expect(clampFrame(15, 10)).toBe(9);
|
|
expect(clampFrame(10, 10)).toBe(9);
|
|
});
|
|
|
|
it('clamps a negative index up to 0', () => {
|
|
expect(clampFrame(-3, 10)).toBe(0);
|
|
});
|
|
|
|
it('returns 0 for a NaN / non-finite index', () => {
|
|
expect(clampFrame(NaN, 10)).toBe(0);
|
|
// Infinity is non-finite → 0 per the contract (not the max index).
|
|
expect(clampFrame(Infinity, 10)).toBe(0);
|
|
});
|
|
|
|
it('returns 0 for an empty sequence (count 0)', () => {
|
|
expect(clampFrame(0, 0)).toBe(0);
|
|
expect(clampFrame(3, 0)).toBe(0);
|
|
});
|
|
|
|
it('floors a fractional index', () => {
|
|
expect(clampFrame(2.9, 10)).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('naturalSortFiles', () => {
|
|
it('orders frame names numerically (frame2 < frame10)', () => {
|
|
const sorted = naturalSortFiles([
|
|
mkFile('f10.png'),
|
|
mkFile('f2.png'),
|
|
mkFile('f1.png'),
|
|
]);
|
|
expect(sorted.map((f) => f.name)).toEqual(['f1.png', 'f2.png', 'f10.png']);
|
|
});
|
|
|
|
it('returns a new array and does not mutate the input', () => {
|
|
const input = [mkFile('b.png'), mkFile('a.png')];
|
|
const sorted = naturalSortFiles(input);
|
|
expect(sorted).not.toBe(input);
|
|
expect(input.map((f) => f.name)).toEqual(['b.png', 'a.png']);
|
|
});
|
|
});
|
|
|
|
describe('naturalSortBy', () => {
|
|
it('sorts by a string key with numeric awareness', () => {
|
|
expect(naturalSortBy(['x10', 'x2', 'x1'], (s) => s)).toEqual(['x1', 'x2', 'x10']);
|
|
});
|
|
});
|
|
|
|
describe('IMAGE_QUAD_LABELS', () => {
|
|
it('carries the Vietnamese pane labels with diacritics', () => {
|
|
expect(IMAGE_QUAD_LABELS[0]).toBe('Ảnh gốc');
|
|
expect(IMAGE_QUAD_LABELS[1]).toBe('Bản đồ độ sâu');
|
|
expect(IMAGE_QUAD_LABELS[2]).toBe('Phủ phân vùng');
|
|
expect(IMAGE_QUAD_LABELS[3]).toBe('Tái tạo 3D');
|
|
});
|
|
});
|
|
|
|
describe('frameLabel', () => {
|
|
it('returns "khung 0/0" for an empty sequence', () => {
|
|
expect(frameLabel(0, 0)).toBe('khung 0/0');
|
|
});
|
|
|
|
it('formats a 1-based counter', () => {
|
|
expect(frameLabel(4, 10)).toBe('khung 5/10');
|
|
expect(frameLabel(0, 10)).toBe('khung 1/10');
|
|
});
|
|
|
|
it('clamps the index inside the counter', () => {
|
|
expect(frameLabel(99, 10)).toBe('khung 10/10');
|
|
expect(frameLabel(-5, 10)).toBe('khung 1/10');
|
|
});
|
|
});
|