One of the missing features of the SeleniumFlexAPI was capture/replay. So I looked into different ways to enable it:
- Approach 1: Dispatch a DOM event and listen for it in ide-extensions.js
Problem: Where do I include the name/id of the Flex control? - Approach 2: Custom events
Problem: How do I listen to them in ide-extensions.js?
Solution: Additions to the Selenium IDE code: window.record
The solution is to add a new method to the Selenium IDE code: window.record which delegates to recorder.record. So that the Flex code can call this method directly through the ExternalInterface. The clear advantage of this technique is that there is no code pollution in your production code. But you have to change the SeleniumIDE code. There is an issue in the SeleniumIDE Jira which describes the additions, so go and vote for it!
Addtional code is also needed in the SeleniumFlexAPI.as in the applicationCompleteHandler
:
private function applicationCompleteHandler(event:FlexEvent):void { ... registerListeners(appTreeParser.thisApp.parent); ... }
private function registerListeners(subject:*):void { subject.addEventListener(MouseEvent.CLICK, recordClick); subject.addEventListener(MouseEvent.DOUBLE_CLICK, recordDoubleClick); subject.addEventListener(Event.ADDED, childAdded); addListenerRecursive(subject); }
Bubbling events like MouseEvent.CLICK can be added here but for the non-bubbling ones you have to recursively walk the displayobject hierarchy:
public function addListenerRecursive(root:*):void { for(var i:int = 0; i < root.numChildren; i++) { try { var child:Object = root.getChildAt(i); if (isMenuBar(child)) { child.removeEventListener(MenuEvent.ITEM_CLICK, recordMenuItemClick); child.addEventListener(MenuEvent.ITEM_CLICK, recordMenuItemClick); } if (isTextControl(child)) { child.removeEventListener(Event.CHANGE, recordTextChange); child.addEventListener(Event.CHANGE, recordTextChange); } if (isDataGrid(child)) { child.removeEventListener(DataGridEvent.ITEM_FOCUS_IN, recordItemClick); child.addEventListener(DataGridEvent.ITEM_FOCUS_IN, recordItemClick); } addListenerRecursive(child); } catch(e:Error) {} } }
In the event handling functions you just call the record function with the appropiate SeleniumFlex command:
private function recordClick(event:MouseEvent):void { ExternalInterface.call("record", "flexClick", "name=" + event.target.name, ""); }
Since Flex Sprites have no ids I use the name here for identifying the clicked target.
Another pitfall is when components are added dynamically (like when a Date opens, it adds a calendar view):
private function childAdded(event:Event):void { if (isDate(event.target.parent.parent)) { event.target.parent.parent.removeEventListener(CalendarLayoutChangeEvent.CHANGE, recordDate); event.target.parent.parent.addEventListener(CalendarLayoutChangeEvent.CHANGE, recordDate); } }
Conclusion
So finally capture/replay in SeleniumFlex becomes a reality! Nonetheless there is some work to do to support the different kinds of flex controls and Selenium commands.
Very usefull information!! I try and work very well. Only need more configuration for all the components of flex and use a more specific atributte for identify like “automationName” for example.
Thanks again!
Where should this above mentioned code be added so that it can record & playback flex content in a web application.
All the code should be added to the SeleniumFlexAPI.as file.
Hi,
I am not Java expert to tweak the API.
Can anyone here attach the modified SeleniumFlexAPI, for me to give a try against my project?
Thanks.
sivi
Hi
do you speak of the Javascript or the Actionscript part?
Cheers
Jens
The SeleniumFlexAPI has moved to Google Code : http://code.google.com/p/sfapi/downloads/list
The last commit is 07/2010 , it seems that the project is abandoned.
Anyway, there is a RecordingEventHandler class and a appTreeParser.assignListeners(); method call to be added in SeleniumFlexAPI.applicationCompleteHandler() to be supposed to do the job, but it looks unfinished.