openpencil/tests/engine/undo-create-shape.test.ts
Danila Poyarkov b3053601f7 Add arrow key nudge and fix undo/redo bugs
Nudge: Arrow keys move selection by 1px, Shift+Arrow by 10px.
Consecutive nudges within 300ms are batched into a single undo
entry so Cmd+Z undoes the entire sequence at once.

Fix duplicate redo creating nodes with new IDs, breaking
subsequent undo entries that reference the original IDs. Now
snapshotSubtree/restoreSubtree preserve node IDs across redo.

Fix delete undo not restoring children — deleteSelected only
snapshotted top-level nodes, so undoing a frame delete lost all
its children. Now snapshots the full subtree.

Fix draw tool not committing resize to undo — shapes created via
draw had their final dimensions lost on redo because handleDrawUp
never recorded the resize. Now uses beginBatch/commitBatch to
combine create+resize into a single undo entry.

Clean up use-keyboard.ts — remove shouldPreventDefault and opaque
PREVENT_* sets, move tool switch and nudge to dedicated keydown
listener, add shift() helper.

Closes #182
2026-04-10 00:26:13 +03:00

64 lines
2.1 KiB
TypeScript

import { describe, test, expect } from 'bun:test'
import { createEditor } from '@open-pencil/core/editor'
describe('create shape undo/redo', () => {
test('batched create+resize undoes in one step', () => {
const editor = createEditor()
// Simulate draw tool: batch(create + resize)
editor.undo.beginBatch('Create shape')
const id = editor.createShape('RECTANGLE', 100, 100, 0, 0)
editor.graph.updateNode(id, { x: 100, y: 100, width: 200, height: 150 })
editor.commitResize(id, { x: 100, y: 100, width: 0, height: 0 })
editor.undo.commitBatch()
expect(editor.graph.getNode(id)!.width).toBe(200)
expect(editor.graph.getNode(id)!.height).toBe(150)
// Single undo removes the shape entirely
editor.undo.undo()
expect(editor.graph.getNode(id)).toBeUndefined()
// Single redo restores with full dimensions
editor.undo.redo()
expect(editor.graph.getNode(id)).not.toBeUndefined()
expect(editor.graph.getNode(id)!.width).toBe(200)
expect(editor.graph.getNode(id)!.height).toBe(150)
expect(editor.graph.getNode(id)!.x).toBe(100)
expect(editor.graph.getNode(id)!.y).toBe(100)
})
test('redo after create+move+duplicate restores correct state', () => {
const editor = createEditor()
// Create shape via draw tool (batched)
editor.undo.beginBatch('Create shape')
const id = editor.createShape('RECTANGLE', 50, 50, 0, 0)
editor.graph.updateNode(id, { x: 50, y: 50, width: 120, height: 80 })
editor.commitResize(id, { x: 50, y: 50, width: 0, height: 0 })
editor.undo.commitBatch()
// Move it
editor.select([id])
editor.graph.updateNode(id, { x: 200, y: 200 })
editor.commitMove(new Map([[id, { x: 50, y: 50 }]]))
// Undo move
editor.undo.undo()
expect(editor.graph.getNode(id)!.x).toBe(50)
// Undo create (single step)
editor.undo.undo()
expect(editor.graph.getNode(id)).toBeUndefined()
// Redo create (single step, full dimensions)
editor.undo.redo()
expect(editor.graph.getNode(id)!.width).toBe(120)
// Redo move
editor.undo.redo()
expect(editor.graph.getNode(id)!.x).toBe(200)
})
})