Finding & interacting with DOM Elements
Overview
Finding elements on a page is by far one of the most common and critical functions during an end-to-end test. Nightwatch provides several techniques of locating elements and also an extensible assertion framework to perform verifications on them.
Elements are searched for from the document root, using either a CSS selector or an XPath selector. You can also use other locator strategies. Check out the Webdriver documentation for more information.
Elements are internally identified using a unique web element reference id. When interacting with elements, Nightwatch manages this step of identifying the IDs internally and uses its automatic retry mechanisms for locating the element before interacting with it or performing any assertions.
Element Retries
When interacting with elements, Nightwatch polls the DOM for a configurable duration when trying to find any element. If the element is not found, aNoSuchElementError
error is thrown.
Example
In the following example, the setValue
command internally performs the element lookup, and then calls the element that is set in the setValue
command.
module.exports = {
'Demo test ecosia.org': function (browser) {
browser
.url('https://www.ecosia.org/')
.setValue('input[type=search]', 'nightwatch')
.click('button[type=submit]')
.assert.containsText('.mainline-results', 'Nightwatch.js')
.end();
}
};
import {NightwatchTests} from 'nightwatch';
const ecosiaTest: NightwatchTests = {
'Demo test ecosia.org': () => {
browser
.url('https://www.ecosia.org/')
.setValue('input[type=search]', 'nightwatch')
.click('button[type=submit]')
.assert.containsText('.mainline-results', 'Nightwatch.js')
.end();
},
export default ecosiaTest;
Relative locators
These locators are helpful when it is not easy to construct a locator for the required element, but easy to describe spatially where the element is in relation to another element that does have an easily constructed locator.
If you want to find the password
field that exists below the username
field, you would use the following example:
module.exports = {
'Demo test ecosia.org': function (browser) {
const passwordElement = locateWith(By.tagName('input')).below(By.css('input[type=email]'));
browser
.navigateTo('https://archive.org/account/login')
.setValue(passwordElement, 'password')
.assert.valueEquals('input[type=password]', 'password');
}
};
import {NightwatchTests} from 'nightwatch';
const ecosiaTest: NightwatchTests = {
'Demo test ecosia.org': () => {
const passwordElement = locateWith(By.tagName('input')).below(By.css('input[type=email]'));
browser
.navigateTo('https://archive.org/account/login')
.setValue(passwordElement, 'password')
.assert.valueEquals('input[type=password]', 'password');
},
export default ecosiaTest;
Apart from the commands from the example, you can also use the following commands:
above
below
toRightOf
toLeftOf
near
Chaining Relative Locators
For some complex layouts, such as, where an element might exist above and to the right of the starting element, you can chain relative locators as shown in the following example code snippet:
locateWith(By.tagName('button'))
.below(By.id('email')
.toRightOf(By.css('#cancel')));
Element Properties
An element can also be specified as an object if greater flexibility in locating and interacting with elements is required. The object definition must contain at least the selector
property.
Apart from the selector
property, you can use any of the following properties:
- selector - the element selector name (e.g.: '#input-element')
- locateStrategy - e.g. 'css selector'
- index - used to target a specific element in a query that results in multiple elements returned. Normally, only the first element is used (index = 0) but using the
index
property, you can specify any element within the result. - abortOnFailure - used to overwrite this setting when using
waitForElement*
commands - timeout - used to overwrite the default timeout for when using
waitForElement*
commands or assertions - retryInterval - used to overwrite the default retry interval for when using
waitForElement*
commands or assertions - suppressNotFoundErrors - Some element commands like
.click()
or.getText()
will throw aNoSuchElement
error if the element cannot be located, causing the test to fail. If this option is set totrue
then this error is ignored.
In the earlier example, the input[type=search]
element selector returns 3 elements. If you are want to use the second element, see the following example code snippet:
module.exports = {
'Demo test ecosia.org': function (browser) {
browser.setValue({selector: 'input[type=search]', index: 1}, 'nightwatch')
}
};
import {NightwatchTests} from 'nightwatch';
const ecosiaTest: NightwatchTests = {
'Demo test ecosia.org': () => {
browser.setValue({selector: 'input[type=search]', index: 1}, 'nightwatch');
},