SitePen Support is a service used by companies to improve the productivity and efficiency of their enterprise development teams. Our customers often ask questions about best practices when using various development tools for modern JavaScript and TypeScript development. Recently, we were asked to better explain some of the asynchronous syntax used by Intern. For example, where to put a .end()
statement and where to resolve promises with Intern. Here was our response!
When writing functional tests, a Leadfoot Command object is exposed at this.remote
to automate tasks that would normally be performed manually to test an application. Methods include common operations like finding elements, pressing keys, getting attributes, and clicking onscreen components.
When to use .then
Using this Command object, operations can be chained together that will execute in order one after another. Because every operation performed on a Command object is asynchronous, a then callback should be used to access the value of the previous method in the chain. Consider the following example:
this.remote
.get('http://example.com')
.findByTagName('h1')
.getVisibleText()
.then(function (text) {
assert.strictEqual(text, 'Example Domain');
})
.end();
This simple test gets an HTML page, finds the first <h1>
element, and asserts that the visible text is equal to a known value. A then
callback is needed in this case to access the visible text value after the call to getVisibleText
.
When to use .end
When writing functional tests, Command methods can be divided into two different types of operations: session
operations and element
operations. Session operations act on the entire browser and include tasks like navigating to a given URL, moving the mouse cursor, or getting and setting local storage keys. Element operations act on a specific element on the currently-loaded page and include tasks such as getting visible text, typing into form fields, and accessing HTML attributes.
Session interactions can be performed at any time within a test, from anywhere within the Command chain. The mouse can be moved regardless of the command that came previously, for instance. Conversely, element interactions first require that one or more elements be retrieved. This is usually done by using any of the find
or findAll
methods, the getActiveElement
method, or by returning elements from the execute
or executeAsync
callbacks. Consider the same example from above once more:
this.remote
.get('http://example.com')
// Pushes the
<h1> element onto the context stack
.findByTagName('h1')
.getVisibleText()
.then(function (text) {
assert.strictEqual(text, 'Example Domain');
})
// Pops the
<h1> off of the context stack
.end();
The call to get
is considered a session interaction since it is page-wide and does not operate on a specific element. After the page has loaded, the first <h1>
element is retrieved. All commands thereafter, such as getVisibleText
, are considered element interactions since they operate on this <h1>
element. Any operation that retrieves an element, like findByTagName
, push that element onto the context stack. This means that subsequent operations that use an element, like click
, will operate on the top-level element in the stack. Calling end
just pops the most recent element off the stack. Multiple elements can be popped off with end(count)
, and end(Infinity)
will pop all elements off the stack.
Async flexibility
The end
and then
APIs provide significant asynchronous flexibility within Intern’s functional testing APIs, while keeping the overall API as simple and lightweight as possible. Understanding how the context stack, Command chain, session and element operations work provide key insight into using these APIs efficiently.
Expert support
We answer questions like this for our customers in SitePen Support along with providing bug fixes, advice, and front-end web development for the most popular developer tools and libraries. Contact us to learn more about how we can support your development team!