Sahi Pro - Sahi Flex Support


Sahi Pro provides support for automation of flex applications via the Sahi Flex Library (SFL).

To enable automation, the flex application needs to be compiled with Sahi's swc file.

info Different swc files are available for different versions of Flex.
Compile your application with the relevant swc file.
If your application is compiled with Flex 4.0, use sfl4.swc

Sahi's swc files are located in sahi_pro/sfl/ folder

Compile with sfl.swc using command line

You can compile your flex application with sfl using the following command (Change sfl version as needed)
mxmlc yourapp.mxml -include-libraries+=sfl4.swc --output=yourapp.swf
warning After compilation refresh the browser cache, to make sure that the modified app is available.

Compile using Ant

When compiling using Ant, we need to specify the compiler.include-libraries option
<mxmlc file="sample.mxml">
  ... normal options
  <compiler.include-libraries dir="D:/sahi/sfl" append="true">
    <include name="sfl4.swc" />
warning After compilation refresh the browser cache, to make sure that the modified app is available.
More details on flex compilation itself via ant is available here

Compile using Adobe Flash Builder (Add SWC files to Flex Builder projects)

  1. In Flex Builder, select your Flex project in the Navigator.
  2. Select Project > Properties. The Properties dialog box appears.
  3. Select Flex Compiler in the tree to the left. The Flex Compiler properties panel appears.
  4. In the "Additional compiler arguments" field, enter the following command:
    -include-libraries "sfl4.swc"
info In Flex Builder, the entries in the include-libraries compiler option are relative to the Flex Builder installation directory.
The default location of this directory on Windows 32 bit is
C:\Program Files\Adobe\Flex Builder
and on Windows 64 bit is
C:\Program Files(x86)\Adobe\Flex Builder
warning After compilation refresh the browser cache, to make sure that the modified app is available.

Recording a flex application

From the dashboard, open any browser and navigate to your flex application.
Press ALT and double click on the document window of the page which you want to record.
Sahi's Controller window will popup. You can now start recording your Flex application.

