http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/CreateBitmapReferences.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/CreateBitmapReferences.as 
b/mustella/src/main/flex/CreateBitmapReferences.as
new file mode 100644
index 0000000..0e755a6
--- /dev/null
+++ b/mustella/src/main/flex/CreateBitmapReferences.as
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+import flash.display.DisplayObject;
+
+[Mixin]
+/**
+ *  A "marker" class that causes test scripts to write out
+ *  bitmaps to the urls instead of reading and comparing
+ *  so that baselines/reference-points can be created for
+ *  future comparing.
+ */
+public class CreateBitmapReferences 
+{
+
+       /**
+        *  Mixin callback that gets everything ready to go.
+        *  The UnitTester waits for an event before starting
+        */
+       public static function init(root:DisplayObject):void
+       {
+               UnitTester.createBitmapReferences = true;
+               /// change this to suit your locally running server: 
+               UnitTester.bitmapServerPrefix = 
"http://localhost:9998/baselines/baseline.jsp?filename=";;
+       }
+
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DesktopMacSettings.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DesktopMacSettings.as 
b/mustella/src/main/flex/DesktopMacSettings.as
new file mode 100644
index 0000000..5cbe37c
--- /dev/null
+++ b/mustella/src/main/flex/DesktopMacSettings.as
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+import flash.display.DisplayObject;
+
+[Mixin]
+/**
+ * By including this mixin via CompileMustellaSwfs, we
+ * can set up some variables for UnitTester to use for
+ * a run of mobile tests on the desktop.
+ */
+public class DesktopMacSettings
+{
+       public static function init(root:DisplayObject):void
+       {
+               if( UnitTester.cv == null ){
+                       UnitTester.cv = new ConditionalValue();
+               }
+       
+               UnitTester.cv.os = "mac";
+       }
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DesktopWinSettings.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DesktopWinSettings.as 
b/mustella/src/main/flex/DesktopWinSettings.as
new file mode 100644
index 0000000..fe2c0e2
--- /dev/null
+++ b/mustella/src/main/flex/DesktopWinSettings.as
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+import flash.display.DisplayObject;
+
+[Mixin]
+/**
+ * By including this mixin via CompileMustellaSwfs, we
+ * can set up some variables for UnitTester to use for
+ * a run of mobile tests on the desktop.
+ */
+public class DesktopWinSettings
+{
+       public static function init(root:DisplayObject):void
+       {
+               if( UnitTester.cv == null ){
+                       UnitTester.cv = new ConditionalValue();
+               }
+               
+               UnitTester.cv.os = "win";
+       }
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DeviceNames.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DeviceNames.as 
b/mustella/src/main/flex/DeviceNames.as
new file mode 100644
index 0000000..c27d865
--- /dev/null
+++ b/mustella/src/main/flex/DeviceNames.as
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+COMPILE::SWF
+{
+import flash.display.DisplayObject;
+import flash.system.Capabilities;
+}
+
+/**
+ *  allowed values for deviceName
+ *
+ */
+public class DeviceNames
+{
+       public static const ANDROID:String = "android";
+       public static const IOS:String = "ios";
+       public static const QNX:String = "qnx";
+       public static const MAC:String = "mac";
+       public static const WIN:String = "win";
+
+       public static const OS_VALUES:Array = [WIN, MAC, ANDROID, IOS, QNX];
+       public static const OS_VERSION_VALUES:Array = ["android22", 
"android23", "android3", "android4", "ios3x", "ios40", "ios41", "ios5", "ios6"];
+       public static const DEVICE_VALUES:Array = 
["air","desire","droid","droid2","droidX","evo","incredible","nexusOne","playbook"];
+
+       public static function getFromOS ():String 
+       {
+        COMPILE::SWF
+        {
+               if (Capabilities.os.substring (0, Capabilities.os.indexOf (" 
")) == "Windows" )
+               {
+                       return WIN;
+               } else if (Capabilities.os.substring (0, 
Capabilities.os.indexOf (" ")) == "Mac" )
+               {
+                       return MAC;
+
+               } else 
+               {
+                       return "";
+               }
+        }
+        COMPILE::JS
+        {
+            return "";
+        }
+       }
+
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DispatchEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DispatchEvent.as 
b/mustella/src/main/flex/DispatchEvent.as
new file mode 100644
index 0000000..b2b4e97
--- /dev/null
+++ b/mustella/src/main/flex/DispatchEvent.as
@@ -0,0 +1,141 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+COMPILE::SWF
+{
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.InteractiveObject;
+import flash.events.Event;
+import flash.system.ApplicationDomain;
+}
+
+/**
+ *  The test step that fakes an event.  Do not use for MouseEvent or 
KeyboardEvent
+ *  MXML attributes:
+ *  bubbles
+ *  cancelable
+ *  eventClass
+ *  properties
+ *  target
+ *  type
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional);
+ */
+public class DispatchEvent extends TestStep
+{
+
+       /**
+        *  @private
+        */
+    COMPILE::SWF
+       override public function execute(root:Object, context:UnitTester, 
testCase:TestCase, testResult:TestResult):Boolean
+       {
+               if (waitEvent && waitTarget == null)
+                       waitTarget = target;
+               return super.execute(root, context, testCase, testResult);
+       }
+
+       /**
+        *  Set the target's property to the specified value
+        */
+    COMPILE::SWF
+       override protected function doStep():void
+       {
+               UnitTester.blockFocusEvents = false;
+
+               var actualTarget:Object = context.stringToObject(target);
+               if (!actualTarget)
+               {
+                       testResult.doFail("Target " + target + " not found");
+                       UnitTester.blockFocusEvents = false;
+                       return;
+               }
+
+               var c:Class = 
ApplicationDomain.currentDomain.getDefinition(eventClass) as Class;
+               var event:Event = new c(type, bubbles, cancelable);
+               if (properties)
+               {
+                       for (var s:String in properties)
+                       {
+                               event[s] = properties[s];
+                       }
+               }
+               try
+               {
+                       actualTarget.dispatchEvent(event);
+               }
+               catch (e2:Error)
+               {
+                       TestOutput.logResult("Exception thrown in 
DispatchEvent.");
+                       testResult.doFail (e2.getStackTrace()); 
+               }
+
+               UnitTester.blockFocusEvents = true;
+       }
+
+       /**
+        *  The qualified name of the class for the event
+        *  i.e. flash.events.Event
+        */
+       public var eventClass:String;
+
+       /**
+        *  The object that receives the mouse event
+        */
+       public var target:String;
+
+       /**
+        *  The type of the event to send (mouseUp, mouseDown, etc).
+        */
+       public var type:String;
+
+       /**
+        *  The bubbles property on the Event (optional)
+        */
+       public var bubbles:Boolean;
+
+       /**
+        *  The cancelable property on the Event (optional)
+        */
+       public var cancelable:Boolean;
+
+
+       /**
+        *  The relatedObject property on the MouseEvent (optional)
+        */
+       public var properties:Object;
+
+
+       /**
+        *  customize string representation
+        */
+       override public function toString():String
+       {
+               var s:String = "DispatchEvent: target = ";
+               s += target;
+               if (type)
+                       s += ", type = " + type;
+               return s;
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DispatchKeyEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DispatchKeyEvent.as 
b/mustella/src/main/flex/DispatchKeyEvent.as
new file mode 100644
index 0000000..a8d5b48
--- /dev/null
+++ b/mustella/src/main/flex/DispatchKeyEvent.as
@@ -0,0 +1,673 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+COMPILE::SWF
+{
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.InteractiveObject;
+import flash.events.Event;
+import flash.events.FocusEvent;
+import flash.events.KeyboardEvent;
+import flash.events.TextEvent;
+import flash.system.Capabilities;
+import flash.text.TextField;
+import flash.ui.Keyboard;
+}
+
+/**
+ *  The test step that fakes a keyboard event
+ *  MXML attributes:
+ *  type (optional)
+ *  charCode
+ *  ctrlKey (optional)
+ *  keyCode (optional)
+ *  keyLocation (optional)
+ *  shiftKey (optional)
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional);
+ *  cancelable (optional)
+ */
+
+
+public class DispatchKeyEvent extends TestStep
+{
+       // These are constants from flash.ui.Keyboard.  They are not
+       // available in non-AIR compilations, so they are reproduced here
+       // to avoid compile errors.
+       // If they change in AIR, these will need to be updated.
+       public static const FLASH_UI_KEYBOARD_BACK:uint = 0x01000016;
+       public static const FLASH_UI_KEYBOARD_MENU:uint = 0x01000012;
+       public static const FLASH_UI_KEYBOARD_SEARCH:uint = 0x0100001F;
+       
+    private var inDispatchKey:Boolean;
+    private var gotFocusIn:Boolean;
+    private var charSequence:Array;
+    private var keySequence:Array;
+    private var currentRepeat:int;
+    private var currentKey:int;
+    private var sendBoth:Boolean;
+    
+    /**
+     *  Set the target's property to the specified value
+     */
+    COMPILE::SWF
+    override protected function doStep():void
+    {
+        UnitTester.blockFocusEvents = false;
+
+        sendBoth = false;
+
+        if (!type)
+        {
+            sendBoth = true;
+            type = "keyDown";
+        }
+        
+        var i:int;
+        var n:int;
+        charSequence = new Array();
+        keySequence = new Array();
+               
+               
+        if (charCode)
+        {
+            charSequence.push(charCode);
+            keySequence.push(keyCode ? keyCode : CharCodeToKeyCode[charCode] ? 
CharCodeToKeyCode[charCode] : charCode);
+        }
+        else if (keyCode)
+        {
+            charSequence.push(KeyCodeToCharCode[keyCode] ? 
KeyCodeToCharCode[keyCode] : 0);
+            keySequence.push(keyCode);
+        }
+        else if (char)
+        {
+            n = char.length;
+            for (i = 0; i < n; i++)
+            {
+                var c:uint = char.charCodeAt(i) 
+                charSequence.push(c);
+                keySequence.push(CharCodeToKeyCode[c]);
+            }
+        }
+        else if (key || keys)
+        {
+            var sequence:Array;
+            if (key)
+                sequence = [ key ];
+            else
+                sequence = keys;
+            n = sequence.length;
+            for (i = 0; i < n; i++)
+            {
+                var kc:uint = Keyboard[sequence[i]];
+                if (kc == 0)
+                {
+                    testResult.doFail(key + " is not a valid flash.ui.Keyboard 
constant");
+                    UnitTester.blockFocusEvents = true;
+                    return;
+                }
+                keySequence.push(kc);
+                charSequence.push(KeyCodeToCharCode[kc] ? 
KeyCodeToCharCode[kc] : 0);
+            }
+        }
+        else
+        {
+            testResult.doFail("no keys specified");
+            UnitTester.blockFocusEvents = true;
+            return;
+        }
+               
+        try
+        {
+            for (i = 0; i < repeatCount; i++)
+            {
+                var m:int = charSequence.length;
+                for (var j:int = 0; j < m; j++)
+                {
+                    var event:KeyboardEvent = new KeyboardEvent(type, true, 
cancelable); // all keyboard events bubble
+                    event.ctrlKey = ctrlKey;
+                    event.shiftKey = shiftKey;
+                    event.charCode = charSequence[j];
+                    event.keyCode = keySequence[j];
+                    event.keyLocation = keyLocation;
+
+                    if (keySequence[j] == Keyboard.TAB)
+                    {
+                        // if we don't see a focusIn, focus is being set
+                        // asynchronously so we need to wait.
+                        currentRepeat = i;
+                        currentKey = j;
+                        gotFocusIn = false;
+                        root.addEventListener("focusIn", focusInHandler);
+                        inDispatchKey = true;
+                        dispatchKey(j, event);
+                        inDispatchKey = false;
+                        if (!gotFocusIn)
+                            break;
+                    }
+                    else
+                        dispatchKey(j, event);
+                }
+            }
+        }
+        catch (e1:Error)
+        {
+            TestOutput.logResult("Exception thrown in DispatchKeyEvent.");
+            testResult.doFail (e1.getStackTrace()); 
+        }
+
+        UnitTester.blockFocusEvents = true;
+    }
+
+    /**
+
+     *  (Optional) name of a UI object whose Window/Stage
+
+     *  will be used to dispatch the event
+
+     */
+
+    public var window:String;
+
+
+
+    /**
+     *  The type of the event to send (keyUp, keyDown, etc).
+     *  If not set, we'll send both a keyDown and a keyUp
+     */
+    public var type:String;
+
+    /**
+     *  The char or sequence of chars to send as a string/char if you don't 
know the charCode (optional)
+     */
+    public var char:String;
+
+    /**
+     *  The charCode property on the KeyboardEvent (optional)
+     */
+    public var charCode:uint;
+
+    /**
+     *  The ctrlKey property on the KeyboardEvent (optional)
+     */
+    public var ctrlKey:Boolean;
+
+    /**
+     *  The Keyboard key if you don't know the keyCode (optional)
+     */
+    public var key:String;
+
+    /**
+     *  The sequence of keys (optional) e.g ["LEFT", "UP"]
+     */
+    public var keys:Array;
+
+    /**
+     *  The keyCode property on the KeyboardEvent (optional)
+     */
+    public var keyCode:uint;
+
+    /**
+     *  The keyLocation property on the KeyboardEvent (optional)
+     */
+    public var keyLocation:uint;
+
+    /**
+     *  The number of times to repeat the sequence (optional)
+     */
+    public var repeatCount:uint = 1;
+
+    /**
+     *  The shiftKey property on the KeyboardEvent (optional)
+     */
+    public var shiftKey:Boolean;
+
+    /**
+     *  Designate the created event to be cancelable. by default, they are not
+     */
+    public var cancelable:Boolean = false;
+
+    /**
+     *  The FlashPlayer TextField doesn't actually handle keyboard events so 
we have to
+     *  emulate them
+     */
+    COMPILE::SWF
+    private function emulateKey(actualTarget:Object, event:KeyboardEvent):void
+    {
+        var begin:int = actualTarget.selectionBeginIndex;
+        var end:int = actualTarget.selectionEndIndex;
+        var caret:int = actualTarget.caretIndex;
+        // trace("begin =", begin, "end =", end, "caret =", caret);
+
+        if (event.keyCode == Keyboard.LEFT)
+        {
+            if (event.shiftKey)
+            {
+                if (caret > 0)
+                {
+                    if (caret == begin)
+                    {
+                        begin--;
+                        // last param defines caret position
+                        actualTarget.setSelection(end, begin);
+                    }
+                    else if (caret == end)
+                    {
+                        end--;
+                        if (end < begin)
+                            begin = end;
+                                   actualTarget.setSelection(end, begin);
+
+                    }
+                }
+            }
+            else
+            {
+                if (begin != end)
+                    actualTarget.setSelection(begin, begin);
+
+               else if (caret > 0)
+                    actualTarget.setSelection(caret - 1, caret - 1);
+            }
+        }
+        else if (event.keyCode == Keyboard.RIGHT)
+        {
+            if (event.shiftKey)
+            {
+                if (caret < actualTarget.length)
+                {
+                    if (caret == end)
+                    {
+                        end++;
+                        actualTarget.setSelection(begin, end);
+
+                    }
+                    else if (caret == begin)
+                    {
+                        begin++;
+                        if (end < begin)
+                            end = begin;
+                        // last param defines caret position
+                        actualTarget.setSelection(end, begin);
+
+                    }
+                }
+            }
+            else
+            {
+                if (begin != end)
+                    actualTarget.setSelection(end, end); 
+
+               else if (caret > 0)
+                    actualTarget.setSelection(caret + 1, caret + 1);
+            }
+        }
+    }
+
+    COMPILE::SWF
+    private function focusInHandler(focusEvent:Event):void
+    {
+        gotFocusIn = true;        
+        root.removeEventListener("focusIn", focusInHandler);
+        if (inDispatchKey)
+            return;
+        
+        for (var i:int = currentRepeat; i < repeatCount; i++)
+        {
+            var m:int = charSequence.length;
+            for (var j:int = currentKey + 1; j < m; j++)
+            {
+                var event:KeyboardEvent = new KeyboardEvent(type, true, 
cancelable); // all keyboard events bubble
+                event.ctrlKey = ctrlKey;
+                event.shiftKey = shiftKey;
+                event.charCode = charSequence[j];
+                event.keyCode = keySequence[j];
+                event.keyLocation = keyLocation;
+                
+                if (keySequence[j] == Keyboard.TAB)
+                {
+                    currentRepeat = i;
+                    currentKey = j;
+                    gotFocusIn = false;
+                    root.addEventListener("focusIn", focusInHandler);
+                    inDispatchKey = true;
+                    dispatchKey(j, event);
+                    inDispatchKey = false;
+                    if (!gotFocusIn)
+                        break;
+                }                
+                else
+                    dispatchKey(j, event);
+            }
+        }
+    }
+    
+    COMPILE::SWF
+    private function dispatchKey(index:int, event:KeyboardEvent):void
+    {
+        // note that we don't check Window activation since we want to run in 
the background
+        // and window activation is a player function
+        
+        var actualTarget:Object;
+        
+        if (window)
+        {
+            actualTarget = context.stringToObject(window);
+            actualTarget = actualTarget.stage.focus;
+        }
+        else
+        {
+            actualTarget = root.stage.focus;
+            if (!actualTarget)
+            {
+                actualTarget = UnitTester.getFocus();
+            }
+        }
+        
+        // BACK, MENU, and SEARCH are buttons on mobile (Android) devices.  
+        // On Android devices right now, actualTarget is still null at this 
point.  Dispatching the event to the stage works.
+        // Using the constants in flash.ui.Keyboard will cause an error in a 
non-AIR runs, so the constants are also defined
+        // in this file, above.  There is risk here.
+        if (keySequence[index] == FLASH_UI_KEYBOARD_BACK ||
+            keySequence[index] == FLASH_UI_KEYBOARD_MENU ||
+            keySequence[index] == FLASH_UI_KEYBOARD_SEARCH){
+            
+            actualTarget = root.stage;
+        }
+        
+        if (actualTarget)
+        {
+            var targetType:TypeInfo = context.getTypeInfo(actualTarget);
+            var isTextView:Boolean = 
targetType.isAssignableTo("spark.components::RichEditableText");
+            
+            
+            if (actualTarget is TextField)
+            {
+                if (event.charCode)
+                {
+                    if (actualTarget.type == "input")
+                    {
+                        
actualTarget.replaceSelectedText(String.fromCharCode(event.charCode));
+                        // actualTarget.dispatchEvent(new Event("change", 
true));
+                        actualTarget.dispatchEvent(new Event("change"));
+                    }
+                }
+                else
+                {
+                    if (actualTarget.selectable)
+                        emulateKey(actualTarget, event);
+                }
+            }
+            
+            actualTarget.dispatchEvent(event);
+            
+            
+            
+            if (isTextView)
+            {
+                if (event.keyCode == Keyboard.DELETE ||
+                    
+                    event.keyCode == Keyboard.BACKSPACE ||
+                    
+                    event.keyCode == Keyboard.INSERT ||
+                    
+                    ctrlKey)
+                    
+                {
+                    
+                    // don't send TEXT_INPUT event
+                    
+                }
+                    
+                else
+                    
+                {
+                    
+                    var textEvent:TextEvent = new 
TextEvent(TextEvent.TEXT_INPUT, true, true);
+                    
+                    textEvent.text = String.fromCharCode(charSequence[index]);
+                    
+                    actualTarget.dispatchEvent(textEvent);
+                    
+                }
+                
+            }
+            
+            if (keySequence[index] == Keyboard.TAB && type == "keyDown")
+            {
+                var fm:Object;
+                var newTarget:Object = actualTarget;
+                while (!fm && newTarget)
+                {   
+                    if ("focusManager" in newTarget)
+                        fm = newTarget["focusManager"];
+                    newTarget = newTarget.parent;
+                }
+                newTarget = null;
+                if (fm)
+                {
+                    try
+                    {
+                        newTarget = fm.getNextFocusManagerComponent(shiftKey);
+                    }
+                    catch (e:Error)
+                    {
+                        // ignore error thrown here.  Should only throw if the
+                        // current FM became inactive as a result of 
dispatching
+                        // the key event.  We don't really care too much about
+                        // getting an accurate newTarget in this case because
+                        // newTarget is often wrong since the Player is 
offering
+                        // it up and the Player has that wonky algorithm for
+                        // determining newTarget.   In theory, none of our code
+                        // truly cares as long as it doesn't point to old focus
+                        // object.
+                    }
+                }
+                
+                actualTarget.dispatchEvent(new 
FocusEvent(FocusEvent.KEY_FOCUS_CHANGE, true, true, 
InteractiveObject(newTarget), shiftKey, Keyboard.TAB));
+            }
+            
+            if (sendBoth)
+            {
+                event = new KeyboardEvent("keyUp", true, cancelable);
+                event.ctrlKey = ctrlKey;
+                event.shiftKey = shiftKey;
+                event.charCode = charSequence[index];
+                event.keyCode = keySequence[index];
+                event.keyLocation = keyLocation;
+                actualTarget.dispatchEvent(event);
+            }
+        }
+        else
+        {
+            if (keySequence[index] == Keyboard.TAB && type == "keyDown")
+            {
+                
+                var thisRoot:DisplayObject
+                
+                // note that we don't check Window activation since we want to 
run in the background
+                // and window activation is a player function
+                if (window)
+                {
+                    thisRoot = context.stringToObject(window).root;
+                }
+                else
+                    thisRoot = root;
+                try
+                {
+                    thisRoot.stage.dispatchEvent(new 
FocusEvent(FocusEvent.KEY_FOCUS_CHANGE, true, true, 
InteractiveObject(actualTarget), shiftKey, Keyboard.TAB));
+                }
+                catch(se2:SecurityError)
+                {
+                    thisRoot.dispatchEvent(new 
FocusEvent(FocusEvent.KEY_FOCUS_CHANGE, true, true, 
InteractiveObject(actualTarget), shiftKey, Keyboard.TAB));
+                }
+            }                                          
+            
+        }
+    }
+    
+    private var KeyCodeToCharCode:Object = {
+                    8: 8,
+                    13: 13,
+                    96: 48,
+                    97: 49,
+                    98: 50,
+                    99: 51,
+                    100: 52, 
+                    101: 53,
+                    102: 54,
+                    103: 55,
+                    104: 56,
+                    105: 57,
+                    106: 42,
+                    107: 43,
+                    109: 45,
+                    110: 46,
+                    111: 47
+    }
+
+    private var CharCodeToKeyCode:Object = {
+                    13: 13,
+                    33: 49,
+                    34: 222,
+                    35: 51,
+                    36: 52,
+                    37: 53,
+                    38: 55,
+                    39: 222,
+                    40: 57,
+                    41: 48,
+                    42: 56,
+                    43: 187,
+                    44: 188,
+                    45: 189,
+                    46: 190,
+                    47: 191,
+                    48: 48,
+                    49: 49,
+                    50: 50,
+                    51: 51,
+                    52: 52,
+                    53: 53,
+                    54: 54,
+                    55: 55,
+                    56: 56,
+                    57: 57,
+                    58: 186,
+                    59: 186,
+                    60: 188,
+                    61: 187,
+                    62: 190,
+                    63: 191,
+                    64: 50,
+                    65: 65,
+                    66: 66,
+                    67: 67,
+                    68: 68,
+                    69: 69,
+                    70: 70,
+                    71: 71,
+                    72: 72,
+                    73: 73,
+                    74: 74,
+                    75: 75,
+                    76: 76,
+                    77: 77,
+                    78: 78,
+                    79: 79,
+                    80: 80,
+                    81: 81,
+                    82: 82,
+                    83: 83,
+                    84: 84,
+                    85: 85,
+                    86: 86,
+                    87: 87,
+                    88: 88,
+                    89: 89,
+                    90: 90,
+                    91: 219,
+                    92: 220,
+                    93: 221,
+                    94: 54,
+                    95: 189,
+                    96: 192,
+                    97: 65,
+                    98: 66,
+                    99: 67,
+                    100: 68,
+                    101: 69,
+                    102: 70,
+                    103: 71,
+                    104: 72,
+                    105: 73,
+                    106: 74,
+                    107: 75,
+                    108: 76,
+                    109: 77,
+                    110: 78,
+                    111: 79,
+                    112: 80,
+                    113: 81,
+                    114: 82,
+                    115: 83,
+                    116: 84,
+                    117: 85,
+                    118: 86,
+                    119: 87,
+                    120: 88,
+                    121: 89,
+                    122: 90,
+                    123: 219,
+                    124: 220,
+                    125: 221,
+                    126: 192
+    }
+
+    /**
+     *  customize string representation
+     */
+    override public function toString():String
+    {
+        var s:String = "DispatchKeyEvent";
+        if (charCode)
+            s += ": charCode = " + charCode.toString();
+        if (keyCode)
+            s += ": keyCode = " + keyCode.toString();
+        if (char)
+            s += ": char = " + char;
+        if (key)
+            s += ": key = " + key;
+        if (keys)
+            s += ": keys = " + keys.toString();
+        if (type)
+            s += ", type = " + type;
+        if (shiftKey)
+            s += ", shiftKey = " + shiftKey.toString();
+        if (ctrlKey)
+            s += ", ctrlKey = " + ctrlKey.toString();
+        if (repeatCount)
+            s += ", repeatCount = " + repeatCount.toString();
+        return s;
+    }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DispatchMouseClickEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DispatchMouseClickEvent.as 
b/mustella/src/main/flex/DispatchMouseClickEvent.as
new file mode 100644
index 0000000..3f4574a
--- /dev/null
+++ b/mustella/src/main/flex/DispatchMouseClickEvent.as
@@ -0,0 +1,298 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+COMPILE::SWF
+{
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.InteractiveObject;
+import flash.events.MouseEvent;
+import flash.geom.Point;
+import flash.text.TextField;
+}
+
+/**
+ *  The test step that fakes a mouse event
+ *  MXML attributes:
+ *  target
+ *  type
+ *  ctrlKey (optional)
+ *  delta (optional)
+ *  localX
+ *  localY
+ *  relatedObject (optional)
+ *  shiftKey (optional)
+ *  stageX
+ *  stageY
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional);
+ */
+public class DispatchMouseClickEvent extends TestStep
+{
+
+       /**
+        *  @private
+        */
+    COMPILE::SWF
+       override public function execute(root:Object, context:UnitTester, 
testCase:TestCase, testResult:TestResult):Boolean
+       {
+               if (waitEvent && waitTarget == null)
+                       waitTarget = target;
+               return super.execute(root, context, testCase, testResult);
+       }
+
+       /**
+        *  Set the target's property to the specified value
+        */
+    COMPILE::SWF
+       override protected function doStep():void
+       {
+               UnitTester.blockFocusEvents = false;
+
+               var actualTarget:Object = context.stringToObject(target);
+               if (!actualTarget)
+               {
+                       testResult.doFail("Target " + target + " not found");
+                       UnitTester.blockFocusEvents = true;
+                       return;
+               }
+               dispatchMouseEvent(actualTarget, "mouseDown");
+               dispatchMouseEvent(actualTarget, "mouseUp");
+               dispatchMouseEvent(actualTarget, "click");
+
+               UnitTester.blockFocusEvents = true;
+
+       }
+
+    COMPILE::SWF
+       private function dispatchMouseEvent(actualTarget:Object, 
type:String):void
+       {
+               var event:MouseEvent = new MouseEvent(type, true); // all mouse 
events bubble
+               event.ctrlKey = ctrlKey;
+               event.shiftKey = shiftKey;
+               event.buttonDown = type == "mouseDown";
+               event.delta = delta;
+               if (relatedObject && relatedObject.length > 0)
+               {
+                       event.relatedObject = 
InteractiveObject(context.stringToObject(relatedObject));
+               }
+               
+               var stagePt:Point;
+               if (!isNaN(localX) && !isNaN(localY))
+               {
+                       stagePt = actualTarget.localToGlobal(new Point(localX, 
localY));
+               }
+               else if (!isNaN(stageX) && !isNaN(stageY))
+               {
+                       stagePt = new Point(stageX, stageY);
+               }
+               else
+               {
+                       stagePt = actualTarget.localToGlobal(new Point(0, 0));
+               }
+        try {
+            root[mouseX] = stagePt.x;
+            root[mouseY] = stagePt.y;
+            UnitTester.setMouseXY(stagePt);
+            if (root["topLevelSystemManager"] != root)
+            {
+                root["topLevelSystemManager"][mouseX] = stagePt.x;
+                root["topLevelSystemManager"][mouseY] = stagePt.y;
+            }            
+        } catch (e:Error) {} // some scenarios don't support this
+
+               if (actualTarget is DisplayObjectContainer)
+               {
+                       var targets:Array = 
actualTarget.stage.getObjectsUnderPoint(stagePt);
+                       var arr:Array = 
UnitTester.getObjectsUnderPoint(DisplayObject(actualTarget), stagePt);
+                       targets = targets.concat(arr);
+
+                       for (var i:int = targets.length - 1; i >= 0; i--)
+                       {
+                               if (targets[i] is InteractiveObject)
+                               {
+                                       if (targets[i] is TextField && 
!targets[i].selectable)
+                                       {
+                                               actualTarget = 
targets[i].parent;
+                                               break;
+                                       }
+
+                                       if 
(isMouseTarget(InteractiveObject(targets[i])))
+                                       {
+                                               actualTarget = targets[i];
+                                               break;
+                                       }
+                               }
+/*                             else
+                               {
+                                       try
+                                       {
+                                               actualTarget = 
targets[i].parent;
+                                               while (actualTarget)
+                                               {
+                                                       if (actualTarget is 
InteractiveObject)
+                                                       {
+                                                               if 
(isMouseTarget(InteractiveObject(actualTarget)))
+                                                               {
+                                                                       break;
+                                                               }
+                                                       }
+                                                       actualTarget = 
actualTarget.parent;
+                                               }
+                                               if (actualTarget && 
actualTarget != root)
+                                                       break;
+                                       }
+                                       catch (e:Error)
+                                       {
+                                               if (actualTarget)
+                                                       break;
+                                       }
+                               }
+*/                     }
+               }
+
+               var localPt:Point = actualTarget.globalToLocal(stagePt);
+               event.localX = localPt.x;
+               event.localY = localPt.y;
+
+               if (actualTarget is TextField)
+               {
+                       if (type == "mouseDown")
+                       {
+                               var charIndex:int = 
actualTarget.getCharIndexAtPoint(event.localX, event.localY);
+                               actualTarget.setSelection(charIndex + 1, 
charIndex + 1);
+                       }
+               }
+
+               try
+               {
+                       actualTarget.dispatchEvent(event);
+               }
+               catch (e2:Error)
+               {
+                       TestOutput.logResult("Exception thrown in 
DispatchMouseClickEvent.");
+                       testResult.doFail (e2.getStackTrace()); 
+                       return;
+               }
+       }
+
+       /**
+        *  The object that receives the mouse event
+        */
+       public var target:String;
+
+       /**
+        *  The ctrlKey property on the MouseEvent (optional)
+        */
+       public var ctrlKey:Boolean;
+
+       /**
+        *  The delta property on the MouseEvent (optional)
+        */
+       public var delta:int;
+
+       /**
+        *  The localX property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var localX:Number;
+
+       /**
+        *  The localY property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var localY:Number;
+
+       /**
+        *  The stageX property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var stageX:Number;
+
+       /**
+        *  The stageY property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var stageY:Number;
+
+       /**
+        *  The shiftKey property on the MouseEvent (optional)
+        */
+       public var shiftKey:Boolean;
+
+       /**
+        *  The relatedObject property on the MouseEvent (optional)
+        */
+       public var relatedObject:String;
+
+    COMPILE::SWF
+    private function isMouseTarget(target:InteractiveObject):Boolean
+    {
+        if (!target.mouseEnabled)
+            return false;
+
+               // Examine parent chain for "mouseChildren" set to false:
+               try
+               {
+                       var parent:DisplayObjectContainer = target.parent;
+                       while (parent)
+                       {
+                               if (!parent.mouseChildren)
+                                       return false;
+                               parent = parent.parent;
+                       }
+               }
+               catch (e1:Error)
+               {
+               }
+
+        return true;
+    }
+
+       /**
+        *  customize string representation
+        */
+    COMPILE::SWF
+       override public function toString():String
+       {
+               var s:String = "DispatchMouseClickEvent: target = ";
+               s += target;
+               if (!isNaN(localX))
+                       s += ", localX = " + localX.toString();
+               if (!isNaN(localY))
+                       s += ", localY = " + localY.toString();
+               if (!isNaN(stageX))
+                       s += ", stageX = " + stageX.toString();
+               if (!isNaN(stageY))
+                       s += ", stageY = " + stageY.toString();
+               if (shiftKey)
+                       s += ", shiftKey = " + shiftKey.toString();
+               if (ctrlKey)
+                       s += ", ctrlKey = " + ctrlKey.toString();
+               if (relatedObject)
+                       s += ", relatedObject = " + relatedObject.toString();
+               if (delta)
+                       s += ", delta = " + delta.toString();
+               return s;
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DispatchMouseEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DispatchMouseEvent.as 
b/mustella/src/main/flex/DispatchMouseEvent.as
new file mode 100644
index 0000000..39f8e5d
--- /dev/null
+++ b/mustella/src/main/flex/DispatchMouseEvent.as
@@ -0,0 +1,306 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+COMPILE::SWF
+{
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.InteractiveObject;
+import flash.text.TextField;
+import flash.events.MouseEvent;
+import flash.geom.Point;
+}
+
+/**
+ *  The test step that fakes a mouse event
+ *  MXML attributes:
+ *  target
+ *  type
+ *  ctrlKey (optional)
+ *  delta (optional)
+ *  localX
+ *  localY
+ *  relatedObject (optional)
+ *  shiftKey (optional)
+ *  stageX
+ *  stageY
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional);
+ */
+public class DispatchMouseEvent extends TestStep
+{
+       /**
+        *  @private
+        */
+    COMPILE::SWF
+       override public function execute(root:Object, context:UnitTester, 
testCase:TestCase, testResult:TestResult):Boolean
+       {
+               if (waitEvent && waitTarget == null)
+                       waitTarget = target;
+               return super.execute(root, context, testCase, testResult);
+       }
+
+       /**
+        *  Set the target's property to the specified value
+        */
+    COMPILE::SWF
+       override protected function doStep():void
+       {
+               UnitTester.blockFocusEvents = false;
+
+               var actualTarget:Object = context.stringToObject(target);
+               if (!actualTarget)
+               {
+                       testResult.doFail("Target " + target + " not found");
+                       UnitTester.blockFocusEvents = false;
+                       return;
+               }
+
+               var event:MouseEvent = new MouseEvent(type, true); // all mouse 
events bubble
+               event.ctrlKey = ctrlKey;
+               event.shiftKey = shiftKey;
+               event.buttonDown = buttonDown || type == "mouseDown";
+               event.delta = delta;
+               if (relatedObject && relatedObject.length > 0)
+               {
+                       event.relatedObject = 
InteractiveObject(context.stringToObject(relatedObject));
+                       if (!event.relatedObject)
+                       {
+                               testResult.doFail("RelatedObject " + 
relatedObject + " not found");
+                               UnitTester.blockFocusEvents = false;
+                               return;
+                       }
+               }
+               
+               var stagePt:Point;
+               if (!isNaN(localX) && !isNaN(localY))
+               {
+                       stagePt = actualTarget.localToGlobal(new Point(localX, 
localY));
+               }
+               else if (!isNaN(stageX) && !isNaN(stageY))
+               {
+                       stagePt = new Point(stageX, stageY);
+               }
+               else
+               {
+                       stagePt = actualTarget.localToGlobal(new Point(0, 0));
+               }
+        try {
+            root[mouseX] = stagePt.x;
+            root[mouseY] = stagePt.y;
+            UnitTester.setMouseXY(stagePt);
+            if (root["topLevelSystemManager"] != root)
+            {
+                root["topLevelSystemManager"][mouseX] = stagePt.x;
+                root["topLevelSystemManager"][mouseY] = stagePt.y;
+            }            
+        } catch (e:Error) {}; // some scenarios don't support this
+
+               if (actualTarget is DisplayObjectContainer)
+               {
+                       var targets:Array = 
actualTarget.stage.getObjectsUnderPoint(stagePt);
+                       var arr:Array = 
UnitTester.getObjectsUnderPoint(DisplayObject(actualTarget), stagePt);
+                       targets = targets.concat(arr);
+
+                       for (var i:int = targets.length - 1; i >= 0; i--)
+                       {
+                               if (targets[i] is InteractiveObject)
+                               {
+                                       if (targets[i] is TextField && 
!targets[i].selectable)
+                                       {
+                                               actualTarget = 
targets[i].parent;
+                                               break;
+                                       }
+
+                                       if 
(isMouseTarget(InteractiveObject(targets[i])))
+                                       {
+                                               actualTarget = targets[i];
+                                               break;
+                                       }
+                               }
+/*                             else
+                               {
+                                       try
+                                       {
+                                               actualTarget = 
targets[i].parent;
+                                               while (actualTarget)
+                                               {
+                                                       if (actualTarget is 
InteractiveObject)
+                                                       {
+                                                               if 
(isMouseTarget(InteractiveObject(actualTarget)))
+                                                               {
+                                                                       break;
+                                                               }
+                                                       }
+                                                       actualTarget = 
actualTarget.parent;
+                                               }
+                                               if (actualTarget && 
actualTarget != root)
+                                                       break;
+                                       }
+                                       catch (e:Error)
+                                       {
+                                               if (actualTarget)
+                                                       break;
+                                       }
+                               }
+*/                     }
+               }
+
+               var localPt:Point = actualTarget.globalToLocal(stagePt);
+               event.localX = localPt.x;
+               event.localY = localPt.y;
+
+               if (actualTarget is TextField)
+               {
+                       if (type == "mouseDown")
+                       {
+                               var charIndex:int = 
actualTarget.getCharIndexAtPoint(event.localX, event.localY);
+                               actualTarget.setSelection(charIndex + 1, 
charIndex + 1);
+                       }
+               }
+
+               try
+               {
+                       actualTarget.dispatchEvent(event);
+               }
+               catch (e2:Error)
+               {
+                       TestOutput.logResult("Exception thrown in 
DispatchMouseClickEvent.");
+                       testResult.doFail (e2.getStackTrace()); 
+               }
+
+               UnitTester.blockFocusEvents = true;
+       }
+
+       /**
+        *  The object that receives the mouse event
+        */
+       public var target:String;
+
+       /**
+        *  The type of the event to send (mouseUp, mouseDown, etc).
+        */
+       public var type:String;
+
+       /**
+        *  The buttonDown property on the MouseEvent (optional)
+        */
+       public var buttonDown:Boolean;
+
+       /**
+        *  The ctrlKey property on the MouseEvent (optional)
+        */
+       public var ctrlKey:Boolean;
+
+       /**
+        *  The delta property on the MouseEvent (optional)
+        */
+       public var delta:int;
+
+       /**
+        *  The localX property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var localX:Number;
+
+       /**
+        *  The localY property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var localY:Number;
+
+       /**
+        *  The stageX property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var stageX:Number;
+
+       /**
+        *  The stageY property on the MouseEvent (optional)
+        *  Either set stageX/stageY or localX/localY, but not both.
+        */
+       public var stageY:Number;
+
+       /**
+        *  The shiftKey property on the MouseEvent (optional)
+        */
+       public var shiftKey:Boolean;
+
+       /**
+        *  The relatedObject property on the MouseEvent (optional)
+        */
+       public var relatedObject:String;
+
+    COMPILE::SWF
+    private function isMouseTarget(target:InteractiveObject):Boolean
+    {
+        if (!target.mouseEnabled)
+            return false;
+
+               // Examine parent chain for "mouseChildren" set to false:
+               try
+               {
+                       var parent:DisplayObjectContainer = target.parent;
+                       while (parent)
+                       {
+                               if (!parent.mouseChildren)
+                                       return false;
+                               parent = parent.parent;
+                       }
+               }
+               catch (e1:Error)
+               {
+               }
+
+        return true;
+    }
+
+       /**
+        *  customize string representation
+        */
+    COMPILE::SWF
+       override public function toString():String
+       {
+               var s:String = "DispatchMouseEvent: target = ";
+               s += target;
+               if (type)
+                       s += ", type = " + type;
+               if (!isNaN(localX))
+                       s += ", localX = " + localX.toString();
+               if (!isNaN(localY))
+                       s += ", localY = " + localY.toString();
+               if (!isNaN(stageX))
+                       s += ", stageX = " + stageX.toString();
+               if (!isNaN(stageY))
+                       s += ", stageY = " + stageY.toString();
+               if (shiftKey)
+                       s += ", shiftKey = " + shiftKey.toString();
+               if (ctrlKey)
+                       s += ", ctrlKey = " + ctrlKey.toString();
+               if (relatedObject)
+                       s += ", relatedObject = " + relatedObject.toString();
+               if (delta)
+                       s += ", delta = " + delta.toString();
+               return s;
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DragAndDropMain.mxml
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DragAndDropMain.mxml 
b/mustella/src/main/flex/DragAndDropMain.mxml
new file mode 100644
index 0000000..5efe2b4
--- /dev/null
+++ b/mustella/src/main/flex/DragAndDropMain.mxml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+-->
+<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"; xmlns="*" >
+
+       <mx:Script>
+       <![CDATA[
+               // uncomment this to write bitmaps
+               // public var createBitmapReferences:CreateBitmapReferences;
+
+               public var script:DragAndDropScript;
+
+               // public var cbTester2:CBTester2;                              
        
+
+       ]]>
+       </mx:Script>
+               
+       <mx:List id="lb" dragEnabled="true" dragMoveEnabled="true" 
initialize="lb.dataProvider=['alpha', 'beta', 'gamma', 'delta']" />
+
+       <mx:List id="lb1" dropEnabled="true" 
initialize="lb1.dataProvider=['apple']" />
+
+       <!-- probably best to uncomment one at a time -->
+       <!--<EventSniffer />-->
+       <!--<PixelSniffer />-->
+</mx:Application>

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/DragAndDropScript.mxml
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/DragAndDropScript.mxml 
b/mustella/src/main/flex/DragAndDropScript.mxml
new file mode 100644
index 0000000..7561fbb
--- /dev/null
+++ b/mustella/src/main/flex/DragAndDropScript.mxml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+-->
+<UnitTester xmlns:mx="http://www.adobe.com/2006/mxml"; xmlns="*" 
testSWF="NSMain.mxml">
+
+       <!-- this set of lines form a template that must be in each unit test 
-->
+       <mx:Script>
+       <![CDATA[
+       public static function init(o:DisplayObject):void
+       {
+       }                                       
+       ]]>
+       </mx:Script>
+       <mx:Metadata>
+       <![CDATA[
+               [Mixin]
+       ]]>
+       </mx:Metadata>  
+       <!-- end of set of lines that must be in each unit test -->
+
+       <mx:Script>
+       <![CDATA[
+       ]]>
+       </mx:Script>
+
+       <testCases>
+               <TestCase testID="myProp1">
+                       <body>
+                               <DispatchMouseEvent target="lb" 
type="mouseOver" localX="10" localY="30" />
+                               <DispatchMouseEvent target="lb" 
type="mouseDown" localX="10" localY="30" />
+                               <DispatchMouseEvent target="lb" 
type="mouseMove" buttonDown="true" localX="2" localY="30" waitTarget="lb" 
waitEvent="dragStart" />
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="228" stageY="57" 
+                                                                       
waitTarget="mx.managers::DragManager.mx_internal:dragProxy" waitEvent="move"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="208" stageY="77"
+                                                                       
waitTarget="mx.managers::DragManager.mx_internal:dragProxy" waitEvent="move"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="208" stageY="107"
+                                                                       
waitTarget="mx.managers::DragManager.mx_internal:dragProxy" waitEvent="move"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="218" stageY="157"
+                                                                       
waitTarget="mx.managers::DragManager.mx_internal:dragProxy" waitEvent="move"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="228" stageY="217"
+                                                                       
waitTarget="mx.managers::DragManager.mx_internal:dragProxy" waitEvent="move"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseMove" buttonDown="true" stageX="238" stageY="237" waitTarget="lb1" 
waitEvent="dragEnter"/>
+                               <DispatchMouseEvent target="stage" 
type="mouseUp" stageX="238" stageY="237" waitTarget="lb1" waitEvent="dragDrop" 
/>
+                               <AssertEvent target="lb" 
eventName="dragComplete" eventClass="mx.events::DragEvent" />
+                               <AssertPropertyValue target="lb.dataProvider" 
propertyName="length" value="3" />
+                               <AssertPropertyValue target="lb1.dataProvider" 
propertyName="length" value="2" />
+                       </body>
+               </TestCase>
+       </testCases>
+</UnitTester>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/EffectTesting.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/EffectTesting.as 
b/mustella/src/main/flex/EffectTesting.as
new file mode 100644
index 0000000..507c6f1
--- /dev/null
+++ b/mustella/src/main/flex/EffectTesting.as
@@ -0,0 +1,645 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package 
+{
+    import flash.events.Event;
+    import flash.events.TimerEvent;
+    import flash.utils.Timer;
+    import flash.utils.getQualifiedClassName;
+    
+    import mx.collections.ArrayCollection;
+    import mx.core.IVisualElement;
+    import mx.core.IVisualElementContainer;
+    import mx.core.UIComponent;
+    import mx.effects.CompositeEffect;
+    import mx.effects.Effect;
+    import mx.events.EffectEvent;
+    import mx.geom.TransformOffsets;
+    import mx.states.Transition;
+    
+    import spark.primitives.supportClasses.GraphicElement;
+    
+    /**
+    * 
+    * This class provides some APIs that can be useful for writing Mustella 
effects
+    * and transitions tests.  This will be instrumental in the Catalyst matrix 
tests.
+    * 
+    * It might be useful to think about building a set of TestStep classes 
that wrap 
+    * some of this functionality.
+    */
+    public class EffectTesting
+    {
+        // whether to seek the effect on effectStart (default: false)
+        public static var requestedSeek:Boolean = false;
+        
+        // what time to seek to in an effect (default: NaN - seek to the end 
of the effect)
+        public static var requestedSeekTime:Number = NaN;
+        
+        // the current effect being played
+        [Bindable] public static var currentEffect:Effect;
+        
+        // the document that ready events will be dispatched from and 
transitions will be pulled from
+        private static var rootDocument:UIComponent;
+        
+        // the character used to separate the elements in the expected values 
string
+        private static var elementSeparator:String = "|";
+        
+        // the character used to separate the values in the expected values 
string
+        private static var propertySeparator:String = ",";
+        
+        // keep track of the details of the latest comparison to ease further 
investigation
+        private static var lastResult:ArrayCollection;
+        
+        /**
+        * Sets up for an effect test.  This allows you to seek to a specific 
time in an effect.
+        * 
+        * Call this method after the ResetComponent in your (non-transition) 
effects test.
+        */
+        public static function setupEffectTest(document:Object, 
effect:Effect):String {
+            
+            // reset the test properties
+            resetProperties(document);
+            
+            // null check the effect
+            if (effect == null)
+                throw new Error("ERROR: You must provide a non-null effect to 
test.");
+            
+            // set the current effect
+            currentEffect = effect;
+            
+            // handle the effectEnd event
+            currentEffect.removeEventListener(EffectEvent.EFFECT_END, 
handleEffectEnd);
+            currentEffect.addEventListener(EffectEvent.EFFECT_END, 
handleEffectEnd);
+            
+            // dispatches a setupComplete event and returns setupComplete 
String so you can
+            // use this with either an AssertMethodValue or RunCode in Mustella
+            rootDocument.dispatchEvent(new Event('setupComplete'));
+            return "setupComplete";
+        }
+        
+        /**
+         * Sets up for a transitions test.  This allows you to seek to a 
specific time in a transition.
+         * 
+         * It parses all of the transitions in a document and sets up event 
listeners in a way that allows
+         * seeking to a specific time in the transition.
+         * 
+         * Call this method after the ResetComponent in your transitions test.
+         */
+        public static function setupTransitionTest(document:Object):String {
+            
+            resetProperties(document);
+            
+            var transitions:Array = rootDocument.transitions;
+            
+            // don't manage any listeners if there aren't any transitions
+            if (transitions == null)
+                throw new Error("ERROR: document has no transitions");
+
+            // add event listeners to each transition
+            for each (var t:Transition in transitions){
+                
+                // remove the effectStart event listener and add it again so 
we don't pile them up
+                t.effect.removeEventListener(EffectEvent.EFFECT_START, 
handleEffectStart);
+                t.effect.addEventListener(EffectEvent.EFFECT_START, 
handleEffectStart);
+                
+                // remove the effectEnd event listener and add it again so we 
don't pile them up
+                t.effect.removeEventListener(EffectEvent.EFFECT_END, 
handleEffectEnd);
+                t.effect.addEventListener(EffectEvent.EFFECT_END, 
handleEffectEnd);
+            }
+            
+            // dispatches a setupComplete event and returns setupComplete 
String so you can
+            // use this with either an AssertMethodValue or RunCode in Mustella
+            rootDocument.dispatchEvent(new Event('setupComplete'));
+            return "setupComplete";
+        }
+        
+        /**
+        * Called by the setup methods to reset the properties in this class
+        */
+        private static function resetProperties(document:Object):void {
+            
+            // null checks
+            if (document == null)
+                throw new Error("ERROR: You must provide a non-null 
document.");
+            
+            if (!(document is UIComponent))
+                throw new Error("ERROR: document must be a UIComponent");
+            
+            // reset the rootDocument
+            rootDocument = document as UIComponent;
+            
+            // reset the seek information
+            requestedSeek = false;
+            requestedSeekTime = NaN;
+        }
+        
+        /**
+        * Called on effect start, kicks off the seek behavior if it is 
requested.
+        */
+        private static function handleEffectStart(event:EffectEvent):void {
+            trace('effect start');
+            
+            currentEffect = event.target as Effect;
+            
+            // seek if it was requested
+            if (requestedSeek){
+            
+                // wait roughly a frame then pause the effect before seeking
+                var timer:Timer = new Timer(0);
+                timer.repeatCount = 1;
+                timer.addEventListener(TimerEvent.TIMER, 
function(e:Event):void{ seekCurrentEffect(); });
+                timer.start();
+            }
+        }
+        
+        /**
+        * Pauses then seeks to the position in the current effect. Fires an 
event when that is done.
+        */
+        public static function seekCurrentEffect():void {
+            var seekTime:Number = requestedSeekTime;
+            var c:CompositeEffect = currentEffect as CompositeEffect;
+            
+            // seek to the end if a specific seek time was not requested
+            if (isNaN(seekTime)){
+                // set the seekTime to the end of the effect
+                if (c){
+                    // if its a Parallel/Sequence then use the 
compositeDuration that also handle startDelay
+                    seekTime = c.compositeDuration;
+                } else {
+                    // just a plain effect so use startDelay + duration
+                    seekTime = currentEffect.startDelay + 
currentEffect.duration;
+                }
+            }
+            
+            trace('effect seek to ' + seekTime);
+            
+            // pause then seek
+            currentEffect.pause();
+            currentEffect.playheadTime = seekTime;
+            
+            // dispatch a ready event on the document
+            rootDocument.dispatchEvent(new Event("seekAssertionReady"));
+        }
+        
+        /**
+        * TODO: The inclusion of this method in the API is not fully baked.
+        * This method's name/signature/existance could change in the future
+        * when it is properly implemented.
+        */
+        public static function seekCurrentEffectTo(time:Number):void {
+            currentEffect.playheadTime = time;
+            rootDocument.dispatchEvent(new Event("seekAssertionReady"));
+        }
+        
+        /**
+         * TODO: The inclusion of this method in the API is not fully baked.
+         * This method's name/signature/existance could change in the future
+         * when it is properly implemented.
+         */
+        public static function getCurrentEffectDuration():Number {
+            var c:CompositeEffect = currentEffect as CompositeEffect;
+        
+            if (c){
+                // if its a Parallel/Sequence then use the compositeDuration 
that also handle startDelay
+                return c.compositeDuration;
+            } else {
+                // just a plain effect so use startDelay + duration
+                return currentEffect.startDelay + currentEffect.duration;
+            }
+        }
+        
+        /**
+        * Resumes the current effect
+        */
+        public static function resumeCurrentEffect():void {
+            trace("effect resume");
+            currentEffect.resume();
+        }
+        
+        /**
+        * Fires an event after the effectEnd event that signifies an assertion 
is now valid.
+        * 
+        * In a transition this gets called after the state values have been 
slammed in. 
+        */
+        private static function handleEffectEnd(e:EffectEvent):void {
+            trace('effect end');
+            
+            // dispatch a ready event on the document
+            rootDocument.dispatchEvent(new Event("endAssertionReady"));
+        }
+        
+        /**
+        * Given a root element it compares a set of properties across that 
element and any of its ancestors.
+        *
+        * Sample usage:
+        * 
+        * assertPropertySet(test1, 'width, height', '70,22|10,10', 0)
+        *   outputs: 'FAIL: test1.width: expected 70 +/- 0, but received 100') 
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param propertyNameString - a string deliminated with a character 
that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character 
that lists the values to expect
+        * @param tolerance - the amount of difference between actual and 
expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        * 
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure 
message  
+        */
+        public static function assertPropertySet(rootContainer:IVisualElement, 
propertyNamesString:String, 
+                                                expectedValuesString:String, 
tolerance:Number = 0, depth:int = -1):String {
+            return checkPropertySet(rootContainer, false, propertyNamesString, 
expectedValuesString, tolerance, depth);
+        }
+        
+        /**
+        * Given a root element it compares a set of properties across that 
element and any of its ancestors
+        * using the postLayoutTransformOffsets object of those elements.
+        * 
+        * Sample usage:
+        * 
+        * assertPostLayoutPropertySet(test1, 'rotationX, rotationY', 
'45,45|0,0', 0)
+        *   outputs: 'FAIL: test1.rotationX: expected 45 +/- 0, but received 
0')
+        * 
+        * Use null as an expected value if postLayoutTransformOffsets is null 
for example:
+        *  -  properties: 'rotationX,rotationY'
+        *  -  expected string: 'null,null|null,null'
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param propertyNameString - a string deliminated with a character 
that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character 
that lists the values to expect
+        * @param tolerance - the amount of difference between actual and 
expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        * 
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure 
message
+        */
+        public static function 
assertPostLayoutPropertySet(rootContainer:IVisualElement, 
propertyNamesString:String, 
+                                                          
expectedValuesString:String, tolerance:Number = 0, depth:int = -1):String {
+            return checkPropertySet(rootContainer, true, propertyNamesString, 
expectedValuesString, tolerance, depth);
+        }
+        
+        /**
+        * Workhorse method that is exposed via the two public assert methods.
+        * 
+        * Given a root element it compares a set of properties across that 
element and any of its ancestors
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param postLayout - whether to look at the 
postLayoutTransformOffsets object of an element
+        * @param propertyNameString - a string deliminated with a character 
that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character 
that lists the values to expect
+        * @param tolerance - the amount of difference between actual and 
expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        *  
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure 
message
+        * 
+        */
+        private static function checkPropertySet(rootContainer:IVisualElement, 
postLayout:Boolean, propertyNamesString:String, 
+                                    expectedValuesString:String, 
tolerance:Number = 0, depth:int = -1):String {
+            
+            // reset the result of the last comparison
+            // add to this collection at any point a comparison happens
+            lastResult = new ArrayCollection();
+            
+            // get the list of elements to inspect properties of 
+            var elementsToInspect:Array = getElementsToInspect(rootContainer, 
depth);
+            
+            // get the list of properties to inspect on each element
+            var propertyNames:Array = getPropertyNames(propertyNamesString);
+            
+            // split up the expectedValue string into values for each element
+            var expectedElementValues:Array = 
expectedValuesString.split(elementSeparator);
+            
+            // string that represents the reason for fail
+            var failString:String = "";
+            
+            if (elementsToInspect.length != expectedElementValues.length){
+                // this will also catch existance failures, for example if an
+                // element is supposed to be included or excluded from a state
+                failString = "FAIL: number of elements (" + 
elementsToInspect.length + ") != number of expected elements (" + 
expectedElementValues.length + ")";
+                logResult(failString);
+                return failString;
+            }
+            
+            // Go through each of the elements recursively in the rootContainer
+            for (var i:int = 0; i < elementsToInspect.length; i++){
+                var element:IVisualElement = elementsToInspect[i];
+                var expectedPropertyValues:Array = 
expectedElementValues[i].split(propertySeparator);
+                
+                // check for a malformed expected string
+                if (propertyNames.length != expectedPropertyValues.length){
+                    failString = "FAIL: number of properties != number of 
expected values for " + getElementId(element);
+                    logResult(failString);
+                    return failString;
+                }   
+                
+                // log that we are checking this property
+                logResult(getElementId(element));
+                
+                // check each property value 
+                for (var j:int = 0; j < propertyNames.length; j++){
+
+                    var propertyName:String = propertyNames[j];
+                    var e:* = expectedPropertyValues[j];
+                    var a:*;
+                    
+                    // First need to decide whether to grab the property 
values from 
+                    // the element or its postLayoutTransformOffsets
+                    if (postLayout){
+                        if (element.postLayoutTransformOffsets){
+                            a = 
element.postLayoutTransformOffsets[propertyName];
+                        } else {
+                            a = null;
+                        }
+                    } else {
+                        a = element[propertyName];                    
+                    }
+                    
+                    // prepare the log object for this property
+                    var logItem:Object = new Object();
+                    logItem.target = getElementId(element);
+                    logItem.propertyName = propertyName;
+                    logItem.actual = a;
+                    logItem.expected = e;
+                    logItem.tolerance = tolerance;
+                    logItem.postLayout = postLayout;
+                    logItem.depth = "TODO"; // TODO: one day might want to 
keep track of the depth of this item
+                    logItem.result = "Unknown";
+                    
+                    //
+                    // String comparison
+                    //
+                    
+                    // First just check if expected == actual via a simple 
string comparison.
+                    // If so then move on to the next propertyName, otherwise 
investigate further
+                    // via null and number comparisons.
+                    if (String(e) == String(a)){
+                        // this property passed
+                        
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    //
+                    // Null comparison
+                    //
+                    
+                    // expected == actual == null so this is fine, continue to 
next propertyName
+                    if (e == 'null' && a == null){
+
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    // expected or actual is null, but not both (because of 
above) so fail
+                    if (e == 'null' || a == null){
+                        failString = "FAIL: " + 
describeFailureLocation(element, propertyName, postLayout) + ": " + a + ", but 
expected " + e; 
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    //
+                    // Number comparison
+                    //
+                    
+                    // This approach assumes that it's ok treating undefined 
and NaN the same.
+                    // This is because Number(undefined) gets turned into NaN, 
if this is a limitation
+                    // might have to revisit this in the future.  
+                    var expectedValue:Number = Number(e);
+                    var actualValue:Number = Number(a);
+                    
+                    //
+                    // NaN comparison
+                    //
+                    
+                    // expected == actual == NaN, so this is fine, continue to 
next propertyName
+                    if (isNaN(actualValue) && isNaN(expectedValue)){
+
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    // expected or actual is NaN, but not both (because of 
above) so fail
+                    if (isNaN(actualValue) || isNaN(expectedValue)){
+                        failString = "FAIL: " + 
describeFailureLocation(element, propertyName, postLayout) + ": expected " + 
+                            expectedValue + ' plus or minus ' + tolerance + ", 
but received " + actualValue;
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    //
+                    // Number tolerance comparison
+                    //
+                    
+                    // expected differs from actual by more than the tolerance 
so fail
+                    if (Math.abs(actualValue - expectedValue) > tolerance){
+                        failString = "FAIL: " + 
describeFailureLocation(element, propertyName, postLayout) + ": expected " + 
+                            expectedValue + ' plus or minus ' + tolerance + ", 
but received " + actualValue;
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    // at this point the property passed
+                    
+                    // log the pass
+                    logResult("PASS", logItem);
+                }
+                // at this point the element passed, no need to log here
+            }
+            
+            return "PASS";
+        }
+        
+        /**
+        * Adds a result to the log.
+        * 
+        * @param result - a simple string to add to the log
+        * @param details - an object that if not null is added to the log 
after setting details.result equal to the first parameter
+        */
+        private static function logResult(result:String, details:Object = 
null):void {
+            if (details != null){
+                details.result = result;
+                lastResult.addItem(details);
+            } else {
+                lastResult.addItem(result);
+            }
+        }
+        
+        /**
+        * Returns the log of the last assertion result
+        */
+        public static function getLastResult():ArrayCollection {
+            return lastResult;
+        }
+        
+        /**
+        * Generates a string that describes what property of what element has 
failed.
+        * 
+        * ex: 
+        *   target.width
+        *   target.postLayoutTransformOffsets.width
+        */
+        private static function 
describeFailureLocation(element:IVisualElement, propertyName:String, 
postLayout:Boolean):String{
+            var output:String = "";
+            
+            output += getElementId(element);
+            
+            if (postLayout)
+                output += ".postLayoutTransformOffsets";
+            
+            output += "." + propertyName;
+            
+            return output;
+        }
+        
+        /**
+        * Given a root element and a string of property names this returns the 
formatted string of 
+        * each property value against that element and all descendants in a 
format that the assertion
+        * methods require.
+        * 
+        * @param rootContainer
+        * @param propertyNamesString - ex: 'width, height, alpha'
+        * @param postLayout - set to true if you want to access the properties 
of the postLayoutTransformOffsets
+        * @param requestedDepth - the depth to recurse (-1 by default for full 
recursion)
+        * 
+        * @return string
+        */
+        public static function 
generatePropertySet(rootContainer:IVisualElement, propertyNamesString:String, 
postLayout:Boolean = false, requestedDepth:int = -1):String {
+            // get the list of elements to inspect properties of 
+            var elementsToInspect:Array = getElementsToInspect(rootContainer, 
requestedDepth);
+            var propertyNames:Array = getPropertyNames(propertyNamesString);
+            var output:String = "";
+            
+            // for each element
+            for (var i:int = 0; i < elementsToInspect.length; i++){
+                var e:IVisualElement = elementsToInspect[i];
+                
+                // for each property
+                for (var j:int = 0; j < propertyNames.length; j++){
+                    // the property name
+                    var propertyName:String = propertyNames[j];
+                    
+                    // concatenate the value
+                    if (postLayout){
+                        if (e.postLayoutTransformOffsets){
+                            // access the value via the transform offsets
+                            output += 
e.postLayoutTransformOffsets[propertyName];
+                        } else {
+                            // the transform offsets are null
+                            output += "null";
+                        }
+                    } else {
+                        // access the value directly
+                        output += e[propertyName];
+                    }
+                    
+                    // concatenate the value separator
+                    if (j < propertyNames.length - 1)
+                        output += ",";
+                }
+                
+                // concatenate the element separator
+                if (i < elementsToInspect.length - 1)
+                    output += elementSeparator;
+            }
+            
+            return output;
+        }
+        
+        /**
+        * Returns an array of property names parsed from a comma separated 
string with 
+        * spaces removed. 
+        */
+        private static function getPropertyNames(s:String):Array {
+            // strip spaces
+            while (s.indexOf(" ") != -1){
+                s = s.replace(' ','');
+            }
+            
+            return s.split(propertySeparator);
+        }
+
+        /**
+        * Returns the id of an element, if one is not defined then it returns 
the class name
+        */
+        private static function getElementId(element:IVisualElement):String {
+            var s:String = String(Object(element).id);
+
+            return (s != "null") ? s : 
flash.utils.getQualifiedClassName(element).split("::")[1];
+        }
+        
+        /**
+        * Returns an array of all elements in a root element. If the element 
is not a
+        * container then it just returns an array of that element.
+        */
+        public static function getElementsToInspect(root:IVisualElement, 
requestedDepth:int):Array {
+            var output:Array = new Array();
+            
+            if (root is IVisualElementContainer){
+                // if its a container then recursively get all the elements to 
requestedDepth 
+                output = getDescendants(root as IVisualElementContainer, 
requestedDepth);
+            } else {
+                // just return the element
+                output.push(root);
+            }
+            
+            return output;
+        }
+        
+        /**
+        * Recursively generates an array of all elements in a given container 
(including itself) to a requested depth
+        */
+        private static function 
getDescendants(rootContainer:IVisualElementContainer, requestedDepth:int, 
depth:int = 0):Array{
+            var output:Array = new Array();
+            
+            // push the container element
+            output.push(rootContainer);
+            
+            // return if we've gone past the requested depth (and a 
requestedDepth of not -1)
+            if (requestedDepth != -1 && (depth >= requestedDepth)){
+                return output;
+            }
+            
+            for (var i:int = 0; i < rootContainer.numElements; i++){
+                var e:IVisualElement = rootContainer.getElementAt(i);
+                if (e is IVisualElementContainer){
+                    // recursively get the elements of the container
+                    output = output.concat(getDescendants(e as 
IVisualElementContainer, requestedDepth, depth+1));
+                } else {
+                    // push the non-container element
+                    output.push(e);
+                }
+            }
+
+            return output;
+        }
+        
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/EnableRemoteImageDiff.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/EnableRemoteImageDiff.as 
b/mustella/src/main/flex/EnableRemoteImageDiff.as
new file mode 100644
index 0000000..d462a34
--- /dev/null
+++ b/mustella/src/main/flex/EnableRemoteImageDiff.as
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package {
+
+import flash.display.DisplayObject;
+import flash.net.*;
+import flash.events.Event;
+
+[Mixin]
+/**
+ *  A "marker" class that causes test scripts to write out
+ *  bitmaps to the urls instead of reading and comparing
+ *  so that baselines/reference-points can be created for
+ *  future comparing.
+ */
+public class EnableRemoteImageDiff
+{
+
+       /**
+        *  Mixin callback that gets everything ready to go.
+        *  The UnitTester waits for an event before starting
+        */
+       public static function init(root:DisplayObject):void
+       {
+               CompareBitmap.useRemoteDiffer = true;
+       }
+
+
+}
+}

Reply via email to