/** Duplicate in an asset's menu (#297): a copy right behind the original that shares nothing with it. */ import { describe, expect, it } from 'vitest'; import { CreateAssetCommand, DuplicateAssetCommand } from '../../src/core/commands/assetCommands'; import { SetOverrideCommand } from '../../src/core/commands/sceneObjectCommands'; import { AddVariableCommand } from '../../src/core/commands/variableCommands'; import type { BehaviorAsset, PixelArtAsset, SceneAsset } from '../../src/core/model/Asset'; import { objectReference } from '../../src/core/model/objectRefs'; import { findAsset } from '../../src/core/model/Project'; import { makeBehavior, makeSpriteScene, place } from '../helpers'; describe('duplicating an asset', () => { it('puts a copy of a scene behind it, with fresh object ids that still point at each other', () => { const f = makeSpriteScene(); const behavior = makeBehavior(f, 'Watcher'); f.stack.execute(new AddVariableCommand(behavior.id, 'target', 'object')); const crate = place(f, f.spriteId, { x: 10, y: 0 }); const watcher = place(f, behavior.id, { x: 0, y: 0 }, crate); f.stack.execute(new SetOverrideCommand(f.sceneId, watcher, 'target', objectReference(crate))); const before = structuredClone(f.sceneOf()); const command = new DuplicateAssetCommand(f.sceneId); f.stack.execute(command); const copy = findAsset(f.project, command.copyId) as SceneAsset; expect(f.project.assets.map((a) => a.name)).toEqual(['Level', 'Level 2', 'Box', 'Watcher']); expect(copy.objects).toHaveLength(2); const [copiedCrate, copiedWatcher] = copy.objects; expect([crate, watcher]).not.toContain(copiedCrate!.id); expect([crate, watcher]).not.toContain(copiedWatcher!.id); expect(copiedWatcher!.parentId).toBe(copiedCrate!.id); expect(copiedWatcher!.overrides.target).toEqual(objectReference(copiedCrate!.id)); expect(f.sceneOf()).toEqual(before); f.stack.undo(); expect(findAsset(f.project, command.copyId)).toBeUndefined(); f.stack.redo(); expect(f.project.assets[1]?.id).toBe(command.copyId); }); it('gives the frames of pixel art new ids, since a sprite names a frame across the document', () => { const f = makeSpriteScene(); const create = new CreateAssetCommand('pixelart', 'Slime'); f.stack.execute(create); const original = create.asset as PixelArtAsset; const command = new DuplicateAssetCommand(original.id); f.stack.execute(command); const copy = findAsset(f.project, command.copyId) as PixelArtAsset; expect(copy.name).toBe('Slime 2'); expect(copy.frames[0]?.id).not.toBe(original.frames[0]?.id); expect(copy.layers).toEqual(original.layers); }); it('copies a graph by value, so editing the copy leaves the original alone', () => { const f = makeSpriteScene(); const behavior = makeBehavior(f, 'Mover'); const command = new DuplicateAssetCommand(behavior.id); f.stack.execute(command); const copy = findAsset(f.project, command.copyId) as BehaviorAsset; expect(copy.graph).toEqual(behavior.graph); copy.graph.nodes.push({ ...copy.graph.nodes[0]!, id: 'extra' }); expect(behavior.graph.nodes.some((n) => n.id === 'extra')).toBe(false); }); });