Intern Command Helpers

intern

// Intern - Command Helper

The term "Command Help" refers to a design pattern for creating reusable modules 
for Intern functional tests.  The idea is to create functions that execute 
groups of low-level commands for some higher-level purpose.  For example, assume 
a login form is used by several of our test cases.  The commands in a functional 
test to fill in and submit the login form, and to verify that the login was 
successful, might look like:

return this.remote
    .get('page.html')

    .findById('username')
    .type('foo')
    .end()

    .findById('password')
    .type('bar')
    .end

    .findById('submit')
    .click()
    .end()

    .findByTag('h1')
    .getVisibleText()
    .then(function (text) {
        assert.equal(text, 'Welcome, foo!');
    })

We can dramatically simplify the above code by moving the low-level logic into 
Command helpers:

return this.remote
    .get('page.html')
    .then(login('foo', 'bar'))
    .then(verifyLogin('foo'));

This code easier to read, and the helper functions allow the login and 
verification logic to be shared with other test cases.

A Command helper is implemented as a function that returns a function that can 
be passed to a then call in a Leadfoot Command chain.  This has to be 
implemented as a function that returns a function, instead of being just a plain 
function, because we want to be able to pass parameters to these function.  The 
then function takes a function as a parameter.  It does not offer a way to pass 
parameters to these functions.  Because command helpers are implemented as a 
function that return a function, and the fact that we invoke the outer function 
and pass the result to the then method, we are able to pass parameters to the 
inner function via closure.

In the above code, the username and password are hard-coded, and passed directly 
when we invoke the outer function.  Do not let that confuse us.  Instead of 
hard-coding the data, we can also put this data into a configuration file, and 
read it from there.

The Command chain is built from this.parent rather than a reference to 
this.remote. This allows the helper to execute in the context of the outer 
Command chain (the one that called the helper). For example, if the context of 
the outer Command chain was a particular <table> element on the page, that will 
be the context of this.parent in the Command chain.

While the search context for the this.parent Command chain is initially the 
same as the context parent chain’s context, changes to the this.parent chain’s 
context will not affect the parent. This means you don’t need to worry about 
restoring the search context (by calling end()) at the end of the then callback.

The Command chain must be returned by the then callback or the parent Command 
chain won’t wait for the actions in the then callback to complete.

The complete implementation and usage for the above login and verifyLogin help 
methods:

function login(username, password) {
    return function () {
        return this.parent
            .findById('username')
            .type(username)
            .end()

            .findById('password')
            .type(password)
            .end

            .findById('submit')
            .click();
    }
}

function verifyLogin(username) {
    return function () {
        return this.parent
            .findByTag('h1')
            .getVisibleText()
            .then(function (text) {
                assert.equal(text, 'Welcome, ' + username + '!');
            });
    }
};

registerSuite({
    test1: function () {
        return this.remote
            .get('page.html')
            .then(login('foo', 'bar'))
            .then(verifyLogin('foo'));
    }
});

A good place to keep these helper methods is the page object.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License