Files
tts-workshop/packages/bgm/src/variants.test.ts
T
hypercross c0967ab71f feat(bgm): add board game manifest loader
Parse yaml/json/toml and markdown code blocks into packages, expanding
$variants via typed-csv and collecting parts, surfaces, and setups by
include patterns. Ships zod validation, a vitest config, and 16 tests.
2026-08-09 18:59:14 +08:00

75 lines
2.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { parseCsvData, expandVariants } from './variants.js';
import type { DefFile } from './types.js';
function defFile(name: string, text: string): DefFile {
return { name, text, source: name, kind: 'csv' };
}
describe('parseCsvData', () => {
it('parses the spec example with an empty array', () => {
const csv = [
'name,parents',
'string,string[]',
'clark,[jonathan;martha]',
'bruce,[]',
].join('\n');
const { rows, header } = parseCsvData(csv, 'test');
expect(header).toEqual(['name', 'parents']);
expect(rows).toEqual([
{ name: 'clark', parents: ['jonathan', 'martha'] },
{ name: 'bruce', parents: [] },
]);
});
it('parses a crop tuple', () => {
const csv = [
'id,faceCrop',
'string,[number;number;number;number]',
'fish,[0;0;5;2]',
'grain,[1;0;5;2]',
].join('\n');
const { rows } = parseCsvData(csv, 'test');
expect(rows).toEqual([
{ id: 'fish', faceCrop: [0, 0, 5, 2] },
{ id: 'grain', faceCrop: [1, 0, 5, 2] },
]);
});
it('throws a BgmError on a type mismatch', () => {
const csv = ['n', 'number', 'not-a-number'].join('\n');
expect(() => parseCsvData(csv, 'test')).toThrow(/Invalid CSV/);
});
it('allows a header and schema with no data rows', () => {
const { rows } = parseCsvData('a\nstring', 'test');
expect(rows).toEqual([]);
});
});
describe('expandVariants', () => {
it('parses inline CSV when the value contains a newline', () => {
const rows = expandVariants('a,b\nstring,number\nx,1', 'pkg/def.yaml', new Map(), 'src');
expect(rows).toEqual([{ a: 'x', b: 1 }]);
});
it('resolves a path against the def file directory', () => {
const defs = new Map<string, DefFile[]>([
['pkg/parts/seats.csv', [defFile('pkg/parts/seats.csv', 'seat\nnumber\n0\n1')]],
]);
const rows = expandVariants('./seats.csv', 'pkg/parts/board.yaml', defs, 'src');
expect(rows).toEqual([{ seat: 0 }, { seat: 1 }]);
});
it('throws when the referenced csv is missing', () => {
expect(() => expandVariants('./nope.csv', 'pkg/def.yaml', new Map(), 'src')).toThrow(
/CSV not found/,
);
});
it('throws when $variants is not a string', () => {
expect(() => expandVariants(42, 'pkg/def.yaml', new Map(), 'src')).toThrow(
/must be a path or inline CSV/,
);
});
});