import { Actions, Task, UndoRedoStore } from 'mailspring-exports'; class Undoable extends Task { constructor() { super(); this.canBeUndone = true; } } class PermanentTask extends Task { constructor() { super(); this.canBeUndone = false; } } describe('UndoRedoStore', function undoRedoStoreSpec() { beforeEach(() => { UndoRedoStore._undo = []; UndoRedoStore._redo = []; spyOn(UndoRedoStore, 'trigger'); spyOn(Actions, 'queueTask').andCallFake((...args) => { UndoRedoStore._onQueue(...args); }); spyOn(Actions, 'queueTasks').andCallFake((...args) => { UndoRedoStore._onQueue(...args); }); this.ids = arrarr => arrarr.map(arr => arr.map(itm => itm.id)); this.t1 = new Undoable(); this.t2 = new Undoable(); this.t3 = new Undoable(); this.t4 = new Undoable(); this.p1 = new PermanentTask(); this.t1.id = 't1'; this.t2.id = 't2'; this.t3.id = 't3'; this.t4.id = 't4'; this.p1.id = 'p1'; }); it('pushes single tasks onto undo/redo', () => { Actions.queueTask(this.t1); expect(UndoRedoStore._redo).toEqual([]); expect(UndoRedoStore._undo).toEqual([[this.t1]]); expect(UndoRedoStore.trigger).toHaveBeenCalled(); }); it('pushes multiple tasks onto redo', () => { Actions.queueTasks([this.t1, this.t2]); expect(UndoRedoStore._redo).toEqual([]); expect(UndoRedoStore._undo).toEqual([[this.t1, this.t2]]); expect(UndoRedoStore.trigger).toHaveBeenCalled(); }); it("only undoes task if they're all 'undoable'", () => { Actions.queueTask([this.t1, this.p1]); expect(UndoRedoStore._redo).toEqual([]); expect(UndoRedoStore._undo).toEqual([]); expect(UndoRedoStore.trigger).not.toHaveBeenCalled(); }); it('refreshes redo if we get a new task', () => { UndoRedoStore._redo = [[this.t1, this.t2], [this.t3]]; Actions.queueTask(this.t3); expect(UndoRedoStore._redo).toEqual([]); }); it("doesn't refresh redo if our task is itself a redo task", () => { UndoRedoStore._redo = [[this.t1, this.t2], [this.t3]]; const tr = new Undoable(); tr.source = 'redo'; Actions.queueTask(tr); expect(UndoRedoStore._redo).toEqual([[this.t1, this.t2], [this.t3]]); expect(UndoRedoStore._undo).toEqual([[tr]]); }); it('runs undoTask on each group of undo tasks', () => { UndoRedoStore._undo = [[this.t3], [this.t1, this.t2]]; UndoRedoStore.undo(); expect(Actions.queueTask.calls.length).toBe(2); expect(Actions.queueTask.calls[0].args[0]).toBe('t1'); expect(Actions.queueTask.calls[1].args[0]).toBe('t2'); expect(UndoRedoStore._undo).toEqual([[this.t3]]); }); it('creates identical redo tasks and pushes on the stack', () => { UndoRedoStore._undo = [[this.t3], [this.t1, this.t2]]; UndoRedoStore.undo(); expect(UndoRedoStore._undo).toEqual([[this.t3]]); expect(UndoRedoStore._redo[0][0].id).toBe('t1'); expect(UndoRedoStore._redo[0][1].id).toBe('t2'); expect(UndoRedoStore._redo.length).toBe(1); }); it('redoes the latest task', () => { UndoRedoStore._undo = [[this.t3], [this.t1, this.t2]]; UndoRedoStore.undo(); UndoRedoStore.redo(); expect(Actions.queueTasks.calls[0].args[0][0].id).toBe('t1'); expect(Actions.queueTasks.calls[0].args[0][1].id).toBe('t2'); expect(UndoRedoStore._undo[0]).toEqual([this.t3]); expect(UndoRedoStore._undo[1][0].id).toBe('t1'); expect(UndoRedoStore._undo[1][1].id).toBe('t2'); }); it('marks the incoming task as a redo task', () => { UndoRedoStore._undo = [[this.t3], [this.t1, this.t2]]; UndoRedoStore.undo(); UndoRedoStore.redo(); expect(Actions.queueTasks.calls[0].args[0][0].source).toEqual('redo'); expect(Actions.queueTasks.calls[0].args[0][1].source).toEqual('redo'); }); it('correctly follows the undo redo sequence of events', () => { Actions.queueTask(this.t1); Actions.queueTask(this.t2); Actions.queueTasks([this.t3, this.t4]); expect(UndoRedoStore._undo).toEqual([[this.t1], [this.t2], [this.t3, this.t4]]); UndoRedoStore.undo(); UndoRedoStore.undo(); UndoRedoStore.undo(); UndoRedoStore.undo(); UndoRedoStore.undo(); expect(UndoRedoStore._undo).toEqual([]); expect(this.ids(UndoRedoStore._redo)).toEqual([['t3', 't4'], ['t2'], ['t1']]); UndoRedoStore.redo(); UndoRedoStore.redo(); UndoRedoStore.redo(); UndoRedoStore.redo(); UndoRedoStore.redo(); UndoRedoStore.redo(); expect(this.ids(UndoRedoStore._undo)).toEqual([['t1'], ['t2'], ['t3', 't4']]); expect(this.ids(UndoRedoStore._redo)).toEqual([]); }); });