text: import {Actions, Task, UndoRedoStore} from 'nylas-exports'
  
  class Undoable extends Task {
    canBeUndone() {
      return true
    }
  
    createIdenticalTask() {
      const t = new Undoable()
      t.id = this.id
      return t
    }
  }
  
  class PermanentTask extends Task {
    canBeUndone() {
      return false
    }
  }
  
  describe("UndoRedoStore", function undoRedoStoreSpec() {
    beforeEach(() => {
      UndoRedoStore._undo = []
      UndoRedoStore._redo = []
      spyOn(UndoRedoStore, "trigger")
      spyOn(Actions, "undoTaskId")
      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.isRedoTask = true
      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.undoTaskId.calls.length).toBe(2)
      expect(Actions.undoTaskId.calls[0].args[0]).toBe("t1")
      expect(Actions.undoTaskId.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].isRedoTask).toBe(true)
      expect(Actions.queueTasks.calls[0].args[0][1].isRedoTask).toBe(true)
    });
  
    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([])
    });
  });
  