Daniel@0: "use strict"; Daniel@0: Daniel@0: describe("ContextModule.StateHistory", function() { Daniel@0: Daniel@0: it("is promptly created", function() { Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory(); Daniel@0: Daniel@0: expect(testedStateHistory).not.toBe(null); Daniel@0: expect(testedStateHistory.get("maxStackSize")).toEqual(50); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual(undefined); Daniel@0: expect(testedStateHistory.get("compoundChangeDetector")).toEqual(undefined); Daniel@0: }); Daniel@0: Daniel@0: it("is promptly created with attributes", function() { Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory({ Daniel@0: maxStackSize: 40, Daniel@0: currentSerializedState: 42, Daniel@0: compoundChangeDetector: function() {} Daniel@0: }); Daniel@0: Daniel@0: expect(testedStateHistory).not.toBe(null); Daniel@0: expect(testedStateHistory.get("maxStackSize")).toEqual(40); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual(42); Daniel@0: expect(typeof testedStateHistory.get("compoundChangeDetector")).toEqual("function"); Daniel@0: }); Daniel@0: Daniel@0: it("undoes and redoes", function() { Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory({ Daniel@0: currentSerializedState: {"a": 42}, Daniel@0: }); Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 42}); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 42}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: expect(function() { Daniel@0: testedStateHistory.undo(); Daniel@0: }).toThrow(); Daniel@0: expect(function() { Daniel@0: testedStateHistory.redo(); Daniel@0: }).toThrow(); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 43, "b": 44}); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 43, "b": 44}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: // The same object does not create a new state in the undo stack Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 42}); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 42}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 43, "b": 44}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(true); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 42}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: expect(testedStateHistory.canRedo()).toBe(true); Daniel@0: Daniel@0: expect(function() { Daniel@0: testedStateHistory.undo(); Daniel@0: }).toThrow(); Daniel@0: Daniel@0: testedStateHistory.redo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 43, "b": 44}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(true); Daniel@0: Daniel@0: testedStateHistory.redo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 42}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 43, "b": 44}); Daniel@0: Daniel@0: // In current implementation redo stack is wasted even Daniel@0: // if the new state is exactly the same as the first element there Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 42}); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"a": 42}); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: }); Daniel@0: it("resets", function() { Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory({ Daniel@0: currentSerializedState: {"a": 42}, Daniel@0: }); Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 43, "b": 44}); Daniel@0: testedStateHistory.set("currentSerializedState", {"a": 43, "b": 45}); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(true); Daniel@0: Daniel@0: testedStateHistory.reset(); Daniel@0: Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: }); Daniel@0: Daniel@0: it("trims undo and redo stacks", function() { Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory({ Daniel@0: maxStackSize: 10, Daniel@0: }); Daniel@0: for (var i = 20; i >= 0; --i) { Daniel@0: testedStateHistory.set("currentSerializedState", {"x": i}); Daniel@0: } Daniel@0: for (var i = 1; i <= 10; i++) { Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"x": i}); Daniel@0: } Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: expect(testedStateHistory.canRedo()).toBe(true); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"x": 10}); Daniel@0: Daniel@0: testedStateHistory.set("maxStackSize", 5); Daniel@0: for (var i = 9; i >= 5; --i) { Daniel@0: testedStateHistory.redo(); Daniel@0: expect(testedStateHistory.get("currentSerializedState")).toEqual({"x": i}); Daniel@0: } Daniel@0: Daniel@0: expect(testedStateHistory.canUndo()).toBe(true); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: testedStateHistory.set("maxStackSize", 1); Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: testedStateHistory.redo(); Daniel@0: expect(testedStateHistory.canRedo()).toBe(false); Daniel@0: Daniel@0: testedStateHistory.set("maxStackSize", 3); Daniel@0: testedStateHistory.set("currentSerializedState", {"x": 100}); Daniel@0: testedStateHistory.set("currentSerializedState", {"x": 200}); Daniel@0: testedStateHistory.set("currentSerializedState", {"x": 300}); Daniel@0: testedStateHistory.undo(); Daniel@0: testedStateHistory.undo(); Daniel@0: testedStateHistory.undo(); Daniel@0: expect(testedStateHistory.canUndo()).toBe(false); Daniel@0: }); Daniel@0: Daniel@0: it("triggers change:currentSerializedState when needed", function() { Daniel@0: var spyNames = [ Daniel@0: "change", Daniel@0: "change:currentSerializedState", Daniel@0: ]; Daniel@0: var spy = jasmine.createSpyObj("listener", spyNames); Daniel@0: var expectSpyCallCount = function() { Daniel@0: _.each(arguments, function(arg, i) { Daniel@0: //console.log("___", i, "--->", spy[spyNames[i]].calls.count(), arg); Daniel@0: expect(spy[spyNames[i]].calls.count()).toEqual(arg); Daniel@0: }); Daniel@0: for (var i = arguments.length; i < spyNames.length; i++) { Daniel@0: expect(spy[spyNames[i]].calls.count()).toEqual(0); Daniel@0: } Daniel@0: }; Daniel@0: var resetSpyCallCount = function() { Daniel@0: _.each(spyNames, function(spyName) { Daniel@0: spy[spyName].calls.reset(); Daniel@0: }); Daniel@0: }; Daniel@0: var expectSpyCallCountAndReset = function() { Daniel@0: expectSpyCallCount.apply(null, arguments); Daniel@0: resetSpyCallCount(); Daniel@0: }; Daniel@0: Daniel@0: var testedStateHistory = new App.ContextModule.StateHistory({ Daniel@0: maxStackSize: 10, Daniel@0: }); Daniel@0: Daniel@0: _.each(spyNames, function(spyName) { Daniel@0: testedStateHistory.on(spyName, spy[spyName]); Daniel@0: }); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"test": true}); Daniel@0: expectSpyCallCountAndReset(1, 1); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"test": true}); Daniel@0: expectSpyCallCountAndReset(0, 0); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"test": false}); Daniel@0: expectSpyCallCountAndReset(1, 1); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: testedStateHistory.undo(); Daniel@0: expectSpyCallCountAndReset(2, 2); Daniel@0: Daniel@0: testedStateHistory.redo(); Daniel@0: testedStateHistory.redo(); Daniel@0: expectSpyCallCountAndReset(2, 2); Daniel@0: Daniel@0: testedStateHistory.undo(); Daniel@0: expectSpyCallCountAndReset(1, 1); Daniel@0: Daniel@0: testedStateHistory.set("currentSerializedState", {"test": true}); Daniel@0: expectSpyCallCountAndReset(0, 0); Daniel@0: Daniel@0: testedStateHistory.reset(); Daniel@0: expectSpyCallCountAndReset(1, 0); Daniel@0: testedStateHistory.reset(); Daniel@0: expectSpyCallCountAndReset(0, 0); Daniel@0: Daniel@0: }); Daniel@0: });