import { describe, expect, it } from 'vitest'; import { bool, choice, disabled, json, kebab, num, positional, str } from '../../src/core/props.ts'; const noopGet = () => 1; describe('kebab', () => { it('converts camelCase to kebab-case', () => { expect(kebab('fillColor')).toBe('fill-color'); expect(kebab('zIndex')).toBe('z-index'); expect(kebab('zIndexOffset')).toBe('z-index-offset'); }); it('leaves single lowercase words unchanged', () => { expect(kebab('opacity')).toBe('opacity'); }); }); describe('num', () => { it('defaults to 0 and decodes/encodes via Number/String', () => { const def = num(); expect(def.default).toBe(0); expect(def.decode('42')).toBe(42); expect(def.encode(42)).toBe('42'); }); it('accepts a custom default and passes through extra opts', () => { const def = num(10, { get: noopGet }); expect(def.default).toBe(10); expect(def.get).toBe(noopGet); }); }); describe('str', () => { it('defaults to empty string and decode/encode are identity', () => { const def = str(); expect(def.default).toBe(''); expect(def.decode('hello')).toBe('hello'); expect(def.encode('hello')).toBe('hello'); }); }); describe('choice', () => { it('decode/encode are identity, typed as the literal union', () => { const def = choice<'a' | 'b'>('a'); expect(def.default).toBe('a'); expect(def.decode('b')).toBe('b'); expect(def.encode('b')).toBe('b'); }); }); describe('bool', () => { it('decodes any value other than the literal string "false" as true', () => { const def = bool(); expect(def.decode('')).toBe(true); expect(def.decode('true')).toBe(true); expect(def.decode('anything')).toBe(true); expect(def.decode('false')).toBe(false); }); it('encode returns null when the value matches the default (removes the attribute)', () => { const defFalse = bool(false); expect(defFalse.encode(false)).toBeNull(); expect(defFalse.encode(true)).toBe(''); const defTrue = bool(true); expect(defTrue.encode(true)).toBeNull(); expect(defTrue.encode(false)).toBe('false'); }); }); describe('disabled', () => { it('names its attribute disable-', () => { const def = disabled(); expect(def.attribute).toBeTypeOf('function'); expect((def.attribute as (name: string) => string)('dragging')).toBe('disable-dragging'); }); it('defaults to true (enabled); presence of the attribute disables unless its value is "false"', () => { const def = disabled(); expect(def.default).toBe(true); // Bare `disable-foo` (raw === '') means "disable this" -> false. expect(def.decode('')).toBe(false); expect(def.decode('anything')).toBe(false); // `disable-foo="false"` is a double-negative escape hatch -> stays enabled. expect(def.decode('false')).toBe(true); }); it('encodes false as a present attribute, true as absent', () => { const def = disabled(); expect(def.encode(true)).toBeNull(); expect(def.encode(false)).toBe(''); }); }); describe('json', () => { it('decodes via JSON.parse and encodes via JSON.stringify', () => { const def = json<{ a: number }>({ a: 0 }); expect(def.decode('{"a":5}')).toEqual({ a: 5 }); expect(def.encode({ a: 5 })).toBe('{"a":5}'); }); }); describe('positional', () => { it('adds option: false while preserving every other field', () => { const base = num(5, { get: () => 1 }); const pos = positional(base); expect(pos.option).toBe(false); expect(pos.default).toBe(5); expect(pos.get).toBe(base.get); expect(pos.decode).toBe(base.decode); }); });