diff --git a/src/FlexUnitApplication.mxml b/src/FlexUnitApplication.mxml index a9e57b3..4f7c30a 100644 --- a/src/FlexUnitApplication.mxml +++ b/src/FlexUnitApplication.mxml @@ -20,7 +20,7 @@ } private function onCreationComplete():void { - testRunner.runWithFlexUnit4Runner(currentRunTestSuite(), "Robotlegs-Undoable-Commands"); + testRunner.runWithFlexUnit4Runner(currentRunTestSuite(), "Robotlegs-UnodableCommands"); } ]]> diff --git a/src/org/robotlegs/utilities/undoablecommand/CommandEvent.as b/src/org/robotlegs/utilities/undoablecommand/CommandEvent.as new file mode 100644 index 0000000..bfc2617 --- /dev/null +++ b/src/org/robotlegs/utilities/undoablecommand/CommandEvent.as @@ -0,0 +1,36 @@ +package org.robotlegs.utilities.undoablecommand +{ + import flash.events.Event; + + import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand; + + /** + * CommandEvents occur when commands finish executing or undoing + * + */ + public class CommandEvent extends Event + { + /** + * Defines the value of the type property of an executeComplete event object. + */ + public static const EXECUTE_COMPLETE:String = "executeComplete"; + /** + * Defines the value of the type property of an undoExecuteComplete event object. + */ + public static const UNDO_EXECUTE_COMPLETE:String = "undoExecuteComplete"; + + /** + * The command associated with the event + */ + public var command:IUndoableCommand; + + /** + * @param type The type of event + * @param command The command associated with the event + */ + public function CommandEvent(type:String, command:IUndoableCommand) { + super(type, false, false); + this.command = command; + } + } +} \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/CommandHistory.as b/src/org/robotlegs/utilities/undoablecommand/CommandHistory.as index 4bff4b5..88de3ca 100644 --- a/src/org/robotlegs/utilities/undoablecommand/CommandHistory.as +++ b/src/org/robotlegs/utilities/undoablecommand/CommandHistory.as @@ -1,13 +1,26 @@ package org.robotlegs.utilities.undoablecommand { + import flash.events.IEventDispatcher; + import org.robotlegs.utilities.undoablecommand.interfaces.*; public class CommandHistory { + /** + * Command history data store. + * Vector chosen over array for strongtyping & speed + */ private var _historyStack:Vector.; + /** + * Pointer to the current command in the history stack + * Index starts at 1. + * If this is 0, we are pointing to null at the start of the stack + */ public var currentPosition:uint; - public var isReady:Boolean; + + [Inject] + public var eventDispatcher:IEventDispatcher; public function CommandHistory() { _historyStack = new Vector.(); @@ -20,7 +33,6 @@ package org.robotlegs.utilities.undoablecommand */ public function get canStepForward():Boolean { return (currentPosition < numberOfHistoryItems); - } /** @@ -38,8 +50,11 @@ package org.robotlegs.utilities.undoablecommand */ public function stepForward():uint { if (canStepForward) { - _historyStack[currentPosition++].execute(); + _historyStack[currentPosition].execute(); + currentPosition++; } + this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.STEP_FORWARD_COMPLETE, currentCommand)); + return currentPosition; } @@ -49,9 +64,29 @@ package org.robotlegs.utilities.undoablecommand * @return position in history stack after this operation */ public function stepBackward():uint { + var storeCurrentPosition:uint = currentPosition; if (canStepBackward) { - _historyStack[--currentPosition].undo(); + // Hacky workaround: + // if undo was invoked from a commandHistory object, + // _historyStack[currentPosition - 1].undo() calls + // stepBackward() again (), in which case we want to prevent it from + // updating the current position twice. I'm certain thar be a better way. + _historyStack[currentPosition - 1].undo(); + currentPosition = storeCurrentPosition - 1; } + + // If there's no undone command, + // dispatch null as the historyevent command + var undoneCommand:IUndoableCommand; + if (_historyStack.length > 0 && currentPosition != 0) { + //the undone command + undoneCommand = _historyStack[currentPosition]; + } else { + undoneCommand = null; + } + + this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.STEP_BACKWARD_COMPLETE, undoneCommand)); + return currentPosition; } @@ -69,9 +104,12 @@ package org.robotlegs.utilities.undoablecommand positionToMoveTo = currentPosition - numTimes; } + // Move backward while(canStepBackward && currentPosition != positionToMoveTo) { stepBackward(); } + + this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.REWIND_COMPLETE, currentCommand)); return currentPosition; } @@ -89,15 +127,19 @@ package org.robotlegs.utilities.undoablecommand positionToMoveTo = currentPosition + numTimes; } + // Move forward while(canStepForward && currentPosition != positionToMoveTo) { stepForward(); } + + this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.FAST_FORWARD_COMPLETE, currentCommand)); + return currentPosition; } /** - * @return number of items in history, both forward - * and backward from current position + * @return total number of items in history, + * irrespective of whether they have been undone */ public function get numberOfHistoryItems():uint { return _historyStack.length; @@ -105,20 +147,47 @@ package org.robotlegs.utilities.undoablecommand /** * Push a command onto the history stack + * If there are commands that are yet to be redone, + * those commands are lost and this command becomes + * the top of the command stack. + * * @return position in history stack after this operation */ public function push(command:IUndoableCommand):uint { + if (currentPosition != numberOfHistoryItems) { _historyStack = _historyStack.slice(0, currentPosition); } _historyStack.push(command); - // Executes the command + + // Execute the command & move pointer forward stepForward(); return currentPosition; } + /** + * @return command at the current position in the history stack, + * or null if we're at position 0, or there are simply no commands + * @see currentPosition + */ public function get currentCommand():IUndoableCommand { + if (_historyStack.length == 0 || currentPosition == 0) { + return null; + } return _historyStack[currentPosition - 1]; } + + /** + * @private + */ + public function toString():String { + var output:String = ""; + var count:uint = 0; + for each(var command:IUndoableCommand in _historyStack) { + output += String(count) + String(command) + "\n"; + count++; + } + return output; + } } } \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/HistoryEvent.as b/src/org/robotlegs/utilities/undoablecommand/HistoryEvent.as new file mode 100644 index 0000000..4492f64 --- /dev/null +++ b/src/org/robotlegs/utilities/undoablecommand/HistoryEvent.as @@ -0,0 +1,50 @@ +package org.robotlegs.utilities.undoablecommand +{ + import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand; + + public class HistoryEvent extends CommandEvent + { + /** + * Defines the value of the type property of a stepBackward event object. + * Map this event to StepBackwardCommand to trigger an undo action. + */ + public static const STEP_BACKWARD:String = "stepBackward"; + /** + * Defines the value of the type property of a stepforward event object. + * Map this event to StepForwardCommand to trigger a redo action. + */ + public static const STEP_FORWARD:String = "stepForward"; + + /** + * Defines the value of the type property of a stepForwardComplete event object. + */ + public static const STEP_FORWARD_COMPLETE:String = "stepForwardComplete"; + + /** + * Defines the value of the type property of a stepBackwardComplete event object. + */ + public static const STEP_BACKWARD_COMPLETE:String = "stepBackwardComplete"; + + /** + * Defines the value of the type property of a rewind event object. + */ + public static const REWIND:String = "rewind"; + /** + * Defines the value of the type property of a fastForward event object. + */ + public static const FAST_FORWARD:String = "fastForwardHistory"; + + /** + * Defines the value of the type property of a rewindComplete event object. + */ + public static const REWIND_COMPLETE:String = "rewindComplete"; + /** + * Defines the value of the type property of an fastForwardComplete event object. + */ + public static const FAST_FORWARD_COMPLETE:String = "fastForwardComplete"; + + public function HistoryEvent(type:String, command:IUndoableCommand = null) { + super(type, command); + } + } +} \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/ManagedUndoableCommand.as b/src/org/robotlegs/utilities/undoablecommand/ManagedUndoableCommand.as index f1e618c..f760b6f 100644 --- a/src/org/robotlegs/utilities/undoablecommand/ManagedUndoableCommand.as +++ b/src/org/robotlegs/utilities/undoablecommand/ManagedUndoableCommand.as @@ -2,9 +2,10 @@ package org.robotlegs.utilities.undoablecommand { /** - * This command handles it's own history when provided/injected with a CommandHistory object + * This command handles its own history when provided/injected with a CommandHistory object * All functions assume the CommandHistory dependency (history) has been provided * Command is only pushed to the CommandHistory when Command is executed. + * */ public class ManagedUndoableCommand extends UndoableCommand { @@ -14,6 +15,11 @@ package org.robotlegs.utilities.undoablecommand */ private var hasRegisteredWithHistory:Boolean; + /** + * @private + * Flag true after this Command has been stepped back by CommandHistory + */ + private var hasSteppedBack:Boolean; /** * Reference to the CommandHistory being used by this Command */ @@ -23,35 +29,45 @@ package org.robotlegs.utilities.undoablecommand /** * @inheritDoc */ - public function ManagedUndoableCommand(autoExecute:Boolean = true, doFunction:Function = null, undoFunction:Function = null) { - super(autoExecute, doFunction, undoFunction); - + public function ManagedUndoableCommand(doFunction:Function = null, undoFunction:Function = null) { + super(doFunction, undoFunction); } /** - * @throws Error if this Command is not on the top of the history stack. Prevents calling execute Commands out of CommandHistory order + * Executes the command. + * Override this function in your subclasses to implement your command's actions. + * Note command is only automatically pushed to history once we try to execute this command * @inheritDoc + * @see undoExecute */ override protected function doExecute():void { // Only push to history once we actually try to execute this command - registerIfRequired() - - if (history.currentCommand !== this) { - throw new Error("Cannot execute command unless command is first in command history"); + if (!hasRegisteredWithHistory) { + hasRegisteredWithHistory = true; + hasExecuted = true; + history.push(this); } - - } /** - * @throws Error If this Command is not on the top of the history stack. Prevents undoing Commands out of order + * Override this function in your subclasses to implement the undo of the actions performed in doExecute(). * @inheritDoc + * @see doExecute */ - override protected function undoExecute():void { - if (history.currentCommand != this) { - throw new Error("Cannot undo command unless command is first in command history"); + override protected function undoExecute() : void { + if (hasExecuted) { + this.hasExecuted = false; + + if (!hasSteppedBack) { + hasSteppedBack = true; + if (history.currentCommand != this) { + throw new Error("Cannot undo command unless command is first in command history"); + } + + history.stepBackward(); + hasSteppedBack = false; + } } - history.stepBackward(); } /** @@ -61,8 +77,8 @@ package org.robotlegs.utilities.undoablecommand */ private function registerIfRequired():void { if (!hasRegisteredWithHistory) { - history.push(this); hasRegisteredWithHistory = true; + history.push(this); } } } diff --git a/src/org/robotlegs/utilities/undoablecommand/UndoableCommand.as b/src/org/robotlegs/utilities/undoablecommand/UndoableCommand.as index e2692c2..3c81067 100644 --- a/src/org/robotlegs/utilities/undoablecommand/UndoableCommand.as +++ b/src/org/robotlegs/utilities/undoablecommand/UndoableCommand.as @@ -1,6 +1,7 @@ package org.robotlegs.utilities.undoablecommand { import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand; @@ -10,18 +11,31 @@ package org.robotlegs.utilities.undoablecommand * Keeps track of whether this command has been executed, * to prevent undoing actions that have not been done. */ - private var hasExecuted:Boolean; + protected var hasExecuted:Boolean; + /** + * @private + * Reference to the function to execute in the execute() function + */ private var doFunction:Function; + + /** + * @private + * Reference to the undo function to execute in the undo() function + */ private var undoFunction:Function; + private var _eventDispatcher:IEventDispatcher; + + private static const EXECUTE:String = "doExecuteCommand"; + /** * Creates a new UndoableCommand * @param doFunction the function to execute * @param undoFunction execute this function to undo the operations of doFunction * @param autoExecute automatically executes this command on creation. Be careful when setting this false */ - public function UndoableCommand(autoExecute:Boolean = true, doFunction:Function = null, undoFunction:Function = null) { + public function UndoableCommand(doFunction:Function = null, undoFunction:Function = null) { // set function defaults if (doFunction is Function) { this.doFunction = doFunction; @@ -34,9 +48,6 @@ package org.robotlegs.utilities.undoablecommand } else { this.undoFunction = undoExecute; } - if (autoExecute) { - this.execute(); - } } /** @@ -47,9 +58,10 @@ package org.robotlegs.utilities.undoablecommand * Will not execute more than once without first undoing */ public final function execute():void { - if (!hasExecuted) { - hasExecuted = true; + if (!hasExecuted) { doFunction(); + hasExecuted = true; + eventDispatcher.dispatchEvent(new CommandEvent(CommandEvent.EXECUTE_COMPLETE, this)); } } @@ -62,13 +74,14 @@ package org.robotlegs.utilities.undoablecommand */ public final function undo():void { if (hasExecuted) { - hasExecuted = false; undoFunction(); + hasExecuted = false; + eventDispatcher.dispatchEvent(new CommandEvent(CommandEvent.UNDO_EXECUTE_COMPLETE, this)); } } /** - * Subclasses must override this function. + * Subclasses must override this function */ protected function doExecute():void { throw new Error("Cannot call doExecute on super. " + @@ -82,5 +95,14 @@ package org.robotlegs.utilities.undoablecommand throw new Error("Cannot call undoExecute on super. " + "Subclasses must override undoExecute"); } + + [Inject] + public function get eventDispatcher():IEventDispatcher { + return _eventDispatcher; + } + + public function set eventDispatcher(value:IEventDispatcher):void { + _eventDispatcher = value; + } } } \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/commands/StepBackwardCommand.as b/src/org/robotlegs/utilities/undoablecommand/commands/StepBackwardCommand.as new file mode 100644 index 0000000..c48f46e --- /dev/null +++ b/src/org/robotlegs/utilities/undoablecommand/commands/StepBackwardCommand.as @@ -0,0 +1,22 @@ +package org.robotlegs.utilities.undoablecommand.commands +{ + import org.robotlegs.utilities.undoablecommand.CommandHistory; + + + /** + * Map this command to HistoryEvent.STEP_BACKWARD to trigger an undo action. + */ + public class StepBackwardCommand + { + [Inject] + public var commandHistory:CommandHistory; + + public function StepBackwardCommand() { + + } + + public function execute():void { + commandHistory.stepBackward(); + } + } +} \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/commands/StepForwardCommand.as b/src/org/robotlegs/utilities/undoablecommand/commands/StepForwardCommand.as new file mode 100644 index 0000000..552dfaa --- /dev/null +++ b/src/org/robotlegs/utilities/undoablecommand/commands/StepForwardCommand.as @@ -0,0 +1,21 @@ +package org.robotlegs.utilities.undoablecommand.commands +{ + import org.robotlegs.utilities.undoablecommand.CommandHistory; + + /** + * Map this command to HistoryEvent.STEP_FORWARD to trigger a redo action. + */ + public class StepForwardCommand + { + [Inject] + public var commandHistory:CommandHistory; + + public function StepForwardCommand() { + + } + + public function execute():void { + commandHistory.stepForward(); + } + } +} \ No newline at end of file diff --git a/src/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.as b/src/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.as index 318449a..265ffb2 100644 --- a/src/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.as +++ b/src/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.as @@ -1,8 +1,11 @@ package org.robotlegs.utilities.undoablecommand.interfaces { + import flash.events.IEventDispatcher; public interface IUndoableCommand { function execute():void; function undo():void; + function get eventDispatcher():IEventDispatcher; + function set eventDispatcher(value:IEventDispatcher):void; } } \ No newline at end of file diff --git a/src/tests/MockManagedUndoableCommand.as b/src/tests/MockManagedUndoableCommand.as index 70f3b2b..e7ad9ce 100644 --- a/src/tests/MockManagedUndoableCommand.as +++ b/src/tests/MockManagedUndoableCommand.as @@ -13,10 +13,6 @@ package tests */ public var testArray:Array; - public function MockManagedUndoableCommand() { - super(false, null, null); - } - /** * Cause change to the array */ @@ -35,6 +31,7 @@ package tests // pop() isn't the best undo is it // will do for now this.testArray.pop(); + } } } \ No newline at end of file diff --git a/src/tests/MockUndoableCommand.as b/src/tests/MockUndoableCommand.as index 6d6e247..d38958e 100644 --- a/src/tests/MockUndoableCommand.as +++ b/src/tests/MockUndoableCommand.as @@ -1,5 +1,7 @@ package tests { + import flash.events.EventDispatcher; + import org.robotlegs.utilities.undoablecommand.UndoableCommand; import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand; @@ -11,13 +13,15 @@ package tests static public var testArray:Array; public function MockUndoableCommand() { - super(false, null, null); + eventDispatcher = new EventDispatcher(); + super(null, null); } /** * Cause damage to the array */ override protected function doExecute():void { + trace("Do"); MockUndoableCommand.testArray.push(new Object()); } diff --git a/src/tests/TestHistory.as b/src/tests/TestHistory.as index 2167009..f58f840 100644 --- a/src/tests/TestHistory.as +++ b/src/tests/TestHistory.as @@ -1,5 +1,7 @@ package tests { + import flash.events.EventDispatcher; + import flexunit.framework.Assert; import org.robotlegs.utilities.undoablecommand.CommandHistory; @@ -16,6 +18,7 @@ package tests [Before] public function setupTests():void { _testHistory = new CommandHistory(); + _testHistory.eventDispatcher = new EventDispatcher(); testArray = new Array(); MockUndoableCommand.testArray = testArray; @@ -117,6 +120,7 @@ package tests // Test both return values of canStepBackward verifying // multiple calls work public function testCanStepBackward():void { + Assert.assertFalse(_testHistory.canStepBackward); _testHistory.push(new MockUndoableCommand()); Assert.assertTrue(_testHistory.canStepBackward); _testHistory.push(new MockUndoableCommand()); @@ -135,6 +139,7 @@ package tests // Test both return values of canStepForward, verifying // multiple calls work public function testCanStepForward():void { + Assert.assertFalse(_testHistory.canStepForward); _testHistory.push(new MockUndoableCommand()); _testHistory.push(new MockUndoableCommand()); _testHistory.push(new MockUndoableCommand()); @@ -265,6 +270,8 @@ package tests // Test forward/back/position settings while // moving backwards & forwards public function testGetCurrentCommand():void { + Assert.assertNull(_testHistory.currentCommand); + Assert.assertEquals(0, _testHistory.currentPosition); var appleCommand:MockUndoableCommand = new MockUndoableCommand(); var bananaCommand:MockUndoableCommand = new MockUndoableCommand(); var pineappleCommand:MockUndoableCommand = new MockUndoableCommand(); @@ -273,6 +280,8 @@ package tests _testHistory.push(bananaCommand); _testHistory.push(pineappleCommand); + + Assert.assertEquals(pineappleCommand, _testHistory.currentCommand); _testHistory.stepBackward(); Assert.assertEquals(bananaCommand, _testHistory.currentCommand); @@ -282,5 +291,7 @@ package tests _testHistory.stepBackward(); Assert.assertEquals(appleCommand, _testHistory.currentCommand); } + + } } \ No newline at end of file diff --git a/src/tests/TestHistoryEvents.as b/src/tests/TestHistoryEvents.as new file mode 100644 index 0000000..02bea7d --- /dev/null +++ b/src/tests/TestHistoryEvents.as @@ -0,0 +1,95 @@ +package tests +{ + import flash.events.EventDispatcher; + + import flexunit.framework.Assert; + + import org.flexunit.async.Async; + import org.robotlegs.utilities.undoablecommand.CommandHistory; + import org.robotlegs.utilities.undoablecommand.HistoryEvent; + + public class TestHistoryEvents + { + // Reference declaration for class to test + private var history:CommandHistory; + private var eventBus:EventDispatcher; + private var bananaCommand:MockUndoableCommand; + private var appleCommand:MockUndoableCommand; + private var pineappleCommand:MockUndoableCommand; + + public function TestHistoryEvents() + { + } + + [Before] + public function setupTests():void { + history = new CommandHistory(); + eventBus = new EventDispatcher() + history.eventDispatcher = eventBus; + MockUndoableCommand.testArray = new Array() + bananaCommand = new MockUndoableCommand(); + bananaCommand.eventDispatcher = eventBus; + appleCommand = new MockUndoableCommand(); + appleCommand.eventDispatcher = eventBus; + pineappleCommand = new MockUndoableCommand(); + pineappleCommand.eventDispatcher = eventBus; + } + + [After] + public function reset():void { + history = null; + eventBus = null; + MockUndoableCommand.testArray = null; + } + + + [Test(async)] + public function testFastForwardEvents():void { + Async.handleEvent(this, history.eventDispatcher, HistoryEvent.FAST_FORWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void { + Assert.assertStrictlyEquals(pineappleCommand, event.command) + }); + + history.push(bananaCommand); + history.push(appleCommand); + history.push(pineappleCommand); + history.rewind(); + history.fastForward(); + } + + [Test(async)] + public function testRewindEvents():void { + Async.handleEvent(this, history.eventDispatcher, HistoryEvent.REWIND_COMPLETE, function(event:HistoryEvent, passThrough:Object):void { + Assert.assertNull(event.command); + + }); + + history.push(bananaCommand); + history.push(appleCommand); + history.push(pineappleCommand); + history.rewind(); + } + + [Test(async)] + public function testStepBackwardEvents():void { + Async.handleEvent(this, history.eventDispatcher, HistoryEvent.STEP_BACKWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void { + Assert.assertStrictlyEquals(pineappleCommand, event.command); + }); + + history.push(bananaCommand); + history.push(appleCommand); + history.push(pineappleCommand); + history.stepBackward(); + } + + [Test(async)] + public function testStepForwardEvents():void + { + Async.handleEvent(this, history.eventDispatcher, HistoryEvent.STEP_FORWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void { + Assert.assertStrictlyEquals(bananaCommand, event.command); + + }); + + history.push(bananaCommand); + } + } +} \ No newline at end of file diff --git a/src/tests/TestManagedUndoableCommand.as b/src/tests/TestManagedUndoableCommand.as index cafc327..779f662 100644 --- a/src/tests/TestManagedUndoableCommand.as +++ b/src/tests/TestManagedUndoableCommand.as @@ -1,11 +1,12 @@ package tests { import flash.display.DisplayObjectContainer; + import flash.events.EventDispatcher; import flexunit.framework.Assert; import org.robotlegs.utilities.undoablecommand.CommandHistory; - import org.swiftsuspenders.Injector; + /** * @private @@ -21,16 +22,26 @@ package tests private var testArray:Array; private var contextView:DisplayObjectContainer; - private var injector:Injector; + private var history:CommandHistory; + private var eventBus:EventDispatcher; [Before] public function setupTests():void { + eventBus = new EventDispatcher(); testArray = new Array(); history = new CommandHistory(); - _managedUndoableCommand = new MockManagedUndoableCommand(); - _managedUndoableCommand.testArray = testArray; - _managedUndoableCommand.history = history; + history.eventDispatcher = eventBus; + _managedUndoableCommand = createCommand(); + } + + private function createCommand():MockManagedUndoableCommand { + var newCommand:MockManagedUndoableCommand = new MockManagedUndoableCommand(); + newCommand.testArray = testArray; + newCommand.history = history; + newCommand.eventDispatcher = eventBus; + + return newCommand; } [After] @@ -38,7 +49,6 @@ package tests testArray = null; _managedUndoableCommand = null; history = null; - } [Test] @@ -49,16 +59,17 @@ package tests [Test] public function testExecute():void { + Assert.assertEquals(0, testArray.length); _managedUndoableCommand.execute(); - Assert.assertEquals(testArray.length, 1); + Assert.assertEquals(1, testArray.length); } [Test] public function testUndo():void { _managedUndoableCommand.execute(); - Assert.assertEquals(testArray.length, 1); + Assert.assertEquals(1, testArray.length); _managedUndoableCommand.undo(); - Assert.assertEquals(testArray.length, 0); + Assert.assertEquals(0, testArray.length); } [Test] @@ -66,34 +77,114 @@ package tests _managedUndoableCommand.execute(); _managedUndoableCommand.execute(); _managedUndoableCommand.execute(); - Assert.assertEquals(testArray.length, 1); + Assert.assertEquals(1, testArray.length); } [Test] public function testUndoMultiple():void { _managedUndoableCommand.execute(); + Assert.assertEquals(1, testArray.length); _managedUndoableCommand.undo(); _managedUndoableCommand.undo(); _managedUndoableCommand.undo(); - Assert.assertEquals(testArray.length, 0); + Assert.assertEquals(0, testArray.length); } [Test] public function testUndoNothingToUndo():void { _managedUndoableCommand.undo(); - Assert.assertEquals(testArray.length, 0); + Assert.assertEquals(0, testArray.length); } [Test] public function testDefaultFunctions():void { - var command:MockManagedUndoableCommand = new MockManagedUndoableCommand();//MockManagedUndoableCommand(injector.instantiate(MockManagedUndoableCommand)); + var command:MockManagedUndoableCommand = createCommand();/// = new MockManagedUndoableCommand(); + command.testArray = testArray; command.history = history; command.execute(); - Assert.assertEquals(testArray.length, 1); + Assert.assertEquals(1, testArray.length); command.undo(); - Assert.assertEquals(testArray.length, 0); + Assert.assertEquals(0, testArray.length); + } + + [Test] + // Test forward/back/position settings while + // moving backwards & forwards + public function testGetCurrentCommand():void { + Assert.assertNull(history.currentCommand); + Assert.assertEquals(0, history.currentPosition); + + var appleCommand:MockManagedUndoableCommand = createCommand(); + + var bananaCommand:MockManagedUndoableCommand = createCommand(); + + var pineappleCommand:MockManagedUndoableCommand = createCommand(); + + /*_testHistory.push(appleCommand); + _testHistory.push(bananaCommand); + _testHistory.push(pineappleCommand);*/ + + appleCommand.execute(); + bananaCommand.execute(); + pineappleCommand.execute(); + + Assert.assertEquals(pineappleCommand, history.currentCommand); + history.stepBackward(); + Assert.assertEquals(bananaCommand, history.currentCommand); + history.stepForward(); + Assert.assertEquals(pineappleCommand, history.currentCommand); + history.stepBackward(); + history.stepBackward(); + Assert.assertEquals(appleCommand, history.currentCommand); + } + + [Test] + // Test forward/back/position settings while + // moving backwards & forwards + public function testAddToHistory():void { + var appleCommand:MockManagedUndoableCommand = createCommand(); + Assert.assertNull(history.currentCommand); + appleCommand.execute(); + Assert.assertEquals(appleCommand, history.currentCommand); + Assert.assertEquals(1, testArray.length); + } + + [Test] + // Test forward/back/position settings while + // moving backwards & forwards + public function testCommandUndo():void { + var appleCommand:MockManagedUndoableCommand = createCommand(); + Assert.assertNull(history.currentCommand); + appleCommand.execute(); + + Assert.assertEquals(appleCommand, history.currentCommand); + Assert.assertEquals(1, testArray.length); + appleCommand.undo(); + Assert.assertNull(history.currentCommand); + Assert.assertEquals(0, testArray.length); + + } + + [Test] + // Test forward/back/position settings while + // moving backwards & forwards + public function testHistoryUndo():void { + var appleCommand:MockManagedUndoableCommand = createCommand(); + Assert.assertNull(history.currentCommand); + appleCommand.execute(); + Assert.assertEquals(1, testArray.length); + Assert.assertEquals(appleCommand, history.currentCommand); + Assert.assertEquals(1, history.currentPosition); + history.stepBackward(); + Assert.assertEquals(0, history.currentPosition); + Assert.assertNull(history.currentCommand); + Assert.assertEquals(0, testArray.length); + history.stepForward(); + Assert.assertEquals(1, testArray.length); + Assert.assertEquals(appleCommand, history.currentCommand); + Assert.assertEquals(1, history.currentPosition); } } } \ No newline at end of file diff --git a/src/tests/TestUndoableCommand.as b/src/tests/TestUndoableCommand.as index 6f9e201..3a92165 100644 --- a/src/tests/TestUndoableCommand.as +++ b/src/tests/TestUndoableCommand.as @@ -1,5 +1,7 @@ package tests { + import flash.events.EventDispatcher; + import flexunit.framework.Assert; import org.robotlegs.utilities.undoablecommand.UndoableCommand; @@ -10,10 +12,11 @@ package tests public class TestUndoableCommand { // Reference declaration for class to test - private var _undoableCommand : UndoableCommand; + private var _undoableCommand:UndoableCommand; + private var testArray:Array; private var testObject:Object; - + private var eventBus:EventDispatcher; private function doStuff():void { testArray.push(testObject); @@ -26,14 +29,16 @@ package tests [Before] public function setupTests():void { testArray = new Array(); - _undoableCommand = new UndoableCommand(false, doStuff, undoStuff); + eventBus = new EventDispatcher(); + _undoableCommand = new UndoableCommand(doStuff, undoStuff); + _undoableCommand.eventDispatcher = eventBus; } [After] public function reset():void { _undoableCommand = null; testArray = null; - + eventBus = null; } [Test] @@ -61,8 +66,7 @@ package tests _undoableCommand.execute(); _undoableCommand.execute(); _undoableCommand.execute(); - Assert.assertEquals(testArray.length, 1); - + Assert.assertEquals(testArray.length, 1); } [Test]