import { describe, expect, it } from 'vitest'; import { buildAsset } from '../../src/core/commands/assetCommands'; import { createSceneObject } from '../../src/core/model/SceneObject'; import { coerceValue, defaultsFor, schemaFor } from '../../src/core/properties/PropertySchema'; import { inheritedValue, isModified, isOverridden, resolveAssetProps, resolveSceneObjectProps } from '../../src/core/properties/resolve'; describe('PropertySchema', () => { it('has no position on any asset type', () => { for (const type of ['scene', 'camera', 'sprite'] as const) { const keys = schemaFor(type).map((d) => d.key); expect(keys).not.toContain('x'); expect(keys).not.toContain('y'); expect(keys).not.toContain('position'); } }); it('provides defaults for every key', () => { expect(defaultsFor('sprite')).toEqual({ width: 100, height: 100, color: 0xe0553f }); }); it('coerces values against the definition', () => { const width = schemaFor('sprite').find((d) => d.key === 'width')!; expect(coerceValue(width, -5)).toBe(1); expect(coerceValue(width, '42')).toBe(42); expect(coerceValue(width, 'abc')).toBe(100); const color = schemaFor('sprite').find((d) => d.key === 'color')!; expect(coerceValue(color, '#ff0000')).toBe(0xff0000); expect(coerceValue(color, 0x1ffffff)).toBe(0xffffff); }); }); describe('resolve', () => { it('fills asset props with defaults and ignores unknown keys', () => { const sprite = buildAsset('sprite', 'Box'); sprite.props = { width: 50, bogus: 1 }; expect(resolveAssetProps(sprite)).toEqual({ width: 50, height: 100, color: 0xe0553f }); }); it('applies sparse overrides on top of asset values', () => { const sprite = buildAsset('sprite', 'Box'); sprite.props.color = 0x00ff00; const object = createSceneObject('o1', sprite.id, { x: 10, y: 20 }); expect(resolveSceneObjectProps(sprite, object)).toEqual({ width: 100, height: 100, color: 0x00ff00 }); object.overrides.color = 0x0000ff; expect(resolveSceneObjectProps(sprite, object).color).toBe(0x0000ff); expect(isOverridden(object, 'color')).toBe(true); expect(isOverridden(object, 'width')).toBe(false); expect(inheritedValue(sprite, 'color')).toBe(0x00ff00); }); it('detects asset values that differ from the schema default', () => { const sprite = buildAsset('sprite', 'Box'); expect(isModified(sprite, 'width')).toBe(false); // explicit default counts as unmodified sprite.props.width = 250; expect(isModified(sprite, 'width')).toBe(true); delete sprite.props.height; // missing → resolved from default → unmodified expect(isModified(sprite, 'height')).toBe(false); expect(isModified(sprite, 'bogus')).toBe(false); }); it('keeps position on the scene object only', () => { const sprite = buildAsset('sprite', 'Box'); const object = createSceneObject('o1', sprite.id, { x: 10, y: 20 }); expect(object.position).toEqual({ x: 10, y: 20, z: 0 }); expect('position' in sprite).toBe(false); expect(resolveSceneObjectProps(sprite, object)).not.toHaveProperty('x'); }); });