- Graph: nodes.ts split into nodes/<category>/<Type>.ts (62 files) with shared.ts factories and index.ts registry; NodeContext/values/NodeRegistry separated, writeTyped() replaces duplicated typed writes - Comments: one style (JSDoc on non-obvious exports, // for decisions and traps), wrong/stale comments fixed or removed, ids.ts now real 10 base-36 chars - Core: chainOf, shared worldPosition/editorPosition core, commands/require.ts, commands/naming.ts, util/list.ts, setBagValue/toPosition/propDef; dead code removed (isBehaviorAsset, SCENE_OBJECT_SCHEMA, CommandStack.subscribe, Input.axis, setMode, titleText, World.worldPosition, unused ctor params) - Editor: Inspector split into inspector/ sections, App.ts into Editor.ts + debugHook.ts + shortcuts.ts, Viewport into items.ts + symbols.ts, shared "Add collider" action, unified type labels/swatches - UI toolkit: Widget.trackHover/onPointerRelease, drawControlBox, math helpers, Popup.openBelow, Menu/Dialog/ColorPicker/ListRow in own files, named Theme sizes - Tools: seed-demo/smoke-demo and demo-check project removed, tools/lib (harness, projects, builders), e2e.mjs as runner over tools/e2e/*.mjs sections - Tests: tests/helpers.ts replaces copied fixtures and NodeContext doubles Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { CreateAssetCommand } from '../../src/core/commands/assetCommands';
|
||
import { AddColliderCommand } from '../../src/core/commands/colliderCommands';
|
||
import { SetOverrideCommand } from '../../src/core/commands/sceneObjectCommands';
|
||
import { footprintOf, textSize } from '../../src/core/properties/bounds';
|
||
import { fakeHandle, makeSpriteScene, place, runNode, startWorld } from '../helpers';
|
||
|
||
describe('text objects', () => {
|
||
it('measures a text footprint from its content and font size', () => {
|
||
expect(textSize({ text: 'ab', fontSize: 100 }).w).toBeCloseTo(110, 5);
|
||
expect(textSize({ text: 'ab', fontSize: 100 }).h).toBeCloseTo(125, 5);
|
||
expect(footprintOf('text', { text: 'abcd', fontSize: 100 }).w).toBeCloseTo(220, 5);
|
||
expect(runNode('SetText', { object: fakeHandle({ text: 'old' }), text: 42 })).toEqual({});
|
||
});
|
||
});
|
||
|
||
describe('the mouse', () => {
|
||
it('hit-tests an object through its colliders, else through its sprites and texts', () => {
|
||
const f = makeSpriteScene('Button');
|
||
const { stack, sceneId } = f;
|
||
const button = place(f, f.spriteId, { x: 0, y: 0 });
|
||
const label = new CreateAssetCommand('text', 'Label');
|
||
stack.execute(label);
|
||
place(f, label.asset.id, { x: 0, y: 200 }, button);
|
||
|
||
const { world } = startWorld(f);
|
||
const entity = world.findByObjectId(button)!;
|
||
expect(world.hitTest(entity, 10, 10)).toBe(true); // inside the 100×100 sprite
|
||
expect(world.hitTest(entity, 10, 200)).toBe(true); // on the label parented below it
|
||
expect(world.hitTest(entity, 400, 400)).toBe(false);
|
||
|
||
// A collider takes over the hit test for the whole subtree.
|
||
const collider = new AddColliderCommand(sceneId, button);
|
||
stack.execute(collider);
|
||
stack.execute(new SetOverrideCommand(sceneId, collider.objectId, 'width', 20));
|
||
world.syncFromModel();
|
||
expect(world.hitTest(entity, 5, 5)).toBe(true);
|
||
expect(world.hitTest(entity, 40, 0)).toBe(false); // outside the 20-wide collider
|
||
expect(world.hitTest(entity, 10, 200)).toBe(false); // the label no longer answers
|
||
|
||
// Switched off, nothing of it can be hit.
|
||
entity.props.enabled = false;
|
||
expect(world.hitTest(entity, 5, 5)).toBe(false);
|
||
});
|
||
|
||
it('reports pointer edges for exactly one tick', () => {
|
||
const { world } = startWorld(makeSpriteScene());
|
||
world.movePointer(12, 34, true);
|
||
world.setPointerDown(true);
|
||
expect(world.pointer).toMatchObject({ x: 12, y: 34, inside: true, down: true, pressed: true });
|
||
world.tick(0.1);
|
||
expect(world.pointer).toMatchObject({ down: true, pressed: false, released: false });
|
||
world.setPointerDown(false);
|
||
expect(world.pointer).toMatchObject({ down: false, released: true });
|
||
});
|
||
});
|