mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-19 06:40:38 -07:00
test(app): add bun test infra and first unit tests
bun test as the runner — already the toolchain, zero new deps beyond @types/bun. 54 tests across 5 files covering the already-pure logic: FastAPI error normalization (extracted verbatim to lib/api/errors.ts), clip trim clamping (extracted from StoryTrackEditor to lib/utils/trim.ts, magic 100 now MIN_CLIP_DURATION_MS), duration/size/engine formatters, engine-language map consistency, and changelog parsing. Wired into CI after typecheck. Known issues surfaced by the tests, left as-is for now: parseChangelog's heading regex uses \s* which matches newlines, so a dateless heading followed directly by a bullet swallows that bullet; formatFileSize has no TB unit; ENGINE_DISPLAY_NAMES lacks tada/kokoro.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import {
|
||||
ALL_LANGUAGES,
|
||||
ENGINE_LANGUAGES,
|
||||
LANGUAGE_CODES,
|
||||
LANGUAGE_OPTIONS,
|
||||
getLanguageOptionsForEngine,
|
||||
} from './languages';
|
||||
|
||||
describe('ENGINE_LANGUAGES', () => {
|
||||
test('every engine maps only to codes defined in ALL_LANGUAGES', () => {
|
||||
for (const [engine, codes] of Object.entries(ENGINE_LANGUAGES)) {
|
||||
for (const code of codes) {
|
||||
expect(ALL_LANGUAGES[code], `${engine} references unknown code "${code}"`).toBeDefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('no engine lists a language twice', () => {
|
||||
for (const [engine, codes] of Object.entries(ENGINE_LANGUAGES)) {
|
||||
expect(new Set(codes).size, `${engine} has duplicate codes`).toBe(codes.length);
|
||||
}
|
||||
});
|
||||
|
||||
test('every engine supports at least English', () => {
|
||||
for (const codes of Object.values(ENGINE_LANGUAGES)) {
|
||||
expect(codes).toContain('en');
|
||||
}
|
||||
});
|
||||
|
||||
test('English-only engines list exactly one language', () => {
|
||||
expect(ENGINE_LANGUAGES.luxtts).toEqual(['en']);
|
||||
expect(ENGINE_LANGUAGES.chatterbox_turbo).toEqual(['en']);
|
||||
});
|
||||
|
||||
test('qwen and qwen_custom_voice support the same languages', () => {
|
||||
expect(ENGINE_LANGUAGES.qwen_custom_voice).toEqual(ENGINE_LANGUAGES.qwen);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLanguageOptionsForEngine', () => {
|
||||
test('builds value/label pairs from ALL_LANGUAGES', () => {
|
||||
expect(getLanguageOptionsForEngine('luxtts')).toEqual([{ value: 'en', label: 'English' }]);
|
||||
});
|
||||
|
||||
test('preserves the engine declaration order', () => {
|
||||
const values = getLanguageOptionsForEngine('qwen').map((o) => o.value);
|
||||
expect(values).toEqual([...ENGINE_LANGUAGES.qwen]);
|
||||
});
|
||||
|
||||
test('falls back to qwen languages for unknown engines', () => {
|
||||
expect(getLanguageOptionsForEngine('does-not-exist')).toEqual(
|
||||
getLanguageOptionsForEngine('qwen'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('language option exports', () => {
|
||||
test('LANGUAGE_CODES covers every ALL_LANGUAGES key exactly once', () => {
|
||||
const codes: string[] = [...LANGUAGE_CODES].sort();
|
||||
expect(codes).toEqual(Object.keys(ALL_LANGUAGES).sort());
|
||||
expect(new Set(LANGUAGE_CODES).size).toBe(LANGUAGE_CODES.length);
|
||||
});
|
||||
|
||||
test('LANGUAGE_OPTIONS labels match ALL_LANGUAGES', () => {
|
||||
for (const option of LANGUAGE_OPTIONS) {
|
||||
expect(option.label).toBe(ALL_LANGUAGES[option.value]);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user