test: pin captured-callback identity and canvas-teardown safety

Two non-blocking review questions about edge-case behavior. Both
guarantees were already implicit in the implementation; these tests
pin them so future refactors can't silently regress.

1. The RAF closure captures the onSelectionChange reference snapshotted
   at drag-start, not the live this.onSelectionChange. Reassigning the
   property between drag-start and the next frame must not redirect the
   pending notification.

2. Tearing down the canvas DOM between drag-start and the RAF firing
   must not throw. The deferred callback is a closure over plain
   references, so it runs to completion regardless of element lifecycle.
This commit is contained in:
Glary-Bot
2026-05-11 19:26:27 +00:00
parent 187c9e12b2
commit 0db984b433

View File

@@ -179,4 +179,27 @@ describe('_startDraggingItems defers onSelectionChange', () => {
expect(canvas.onSelectionChange).toBe(onSelectionChange)
canvas.processSelect = original
})
it('invokes the callback captured at drag-start, not a later replacement', () => {
const captured = vi.fn()
const replacement = vi.fn()
canvas.onSelectionChange = captured
canvas['_startDraggingItems'](node, pointer, true)
canvas.onSelectionChange = replacement
vi.advanceTimersByTime(16)
expect(captured).toHaveBeenCalledTimes(1)
expect(replacement).not.toHaveBeenCalled()
})
it('does not throw if the canvas is torn down before the RAF fires', () => {
canvas.onSelectionChange = vi.fn()
canvas['_startDraggingItems'](node, pointer, true)
canvasElement.remove()
expect(() => vi.advanceTimersByTime(16)).not.toThrow()
})
})