Monday, December 1, 2008

FunFX 0.2.0

Hi everybody

I am sorry about the slow update rate of this blog. I have lately been developing the new FunFX. It is still located at github.com
(http://github.com/peternic/funfx/tree/master)

We are going to release it on Rubyforge soon, but please try it by downloading it from github.

It is much better that the first version, and there are just small changes you need to do to your test scripts.

- Peter

Tuesday, August 19, 2008

Taking FunFX to the next level

Hi everybody

FunFX have been needing a facelift for a while now. It works, but there are aspects of FunFX that bearly works.

Aslak Hellesøy, a colleague from BEKK and creator of many fantastic Ruby applications, has taken the initiative to help me improve FunFX.

The bad thing about this facelift will be that there will be some changes in the way test scripts are written, so some rewriting will be necessary.

The first thing we have done is to base FunFX on Watir. This will make it simpler to get going with FunFX and alot of functionality is allready in place.

In the current version, all ruby classes of the Flex objects are created at runtime and in memory. This has made it difficult to begin using FunFX due to bad documentation. In the next version, these classes will be created on file, so that there will be a nice rdoc explaining these classes making it easier to use.

We have also changed to git instead of subversion, so it will be possible to create own versions, that we can merge.

I will write more about the upcomming FunFX during development. For now it is possible to find the new FunFX source at github.com. But notice that it is still early in development.

Hope you will use the new version!

Friday, July 11, 2008

Use the new Flex 3 components like AdvancedDatagrid

Hi

I just recently (yesterday) needed to test a advanced datagrid. I have just tested it as a regular datagrid for the moment. I have not been able to release a fix for this, so I write about it here instead, since I am going on a holiday now for the next 2 weeks.

To be able to utilize the advanceddatagrid you need to include a new file to the Flex project automation_dmv.swc. But with this file included the FunFX version out fails. But with a quick fix this will not be a problem.

Ilkka Kudjoi have made a fix, which is located http://dpaste.com/62646/. His fix makes FunFX only care about automationchildren. I used to only care for automationchildren, but changed it for a while back, but do not remember that for the moment.

So try it if you would like. But will release a new version i agust.

Have good summer!

Monday, June 23, 2008

Automation.swc locks application

Hi, I had a strange problem at work last week. The problem was when I added automation.swc and automation_agent.swc to a new project I am working on, all controls was lokced. I was not able to click on any objects. But as soon as I removed the swc files, everything worked great.

I did not have the time to take a closer look at the problem until today. And searched flexcoders (a great group by the way for information about Flex and Actionscript), to see if anyone else been through the same thing. And what do you know :-) At this post, I got to this site.

The problem seems to be that if you have an invisible container of some sort that overlaps other containers, that container will steal all the focus from any other container.

The solution to this problem is to add an eventlistener to the stage to be able to locate the container. And when you have discovered the container, add mouseEnabled="false" to it. Then everything should be working!

Thursday, June 12, 2008

Use FunFX together with Watir

Usually Flex applications uses a regular HTML pages for authentication or by other support. When this is the case, FunFX alone is not able to get to the Flex part. In these situations you can use the excellent tool Watir.

To make Watir work together with FunFX you do the same setup as usually with FunFX, but you also require watir, and attaches Watir to the browser FunFX started. With the example below I use the the title of the page to make Watir understand what browser to choose.

@ie = Funfx.instance@ie.start(true)
@ie.speed = 1
@ie.goto("http://localhost/flexapplication.html", "flexapplication")
$browser = Watir::IE.attach(:title, "PageTitle")


When you have done this, both the FunFX instance and the Watir instance is able to control the browser. But one thing you should watch out for is that when FunFX enter the Flex application, it must set the flex object again, so either if you use FunFX to navigate to the Flex application, you can define the flex object in the goto statement. Or you can use the setFlexObject method.

@ie.goto("www.google.com", nil)
$browser.text_field(:name, "q").set "pickaxe"
$browser.button(:name, "btnG").click
@ie.goto("http://localhost/flexapplication.html", "flexapplication")
@ie.button("buttonName").click


