diagrams-fable-tauri/tests/properties.test.ts
2026-07-02 23:22:21 +02:00

108 lines
4.3 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
validateValue,
defaultValues,
groupDefs,
type PropertyDef,
} from '../src/model/properties';
const def = (partial: Partial<PropertyDef> & Pick<PropertyDef, 'type'>): PropertyDef => ({
key: 'p',
label: 'Prop',
group: 'general',
...partial,
});
describe('property validation', () => {
it('accepts any string and coerces null to empty', () => {
expect(validateValue(def({ type: 'string' }), 'hello')).toEqual({ ok: true, value: 'hello' });
expect(validateValue(def({ type: 'string' }), null).value).toBe('');
});
it('validates int: coercion, rounding, bounds', () => {
expect(validateValue(def({ type: 'int' }), '42').value).toBe(42);
expect(validateValue(def({ type: 'int' }), 3.7).value).toBe(4);
expect(validateValue(def({ type: 'int', min: 0 }), -1).ok).toBe(false);
expect(validateValue(def({ type: 'int', max: 10 }), 11).ok).toBe(false);
expect(validateValue(def({ type: 'int' }), 'abc').ok).toBe(false);
});
it('validates float with bounds', () => {
expect(validateValue(def({ type: 'float' }), '3.14').value).toBeCloseTo(3.14);
expect(validateValue(def({ type: 'float', min: 0, max: 100 }), 50).ok).toBe(true);
expect(validateValue(def({ type: 'float', min: 0 }), '-0.5').ok).toBe(false);
expect(validateValue(def({ type: 'float' }), Infinity).ok).toBe(false);
});
it('validates enum against options', () => {
const e = def({ type: 'enum', options: [{ value: 'a', label: 'A' }, { value: 'b', label: 'B' }] });
expect(validateValue(e, 'a').ok).toBe(true);
expect(validateValue(e, 'c').ok).toBe(false);
});
it('validates color as hex', () => {
expect(validateValue(def({ type: 'color' }), '#ff0000').ok).toBe(true);
expect(validateValue(def({ type: 'color' }), '#f00').ok).toBe(true);
expect(validateValue(def({ type: 'color' }), 'red').ok).toBe(false);
expect(validateValue(def({ type: 'color' }), '#zzz').ok).toBe(false);
});
it('handles catalogueItem: nullable single id', () => {
const c = def({ type: 'catalogueItem', catalogueId: 'materials' });
expect(validateValue(c, 'steel').value).toBe('steel');
expect(validateValue(c, null).value).toBeNull();
expect(validateValue(c, '').value).toBeNull();
});
it('handles catalogueItems: array of ids', () => {
const c = def({ type: 'catalogueItems', catalogueId: 'insulation' });
expect(validateValue(c, ['a', 'b']).value).toEqual(['a', 'b']);
expect(validateValue(c, null).value).toEqual([]);
expect(validateValue(c, 'single').value).toEqual(['single']);
});
it('validates listOfValues with numeric item types', () => {
const l = def({ type: 'listOfValues', itemType: 'float' });
expect(validateValue(l, ['1.5', '2'] as never).value).toEqual([1.5, 2]);
expect(validateValue(l, ['x'] as never).ok).toBe(false);
const ls = def({ type: 'listOfValues', itemType: 'string' });
expect(validateValue(ls, ['a', 1] as never).value).toEqual(['a', '1']);
});
});
describe('defaults and grouping', () => {
it('produces sensible defaults per type', () => {
const defs: PropertyDef[] = [
def({ key: 's', type: 'string' }),
def({ key: 'i', type: 'int', min: 5 }),
def({ key: 'e', type: 'enum', options: [{ value: 'x', label: 'X' }] }),
def({ key: 'c', type: 'color' }),
def({ key: 'ci', type: 'catalogueItem' }),
def({ key: 'cis', type: 'catalogueItems' }),
def({ key: 'l', type: 'listOfValues' }),
def({ key: 'd', type: 'float', default: 9.5 }),
];
const v = defaultValues(defs);
expect(v.s).toBe('');
expect(v.i).toBe(5);
expect(v.e).toBe('x');
expect(v.c).toBe('#000000');
expect(v.ci).toBeNull();
expect(v.cis).toEqual([]);
expect(v.l).toEqual([]);
expect(v.d).toBe(9.5);
});
it('groups defs in canonical order', () => {
const defs: PropertyDef[] = [
def({ key: 'a', group: 'physics' }),
def({ key: 'b', group: 'general' }),
def({ key: 'c', group: 'presentation' }),
def({ key: 'd', group: 'physics' }),
];
const groups = groupDefs(defs.map((d) => ({ ...d, type: 'string' as const })));
expect(groups.map((g) => g.group.id)).toEqual(['general', 'presentation', 'physics']);
expect(groups[2].defs.map((d) => d.key)).toEqual(['a', 'd']);
});
});