Search code examples
extjswidgetfunctional-testinguniqueidentifier

ExtJS functional testing - get internal elements of ExtJS controls


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


Solution

  • 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