Files
feuerwerk/tests/core/serialize.test.ts
T
rootandClaude Opus 5 9027a86edd A behavior has rows of its own, so loading no longer drops Run in editor
`deserializeProject` builds each asset's effective schema in pass 1 and coerces
`props` against it in pass 2. For a behavior that schema was
`[...COMMON_PROPS, ...variables]` -- but `runInEditor` lives in
`ASSET_SCHEMAS.behavior`, not in `COMMON_PROPS`, so `normalizeBag` dropped the
value as undeclared. The flag therefore survived no save: every reload switched
it back off.

The fix is the full schema, `schemaFor('behavior')`, which is what
`schemaForAsset` hands out everywhere else.

Found by a round-trip test comparing the whole project rather than one field. An
existing test had the loss written down as its expectation
(`{ enabled, speed }`), which is why nothing ever reported it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 16:13:40 +02:00

86 lines
3.0 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { SetAssetPropertyCommand } from '../../src/core/commands/assetCommands';
import { SetOverrideCommand } from '../../src/core/commands/sceneObjectCommands';
import { deserializeProject } from '../../src/core/store/serialize';
import { makeBehavior, makeSpriteScene, place, roundTrip } from '../helpers';
describe('serialize', () => {
it('round-trips a project', () => {
const f = makeSpriteScene();
const box = place(f, f.spriteId, { x: 7, y: 8 });
f.stack.execute(new SetOverrideCommand(f.sceneId, box, 'width', 33));
expect(roundTrip(f.project)).toEqual(f.project);
});
/** A behavior's own rows are not the common ones: coercing against those alone dropped the value. */
it('keeps a behavior running in the editor across a save', () => {
const f = makeSpriteScene();
const behavior = makeBehavior(f, 'B');
f.stack.execute(new SetAssetPropertyCommand(behavior.id, 'runInEditor', true));
const loaded = roundTrip(f.project).assets.find((a) => a.id === behavior.id)!;
expect(loaded.props.runInEditor).toBe(true);
});
it('repairs broken documents', () => {
const project = deserializeProject(
{
name: 'Broken',
activeSceneId: 'missing',
assets: [
{
id: 's1',
type: 'scene',
name: 'Scene',
props: { backgroundColor: '#000000', junk: 1 },
objects: [
{
id: 'o1',
assetId: 'sp1',
position: { x: '5', y: null },
overrides: { color: '#00ff00', width: -3, foo: 1 },
},
{ id: 'o2', assetId: 'nope', position: {} },
{ id: 'o3', assetId: 's1', position: {} },
],
},
{ id: 'sp1', type: 'sprite', props: { width: 10 } },
// An asset type this build does not know (a newer document, a renamed type).
{ id: 'x', type: 'sound' },
{ type: 'camera' },
],
},
'fallback-id',
);
expect(project.id).toBe('fallback-id');
expect(project.activeSceneId).toBe('s1');
expect(project.assets.map((a) => a.id)).toEqual(['s1', 'sp1']);
const scene = project.assets[0]!;
expect(scene.type === 'scene' && scene.props).toEqual({ backgroundColor: 0 });
const objects = scene.type === 'scene' ? scene.objects : [];
expect(objects).toHaveLength(1);
expect(objects[0]).toEqual({
id: 'o1',
assetId: 'sp1',
name: '',
position: { x: 5, y: 0, z: 0 },
editorOffset: { x: 0, y: 0 },
overrides: { color: 0x00ff00, width: 1 },
parentId: '',
instanceEdits: {},
});
expect(project.assets[1]!.name).toBe('sprite');
expect(project.assets[1]!.props).toEqual({
enabled: true,
scale: 1,
rotation: 0,
mirrorX: false,
mirrorY: false,
width: 10,
height: 100,
color: 0xffffff,
texture: '',
shader: '',
});
});
});