warning Ctrl + hover (to get a flex element's accessor) will work only if the Flex application is in focus.
To do this, you will have to first click on the Flex application.

Flex APIs are slightly different from the normal JavaScript APIs.


The code above identifies a mx.controls::TextInput field inside a flex application identified by _f("flex_app_id")

Identification of Flex Components

Sahi identifies flex components based on the type (class name) of element and one of its visible/significant attributes.
The attributes may be one of label, text, name, automationName, toolTip, id, autoGeneratedName, index
or some other attribute which is relevant to that element.

mx components are identified as lower case of their class names.
Eg. mx.controls::TextInput corresponds to API _f("id").textinput("id2")

Spark components are identified with an s_ prefix.
Eg. spark.components::TextArea corresponds to API _f("id").s_textarea("id2")

Recognizing Custom Components

Sahi, by default, adds support for most mx and spark components.

If your component is not recognized, do the following:
  1. CTRL Hover on the element which is a parent of the element you want to recognize.
    You can use up/down arrows near the Accessor field on the Controller.
  2. Once a parent element is identified, click on the "List Properties" button on the Controller.
    It will list down all components inside the parent and also all attributes.
  3. In the fully qualified classnames visible, make an intelligent guess to narrow down your element of interest.
    Suppose your class name of interest is my.package::MyClass

To enable Sahi to recognize custom components in your application,
edit sahi_pro/userdata/config/user_extensions.js and add,

SflWrapper.prototype.addCustomMetaData = function(){
      this.addMetaData({qn: "my.package::MyClass",
        attributes: ["label", "text", "name", "automationName", "toolTip", "id", "autoGeneratedName", "index"],
        action: "click", value: "label", apiName: "myotherclass"});

In the above,
  1. change my.package::MyClass to your relevant class
  2. For clickable elements, keep action:"click". For text input elements, use action:"setValue"
  3. Set value to the attribute which conveys the value of that field.
    For example, a dateField will have selectedDate as its value attribute.
    This will be visible in the "Value" field on the Controller, and is used in assertions.
  4. The name of the API is by default the lowercase of the classname. If apiName attribute is specified, that is used instead.
    Eg. in the above case, the api becomes _flex("id").myotherclass("id2").
    If apiName was omitted, the api would have been _flex("id").myclass("id2").

Performing actions on Flex elements

Sahi's Action APIs also work with Flex elements.



_setValue(_f("mxComponents").textinput("txt1"), "English");
_setValue(_f("sparkComponents").s_richeditabletext("richEdTxt"),"Hi there!");


Relational operators in SFL

SFL supports near, inside, leftOf, rightOf and under APIs.

The usage of these APIs is a little different from the Sahi relation APIs.

  1. The relation APIs are called as member functions.
    _f("mxC").datagriditemrenderer("/").rightOf(_f("mxComponents").datagriditemrenderer("Mary Jones"))
  2. They can be chained to use multiple relations.

Known Issues

  1. For the CTRL-Hover to work, one needs to click on the Flex app and bring it in focus.
  2. File upload/download are not currently supported.
  3. NumericStepper recording does not work. However it can identified from the Controller and scripted.
  4. Parallel run of scripts using testrunner causes scripts to fail. Keep value of threads to 1 in testrunner.bat

Tips on usage

The flex recorder in Sahi is not as sophisticated as the web recorder.

To introspect and identify elements, one can use the Evaluate Expression box.

Ctrl Hover on any Flex element. It will populate something like

  1. One can see all attributes of the textinput field by one of the following
    1. CTRL Hover on element and Click on List Properties
    2. Copy the accessor into Evaluate Expression box and click "Test->"
    3. Explicity put component.listProperties() into Evaluate Expression box and click "Test->"

  2. To fetch the value of an element, use _getValue() API.
    Depending on the type of element, Sahi will correctly return its "value" attribute.
    _assertEqual("abcd", _getValue(_f("mxComponents").textarea("textarea")));

  3. To fetch the text of an element, use _getText() API.
    On flex it fetches the textual content of the component (behaves like innerText in HTML).
    _assertEqual("abcd", _getText(_f("mxComponents").textarea("textarea")));

  4. To fetch any property of an element, use .get

  5. To execute any member function of an element, use .executeFn(arg1, arg2 ...)

    var $fontSize = _fetch(_f("sampleId1").textinput("username").executeFn("getStyle", "fontSize"));
  6. To see which Flex components make up a particular component, use introspect()
    This will show a list of all elements inside the textinput field.
    The output may look something like:
    textinput0>Panel4>username>UITextField8:mx.core::UITextField #abcd

  7. To see all elements in a flex component, (especially when an element is not identified) use

  8. To check if an element exists, use _exists or .exists()

  9. To check if an element is visible, use _isVisible or .isVisible()

  10. To get data from a grid, use .getGridData()
    _assertEqual([["a", "a1"],["b", "b1"]], _f("mymovie").datagrid(0).getGridData());

  11. If getGridData does not work or if we are dealing with a graph,
    it is easier to get the dataprovider of the element and verify that the data is correct.
    To get the dataprovider, do
    This will return a JSON string. To get an object out of it, use
    var obj = eval("(" + _f("mymovie").datagrid(0).get("dataProvider") + ")");
    obj will now be an array of objects, which looks something like this:
    [{"balance":"1000","category":"Personel Care","date":"15/12/2010","description":"ATM Fee",

  12. Sometimes similar elements may be created in a flex app, but only the top most active container may be of relevance while automation.
    For example a tab pane may have multiple tabs and each tab screen may have a "Save" button.
    The sequence of navigation of tabs may give different indexes to these different Save buttons.
    To fix this, Sahi can be told to strictly work only with visible elements using _setStrictVisibilityCheck

    // Force Sahi to only find visible elements
    _setStrictVisibilityCheck(true, _f("myapp"));
    // Perform actions on visible elements only
    // Remove strict visibility check
    _setStrictVisibilityCheck(false, _f("myapp"));

  13. SFL supports indexes and regular expressions just like normal Sahi APIs.
    For example, to look for we could do
    // returns
    //using relation and regular expression
              .rightOf(_f("mxComponents").datagriditemrenderer("Maurice Smith")))

  14. In some flex apps with lots of dynamically loading parts, it may be customary to show a rotating icon with "Loading..."
    or some such message. Sahi can be told to wait till this message disappears using _setFlexReadyCondition.
    At the start of your script add this:
      return !_isVisible(fl.uitextfield("Loading..."));

Flex app and SahiPro version

Your Flex app needs to be compiled with the appropriate swc file based on the Flex SDK used.
It does not have a dependence on the Sahi Pro version itself.

The swc files available for compilation can be found inside the sahi_pro/sfl folder.