sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user