Files
feuerwerk/tests/core/texture-commands.test.ts
T
rootandClaude Fable 5.1 016e83c9b5
game-maker / suite (push) Successful in 9m5s
Transform: scale, rotation and mirror as properties, world transform as a matrix chain, oriented colliders (#76)
Four new rows on sprite, text, collider and container (block TRANSFORM):
scale, rotation in degrees clockwise, mirrorX, mirrorY. As properties they
get override/reset/apply, loading of old projects, write-back after play and
the Properties node for free; camera and behavior have none.

The hierarchy composes T*R*S per level (composeAlongChain replaces
sumAlongChain), so children rotate and scale with their parent. Reparenting
and grouping keep the look (carriedTransform). Drawing goes through
placeNode, the gizmo is a polygon, cull and objectRect use the AABB, drags
are computed in the parent's system.

Colliders are oriented boxes: SAT for box/box, box-circle in the box frame;
exitDistance replaces axisSeparation/axisCorrection, moveAndSlide moves along
the object's own axes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 17:25:06 +02:00

82 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { CreateAssetCommand, CreateTextureCommand } from '../../src/core/commands/assetCommands';
import { CommandError } from '../../src/core/commands/Command';
import { findAsset } from '../../src/core/model/Project';
import { spriteFromTexture } from '../../src/core/properties/aspect';
import { makeProject } from '../helpers';
const FILE = `${'b'.repeat(64)}.webp`;
describe('CreateTextureCommand', () => {
it('adds a texture asset carrying the uploaded image, and removes it again', () => {
const { project, stack } = makeProject();
const command = new CreateTextureCommand('Hero', { file: FILE, width: 64, height: 32 });
stack.execute(command);
const asset = findAsset(project, command.asset.id);
expect(asset).toMatchObject({ type: 'texture', name: 'Hero', file: FILE, width: 64, height: 32 });
// The schema default comes along, so the new texture is a complete asset – nothing to fill in later.
expect(asset?.props).toEqual({ filter: 'linear' });
stack.undo();
expect(project.assets).toHaveLength(0);
stack.redo();
expect(findAsset(project, command.asset.id)?.name).toBe('Hero');
});
it('rejects a file name the server cannot have produced – in execute, not in the constructor', () => {
const { project, stack } = makeProject();
const command = new CreateTextureCommand('Hero', { file: '../secrets.png', width: 1, height: 1 });
expect(() => stack.execute(command)).toThrow(CommandError);
expect(project.assets).toHaveLength(0);
});
it('keeps two assets for the same file apart', () => {
const { project, stack } = makeProject();
stack.execute(new CreateTextureCommand('Hero', { file: FILE, width: 8, height: 8 }));
stack.execute(new CreateTextureCommand('Hero copy', { file: FILE, width: 8, height: 8 }));
expect(project.assets.map((a) => a.id)).toHaveLength(2);
expect(new Set(project.assets.map((a) => a.id)).size).toBe(2);
});
});
describe('CreateAssetCommand with starting props', () => {
it('creates a sprite wearing the texture at its pixel size, and removes it again', () => {
const { project, stack } = makeProject();
const texture = new CreateTextureCommand('Hero', { file: FILE, width: 64, height: 32 });
stack.execute(texture);
const sprite = new CreateAssetCommand('sprite', 'Hero 2', spriteFromTexture(project, texture.asset.id));
stack.execute(sprite);
expect(findAsset(project, sprite.asset.id)?.props).toEqual({
enabled: true,
scale: 1,
rotation: 0,
mirrorX: false,
mirrorY: false,
width: 64,
height: 32,
color: 0xffffff,
texture: texture.asset.id,
shader: '',
});
stack.undo();
expect(findAsset(project, sprite.asset.id)).toBeUndefined();
stack.redo();
expect(findAsset(project, sprite.asset.id)?.props.texture).toBe(texture.asset.id);
});
it('passes each value through the schema', () => {
const { project, stack } = makeProject();
const sprite = new CreateAssetCommand('sprite', 'Tiny', { width: -5 });
stack.execute(sprite);
expect(findAsset(project, sprite.asset.id)?.props.width).toBe(1);
});
it('rejects a property the type does not have, leaving the project untouched', () => {
const { project, stack } = makeProject();
expect(() => stack.execute(new CreateAssetCommand('sprite', 'Odd', { zoom: 2 }))).toThrow(CommandError);
expect(project.assets).toHaveLength(0);
});
});