Hi, I have for a while now been supposed build a custom component with custom events and post how to to this here.
I am eventually finshed an example of how to create a custom component, which is not possible to act upon with the regular actions from FunFX.
(By the way, I followed this tutorial, Create custom component Flex, it is for QTP, but are in many ways the same way to create a custom component for FunFX.
Thanks alot to Jarek for pointing out the problem I had. I used the wrong variable in the call to the super class' constructor. I used a couple of hours yesterday banging my head in to the wall. But after the help from Jarek everything fell into place.
It is actually very easy to implement an custom component, and use it with FunFX. What you need to do is 4 steps:
1. Create the custom component
2. Create the custom event
3. Create the delegate that extends for instance UIComponentAutomationImpl
4. Add the component to the AutomationGenericEnv.xml file
(Does not need to do them in that order)
Create the custom component
I chose to create a simple component listing different buttons, which could easily be done with a repeater, but that is not the point. I wanted to find out and hopefully share my findings with you guys, that haven't tried it yet. What I wanted to do was to implement my own event, so that I could use the itemrenderer tag in the event to make the buttons be pushed.
Below is the code for the custom component. It just extends from a existing component and overrides the necessary methods like, createChildren etc. What is important to notice here is that you need to add you children, that you want to reach into the _renderers so that they can be used by the delagate as automationChildren. The top event sentence makes it possible for the delegate to replay that customevent, which I will create after this.
[Event(name="itemClick", type="event.CustomItemClickEvent")]
public class CustomComponent extends VBox
{
private var _dataProvider:Array;
private var _renderers:Array = [];
private var _itemRenderer:IFactory = new ClassFactory(Button);
public function CustomComponent()
{
super();
}
override protected function createChildren():void{
super.createChildren();
for(var i:int=0; i<_dataProvider.length; i++){
var inst:UIComponent = _itemRenderer.newInstance();
inst.addEventListener(MouseEvent.CLICK, itemClickHandler);
this.addChild(inst);
_renderers.push(inst);
Button(inst).label = _dataProvider[i];
}
invalidateSize();
invalidateDisplayList();
}
//You must also implement an itemClickHandler, for the buttons, but that is nothing special
}
Create the custom event
To be able to click on the button, I need to create a custom event that makes this happen. This event holds a button which is the type of item the custom component is holding. It is probably possible to do this a bit more generic, but this was just to understand how things works.
public class CustomItemClickEvent extends Event
{
public var itemRenderer:Button;
public static const ITEM_CLICK:String = "itemClick";
public function CustomItemClickEvent(type:String){
super(type, bubbles, cancelable);
}
}
Create the delegate
The delegate must be included to the application project that uses the custom component. I chose to build an swc of the delegate and add it as the FUnFXAdapter.swc file.
Below is the code from the delegate. It is important to use the [Mixin] attribute, as this forces the delegate's init methd to be run when your application starts. The numAutomationChild and getAutomationChild methods you will have to implement concerning how your custom component added items to the renders array. Here you can do what you want.
[Mixin]
public class CustomComponentDelegate extends UIComponentAutomationImpl
{
public static function init(root:DisplayObject):void
{
Automation.registerDelegateClass(CustomComponent, CustomComponentDelegate);
}
private var comp:CustomComponent;
public function CustomComponentDelegate(component:CustomComponent)
{
super(component);
comp = component;
}
override public function get numAutomationChildren():int
{
var renderers:Array = comp.getItemRenderers();
if(renderers != null)
return renderers.length;
else return 0;
}
override public function getAutomationChildAt(index:int):IAutomationObject
{
var renderers:Array = comp.getItemRenderers();
if(renderers == null) return null;
if(index >= 0 && index < renderers.length)
return renderers[index];
else
return null;
}
override public function replayAutomatableEvent(event:Event):Boolean
{
var help:IAutomationObjectHelper = Automation.automationObjectHelper;
if (event is CustomItemClickEvent)
{
var rEvent:CustomItemClickEvent = event as CustomItemClickEvent
help.replayClick(rEvent.itemRenderer);
(uiComponent as IInvalidating).validateNow();
return true;
}
else
return super.replayAutomatableEvent(event);
}
Add the component to the AutomationGenericEnv.xml file
When everything is created you can add the component to the AutomationGenericEnv.xml file. Below is what I added somewhere in the file below the Object (because it is based on inheritance and the file is parsed.)
<ClassInfo Name="CustomComponent" Extends="Object" SupportsTabularData="true">
<Implementation Class="CustomComponent"/>
<Events>
<Event Name="ItemClick">
<Implementation Class="customEvent::CustomItemClickEvent" Type="itemClick"/>
<Property Name="itemRenderer">
<PropertyType Type="String" />
</Property>
</Event>
</Events>
<Properties>
</Properties>
</ClassInfo>
Building a test application and writing a test
Now you are ready to write an application using the component and a test which interacts with the component.
<Application="" mx="http://www.adobe.com/2006/mxml" component="component.*">
<mx:VBox>
<component:CustomComponent="" id="cust" dataprovider="['Nr1','Nr2']"/>
</mx:VBox>
</mx:Application>
@ie.custom_component("cust").item_click(:item_renderer => "Nr2")
I hope this was helpfull, and possible to understand :-)
Do not forget to read the post about RSpec and Stories that I wrote just before this post. Thanks!