@ie.set_flex_object("flexapplication")

Tuesday, April 15, 2008

FunFX 0.0.4 Released

Hi, have been a while now, sorry about that. But now thanks to Neil Curzon, Bill Torla and Mario Sanchez a new version is available. This version has added a patch that makes it possible to access different browsers with in the tests, to look at different sessions.

It is now also possible to use FunFX together with Watir. It was difficult before, due to FunFX's lack of ability to hook onto other browser windows.

It is also now possible to select a row in a datagrid by index instead of text. It still uses the item_renderer option, but if you give a integer into it that is within the grids row count it chooses row by index.

Let me know how things work.

Monday, March 10, 2008

Finding the right element in a combobox

This post might be usefull not only for a combobox but all other list type of controls.

You have maybe found a better solution than this yourself, but I would like to show you a simple (maybe not the best) way to find specific item in a combobox. As you maybe know or have experienced Flex does only draw what you see and hence an item you do not see in the combobox is not possible to select. Due to this you will have to scroll the elements to make it visible.

I played around a couple of minutes and just created this while loop that scrolls all the items, and picks out the item with the itemrenderer "12"


@ie.combo_box("cbTest").open
num_rows = @ie.combo_box("cbTest").num_rows
position = 0
while(position <= num_rows)
@ie.combo_box("cbTest").scroll(:position => position)
begin
@ie.combo_box("cbTest").select(:item_renderer => "12")
position = num_rows + 1
rescue
position += 1
end
end


Support for this might be built into the FunFX framework, that it searches like that automatically, or what do you think?

Friday, February 22, 2008

FunFX and custom components


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!

Thursday, February 21, 2008

FunFX and RSpec with stories

Hi again

I how now been playing around with RSpec and its story runner (rBehave). Its a fun way to write the tests as stories. This can work out nice with functional tests.

First thing you will need to do is to install RSpec (gem install rspec).

I have created the test towards the initial test application I put out, the one where you can add a product and a product item. A very simple application, but it shows how to work with FunFX in many ways.

When writing there are three main files (when writing for FunFX, addtional class files if you write for traditional Ruby classes) that must be written. And that is the story, a plain text file, the steps, the code behind the story (actual @ie.button("name").click actions) and a file to runn the stories, this file is often called all.rb and is running all tests you define in it.

When I searched the net for tips on how to organize the different files there were not created any best practice, as far as I could see. But when I asked Aslak Hellesøy a collegue and one of the creators of RSpec, he provided me with an example.

If you are to use some kind of ruby classes, they should go into a folder called lib, the step files should go into a folder called steps, and the story should go into a fodler called stories. The all.rb file and a Rake file is located at the root. This was not too spooky :-)

The first thing I did was to write the story. The story is writen as plain text, but in a certain way. There are certain words that are mapped to the steps file, and these are Given, When, And, and Then.

Below is the text that is in the story file. I think this is a nice way to write tests. This way the business side of a project may read the tests too.


Story: Add product

As an user
I want to be able to register and delete products
So that I can have control over the stock

Scenario: Add and delete product
Given the user is logged into the application
When the user click the Add product button
Then the product registration view is visible

Given the user enters Shirt into the product name textbox
And inputs Tennis into the product category textbox
And the user click the Add item button
And the user enters Pro into the item name textbox
And the user enters 9000 into the price textbox
And the user click the Ok button
Then the product registration view is closed

When the user selects Shirt from the datagrid
Then the screen shows the count of 1 item numbers

When the user drags the Shirt item from the datagrid to the trash
Then the datagrid has 0 number of products left

Then close the application


Since this was my first time implementing a rspec story I did not write entire story before I wrote the steps, but after you have written the story or in between as I did you will create the steps that will be taken at each line.

I have created the test towards the initial test application I put out, the one where you can add a product and a product item. A very simple application, but it shows how to work with FunFX in many ways.

