diff --git a/commands/dispatcher.test.ts b/commands/dispatcher.test.ts index 99dedec..47c112b 100644 --- a/commands/dispatcher.test.ts +++ b/commands/dispatcher.test.ts @@ -1,10 +1,17 @@ import { describe, expect, test } from "bun:test"; +import type { Command } from "./command"; import { createInitialAppState } from "@editor/initial-state"; import { createAppStore } from "@editor/store"; import { commandIds } from "./ids"; import { createCommandRegistry } from "./registry"; import { viewportPanCommand } from "./viewport"; +const noOpCommand: Command = { + id: "test.noop", + name: "No-op", + execute: ({ state }) => state, +}; + describe("command dispatcher", () => { test("throws for unknown commands", () => { const store = createAppStore(createInitialAppState("Test"), createCommandRegistry([])); @@ -16,4 +23,23 @@ describe("command dispatcher", () => { store.dispatch(commandIds.viewportPan, { delta: { x: 3, y: 7 } }); expect(store.getState().editor.viewport.center).toEqual({ x: 3, y: 7 }); }); + + test("repeated no-op commands do not emit or record history", () => { + const initialState = createInitialAppState("Test"); + const store = createAppStore(initialState, createCommandRegistry([noOpCommand])); + let notificationCount = 0; + store.subscribe(() => { + notificationCount += 1; + }); + + const firstResult = store.dispatch("test.noop" as never, undefined as never); + const secondResult = store.dispatch("test.noop" as never, undefined as never); + + expect(firstResult).toBe(initialState); + expect(secondResult).toBe(initialState); + expect(store.getState()).toBe(initialState); + expect(store.getState().history.past).toHaveLength(0); + expect(store.getState().history.future).toHaveLength(0); + expect(notificationCount).toBe(0); + }); }); diff --git a/commands/dispatcher.ts b/commands/dispatcher.ts index d6b05a3..ff939c0 100644 --- a/commands/dispatcher.ts +++ b/commands/dispatcher.ts @@ -25,6 +25,9 @@ export function createCommandDispatcher(options: { const currentState = options.getState(); const context: CommandContext = { state: currentState }; const executedState = command.execute(context, payload); + if (executedState === currentState) { + return currentState; + } const nextState = shouldRecordHistory(commandId, currentState, executedState) ? recordHistory(currentState, executedState) : executedState; options.setState(nextState); return nextState;