2

We have decided to use Sencha ExtJS for the client side of our new products and we plan to do automated functional testing on the client side. We looked into several tools like Ranorex, Selenium, Telerik Test Studio etc and so far we like Test Studio more than the others. Anyway, the question I am asking is relevant no matter which of these tools one might use.

I am interested to find out what is the recommended way to get internal elements of extjs controls. In order to be clear enough I will give an example.

I have a numberfield control and I would like to test that if I clicked on the 'up' button the value in the field will increase by one unit. Note that I did set an unique ID to the number field (id="testDurationHourNumberField"). The number field has the following (simplified) DOM structure:

<table id="testDurationHourNumberField">
<tbody>
    <tr id="testDurationHourNumberField-inputRow">
        <td id="testDurationHourNumberField-labelCell">
            <label id="testDurationHourNumberField-labelEl">Label:</label>
        </td>
        <td id="testDurationHourNumberField-bodyEl">
            <table id="testDurationHourNumberField-triggerWrap">
                <tbody>
                    <tr>
                        <td id="testDurationHourNumberField-inputCell">
                            <input id="testDurationHourNumberField-inputEl">
                        </td>
                        <td>
                            <div role="button" id="ext-gen1211"></div>
                            <div role="button" id="ext-gen1212"></div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </td>
    </tr>
</tbody>
</table>

In order to do this I must access the input element (id="testDurationHourNumberField-inputEl") and the button (id="ext-gen1211"). How is the recommended way of accessing these 2 elements?

So far I have the following options:

  • Use ExtJS internal IDs - does NOT seem good because:
    • These are internal IDs, they are out of my control and they may change if I will later add another control on the page
    • It seems that for the same page I get different IDs in different browsers. For example in IE 10 I get different IDs for the buttons from the IDs I get in Google Chrome.
  • Get controls by unique ID and get internal elements by knowing their internal DOM structure or attributes - does NOT seem good because:
    • I would rather not use this way because I do not know that when upgrading to the next version of ExtJS the internal DOM structure won't change or some attribute names will change and I will have to update the tests that I created
    • eg pseudocode: getById('testDurationHourNumberField').getElementByAttributeAndIndex('role=button', 0)

Can you please let me know which is the recommended way to do this?

Thanks,
Paul

Paul Chiritescu
  • 287
  • 2
  • 3
  • 16
  • what do you mean you don't have access to ExtJS in a functional test? You have to have ExtJS to render the DOM. – dbrin Dec 18 '12 at 21:23
  • We do always have ExtJS when running the web app but when running functional tests we do not have access to it. We do functional tests using tools like Test Studio/Selenium as they offer an automated way of recording user actions and running them as tests. These tools usually operate on the DOM, they parse the DOM, and have a set of APIs to perform on the DOM elements. – Paul Chiritescu Dec 19 '12 at 07:41
  • can they run javascript? – dbrin Dec 19 '12 at 07:53
  • I do not know if they can run javascript, maybe some of the tools have this capability but when I posted this question I assumed the default scenario, that they can not. – Paul Chiritescu Dec 19 '12 at 08:11

3 Answers3

2

This is how i do it in my project:

it("should increment the control value", function () {
    var ctrl = Ext.ComponentQuery.query('#age')[0],
        upBtn = Ext.ComponentQuery.query('#upButton')[0],
        curValue = ctrl.getValue();

    upBtn.fireEvent('click');

    expect(ctrl.getValue()).toEqual(curValue + 1);
});

I use jasmine for it.

Update:

For functional testing you should read this:

  1. Any suggestions for testing extjs code in a browser, preferably with selenium? And this:
  2. http://www.sencha.com/blog/testing-ext-js-ext-gwt-applications-with-selenium
Community
  • 1
  • 1
lontivero
  • 5,235
  • 5
  • 25
  • 42
  • It seems that you are talking about unit testing, but I was asking about functional testing. We do unit tests with Jasmine as well but when doing functional testing we only operate on the DOM, and we do not have access to ExtJS library. – Paul Chiritescu Dec 18 '12 at 15:21
1

I know it has been some time but I will write here how we decided to address this issue. For functional testing we are using Telerik TestStudio and to get internal elements we use GetById approach, where each ID of each component needs to be unique. The main problem in this approach was with the autogenerated IDs:

  • IDs of internal components would get different values depending on the browser
  • IDs of internal components might change on an ExtJs upgrade
  • IDs of internal components might change when adding other components on the page (and this constantly happens)

In order to solve this we implemented patches of ExtJS classes, to make sure that IDs of internal elements are unique.

For the example in my question with the numberfield we have implemented the following patch, to make sure that the buttons of the number field have unique IDs that do not change on a different browser.

Ext.define('Waf.patch.extJS411.form.field.Spinner', {
    override: 'Ext.form.field.Spinner',

    getTriggerMarkup: function() {
        var me = this,
            hideTrigger = (me.readOnly || me.hideTrigger);

        me.triggerTpl = '<td style="{triggerStyle}">' +
            '<div class="' + Ext.baseCSSPrefix + 'trigger-index-0 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-up"' + 'id="' + me.id + '-buttonUp"' + ' role="button"></div>' +
            '<div class="' + Ext.baseCSSPrefix + 'trigger-index-1 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-down"' + 'id="' + me.id + '-buttonDown"' + ' role="button"></div>' +
            '</td>' +
            '</tr>';

        return me.getTpl('triggerTpl').apply({
            triggerStyle: 'width:' + me.triggerWidth + (hideTrigger ? 'px;display:none' : 'px')
        });
    }
});

We have implemented patches also for other classes where we encountered this issue, so we covered most of our usecases. There are not many patches:

  • Ext.form.field.Spinner
  • Ext.form.field.Trigger
  • Ext.panel.Panel
  • Ext.view.BoundList
  • Ext.window.MessageBox

Regards, Paul

Paul Chiritescu
  • 287
  • 2
  • 3
  • 16
0

You may want to look at the automated functional GUI testing tool RIATest.

ExtJS UI widgets are first class citizen in RIATest. This means that unlike other HTML testing tools you do not need to write tests that manipulate the HTML DOM elements when you actually need to work with ExtJS components.

RIATest knows how to ignore ids dynamically generated by ExtJS, yet if you manually assign ids to the components the tool will use them for identification (see e.g. #tree2 in the sample below).

The tests in RIATest operate in terms of ExtJS UI widgets.

Examples of RIATest scripts that work with ExtJS widgets:

The following clicks on an ExtJS button with label "Next Page":

ExtButton("Next Page")=>click();

And the following does drag-n-drop of a row from one ExtJS tree to another:

ExtRow("Controller.js")=>dragAndDropTo(
    ExtTreePanel("#tree2")->ExtRow("Custom Ext JS"));

And this collapses the header of an ExtJS box:

ExtBox("Feeds")->ExtHeader("FeedsВ")->ExtCollapser()=>click();

(All sample code above is from real test scripts that run on ExtJS sample applications).

RIATest also knows when to automatically wait for ExtJS AJAX to finish, so if your UI does dynamic content downloading the tests will auto-magically wait until the data is received from the server.

(Disclaimer: I am a RIATest team member).

TN.
  • 611
  • 1
  • 4
  • 9