XAF - Using TestCafe for Functional Testing of Our Mobile UI

In addition to the Mobile UI features announced last month, we've been hard at work improving the quality and stability of mobile apps generated via XAF's mobile UI platform. Specifically, we have developed an internal functional testing system based upon TestCafe and successfully used it with our internal tests apps (MainDemo.Mobile among others). This online demo also uses our new DevExpress Logify service to track exceptions and improve overall product quality.

Frankly speaking, the main reasons we chose TestCafe was its simplicity and flexibility. TestCafe can be installed as npm package with a simple command, and its API can emulate all necessary webpage actions. We're using our own wrappers for the Selector function to find different element types:

const activeViewSelector = Selector('.dx-active-view');
const textboxClassName = '.dx-texteditor-input';
this.findTextBox = function (fieldName) {
    return activeViewSelector.find('div[data-options*="name: \'' + fieldName + '\'"]').find(textboxClassName);

We've also implemented custom wrappers for TestCafe actions (click, drag, etc.) to handle the moment wherein all the activities on the page (animation, movements, queries) are complete and all required elements become visible:
this.click = async function () {
     await this.waitElementToAppeared.apply(this, arguments);
     await this.testCafe.click.apply(this.testCafe, arguments);
     await this.wait(); 
     await checkForExceptions();
These wrappers are combined in a large helper (I will not show its full source code here) that allows us to write cleaner and more user-friendly test code. For instance, this is what a test case looks like:

import { Selector, ClientFunction } from 'testcafe';
import { XafMobileTestHelper } from './lib/helper';
var helper = new XafMobileTestHelper();

.beforeEach(async (t) => {
    helper.testCafe = t; await t.navigateTo('http://localhost/');
    await helper.switchToIframe("#simulatorFrame");
    await helper.typeText(helper.findTextBox("UserName"), "Sam", { replace: true });
    await helper.click(helper.findActionButton("Log On"));
test('Delete action(action with detail view)', async t => {
    await helper.click(helper.findNavigationMenuButton("menu"));
    await helper.click(helper.findNavigationItem('Task'));
    await helper.click(helper.findToolbarButton("New"));
    await helper.typeText(helper.findTextBox("Subject"), "Task To Delete");
    await helper.click(helper.findToolbarButton("Save"));
    await t.expect(helper.findListViewRecord("Task To Delete").exists).ok();
    await helper.click(helper.findListViewRecord("Task To Delete"));
    await helper.click(helper.findToolbarButton("Delete"));
    await helper.click(helper.findPopupActionButton("Yes"));
    await t .expect(helper.findListViewRecord("Task To Delete").exists).notOk();

Running tests and accumulating results is easy with TestCafe via a single command line:

Our mobile tests are integrated into the advanced and CCTray-based system used for automated execution and monitoring of all our unit and functional tests:

If you are interested in learning more about our Mobile UI (CTP), check out eXpressApp Framework > Getting Started > XAF Mobile (CTP) Tutorial and feel free to contact us with any questions or suggestions via the Support Center (https://www.devexpress.com/ask).

No Comments

Please login or register to post comments.