Below is the steps file that implement the story file. One thing that you must remember is that the keyword And, is not used in the steps file. It just syas that the step is of the same type as the step above. And as I can see is only used for Given steps.


require 'funfx'

steps_for :product do
Given "the user is logged into the application" do
@ie = Funfx.instance
@ie.start(true)
@ie.speed = 1
@ie.goto("http://funfx.rubyforge.org/Flex/FlexRepeater/FlexRepeater.html", "FlexRepeater")
end

When "the user click the $button_name button" do |button_name|
@ie.button(button_name).click
end

Then "the product registration view is visible" do |balance|
@ie.title_window("Product registration").visible == "true"
end

Given "the user enters $text into the product name textbox" do |text|
@ie.text_area("tName").input(:text => text)
end

Given "inputs $text into the product category textbox" do |text|
@ie.text_area("tCategory").input(:text => text)
end

Given "the user click the $button_name button" do |button_name|
@ie.button(button_name).click
end

Given "the user enters $text into the item name textbox" do |text|
@ie.text_area("tfItemName").input(:text => text)
end

Given "the user enters $text into the price textbox" do |text|
@ie.text_area("tfPrice").input(:text => text)
end


Then "the product registration view is closed" do
@ie.title_window("Product registration") == nil
end

When "the user selects $text from the datagrid" do |text|
@ie.data_grid("dgOffer").select(:item_renderer => text)
end

Then "the screen shows the count of $item_number item numbers" do |item_number|
@ie.label("lNumberItems").text == item_number
end

When "the user drags the $item item from the datagrid to the trash" do |item|
@ie.data_grid("dgOffer").drag_start(:dragged_item => "Shirt")
@ie.box("deleteBox").drag_drop
end

Then "the datagrid has $number number of products left" do |number|
@ie.data_grid("dgOffer").num_rows == number
end

Then "close the application" do
@ie.unload
end
end


As Mark Anderson stated in the FunFX mailinglist, Rspec and stories does not provide a teardown method, the @ie.unload method must be played different. Michael Latta provided me with this solution. Add a step that might be called close the application, that will be added to all stories.

The all.rb file looks like this:


require 'rubygems'
require 'spec/story'
$:.unshift(File.dirname(__FILE__) + '/lib')
require 'steps/product'

with_steps_for :product do
run File.dirname(__FILE__) + '/stories/add_product'
end


Now it is just to run the all.rb file with ruby.

Tip: I have noticed that there are some problems with FunFX's ability to extract properties as text, automationName and so on with the Flex 3. Due to this one of the steps in the rspec example fail. But that is one more brilliant thing about rspec, it does not quit when it fails, it just states that the step failed.

Friday, February 8, 2008

Test application is out

Hi

Due to the lack of a good source to what FunFX is able to to and how to do it, I have created a test application, which is located at http://funfx.rubyforge.org/Flex/FlexObjectTest/FlexObjectTest.html
, the source is found at http://funfx.rubyforge.org/Flex/FlexObjectTest/srcview/.

This application contains many of the available display objects in the Flex language, and all display objects have related FunFX tests at http://funfx.rubyforge.org/FunFXTests/.

I hope this application will make it easier to understand FunFX and also be a knowledge base where we can exchange knowledge on how to test different controls.

I hope everybody can contribute with sample code that is either difficult to test and you have tests written for this code, so that other people can learn. Or if you simply have code that you are not able to test, then others can help test the code.

In this way, FunFX would be able to get better much faster.

The test application and tests are under svn control at Rubyforge, but there is no automatic roll out, so I have also created an email funfx.tests at gmail.com, which could be used to contribute with code or tests.

(Please add the word FunFX in the subject so I can sort the mail efficiently.)

I hope this helps!

Version 0.0.3 is out

Hi

Now a new version is released, The one big change is the support for Safari browser on the OS X, thanks to Neil Curzon for implementing this.

Other changes is that I have changed the way FunFX searches children. In the earlier versions, it used the getAutomationChild method but now I have switched to getChild method. This improves the support for repeaters.

Let me know if something does not work with these changes.