108 lines
4.3 KiB
TypeScript
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']);
|
|
});
|
|
});
|