import { describe, expect, it } from "vitest"; import { activeSequence, createCommand, mediaToTimelineItem } from "../../../packages/editor-core/src"; import { normalizeMediaAsset } from "../../../packages/media-engine/src"; import { createInitialEditorState, editorReducer } from "./store"; const media = normalizeMediaAsset({ id: "m1", name: "clip.mov", kind: "video", sourcePath: "/tmp/clip.mov", fingerprint: "clip", duration: 5, width: 1920, height: 1080, hasAudio: true, missing: false, }); describe("editor history v2", () => { it("never lets a stale autosave overwrite newer edits", () => { let state = createInitialEditorState(); state = editorReducer(state, { type: "IMPORT_MEDIA", media: [media] }); const staleSnapshot = state.project; const item = mediaToTimelineItem(media, activeSequence(state.project)); state = editorReducer(state, { type: "APPLY_COMMAND", command: createCommand(state.project, "append", { item }) }); const latestRevision = state.project.revision; state = editorReducer(state, { type: "PROJECT_SAVED", project: staleSnapshot, projectPath: "mobile-project" }); expect(state.project.revision).toBe(latestRevision); expect(activeSequence(state.project).items).toHaveLength(1); expect(state.dirty).toBe(true); }); it("undoes and redoes committed edits without recording playhead movement", () => { let state = createInitialEditorState(); state = editorReducer(state, { type: "IMPORT_MEDIA", media: [media] }); const item = mediaToTimelineItem(media, activeSequence(state.project)); state = editorReducer(state, { type: "APPLY_COMMAND", command: createCommand(state.project, "append", { item }) }); const historyLength = state.history.length; state = editorReducer(state, { type: "SET_PLAYHEAD", frame: 60 }); expect(state.history).toHaveLength(historyLength); state = editorReducer(state, { type: "UNDO" }); expect(activeSequence(state.project).items).toHaveLength(0); state = editorReducer(state, { type: "REDO" }); expect(activeSequence(state.project).items).toHaveLength(1); }); it("treats a continuous slider gesture as one undoable edit", () => { let state = createInitialEditorState(); state = editorReducer(state, { type: "IMPORT_MEDIA", media: [media] }); const item = mediaToTimelineItem(media, activeSequence(state.project)); state = editorReducer(state, { type: "APPLY_COMMAND", command: createCommand(state.project, "append", { item }) }); const historyLength = state.history.length; state = editorReducer(state, { type: "APPLY_COMMAND", command: createCommand(state.project, "set-item", { itemId: item.id, patch: { transform: { ...item.transform, scale: { ...item.transform.scale, value: 120 } } } }), coalesceKey: `${item.id}:transform:scale` }); const changed = activeSequence(state.project).items[0]; state = editorReducer(state, { type: "APPLY_COMMAND", command: createCommand(state.project, "set-item", { itemId: item.id, patch: { transform: { ...changed.transform, scale: { ...changed.transform.scale, value: 135 } } } }), coalesceKey: `${item.id}:transform:scale` }); expect(state.history).toHaveLength(historyLength + 1); expect(activeSequence(state.project).items[0].transform.scale.value).toBe(135); state = editorReducer(state, { type: "UNDO" }); expect(activeSequence(state.project).items[0].transform.scale.value).toBe(100); }); });