Skip to content

Commit 465bdf5

Browse files
committed
Add Overview page for the new Element API.
1 parent 2a8d8df commit 465bdf5

File tree

1 file changed

+120
-89
lines changed

1 file changed

+120
-89
lines changed

docs/api/element/index.md

+120-89
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,138 @@
11
## Element APIs
22

3-
### Overview
4-
The newly added `element()` global object adds to Nightwatch 3 the functionality present in the [Selenium WebElement](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html) class.
3+
### Introduction
54

6-
It supports all the usual ways of locating elements in Nightwatch, as well as the Selenium locators built using `By()`, which is also available in Nightwatch as a global named `by()`.
5+
The new Element API introduced in Nightwatch v3 is a major upgrade from the older element APIs, making it more intuitive and convenient to find and interact with elements. It does so by offering a fluent and chainable syntax, minimizing the selector repetition and making the tests more concise and easy to read.
76

8-
### Usage
7+
The new Element API is available on the `browser.element` namespace and supports all the usual ways of locating elements in Nightwatch, as well as the Selenium locators built using `By()`, which is also available in Nightwatch as a global named `by()`.
98

10-
##### Using regular CSS (or Xpath) selectors
9+
### Usage
1110

12-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">const addButtonEl = element('button[type="submit"]');
11+
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// using regular css selectors
12+
const submitElem = browser.element.find('button[name=submit]');
13+
<br>
14+
// using Nightwatch selector objects
15+
const addButtonElem = browser.element.find({
16+
selector: '//button[@type="button"]',
17+
locateStrategy: 'xpath',
18+
index: 1
19+
});
20+
<br>
21+
// using Selenium `by` locator
22+
const addButtonElem2 = browser.element.find(
23+
by.xpath('//button[@type="button"]')
24+
);
25+
<br>
26+
// locating child elements
27+
const childChildElem = browser.element
28+
.find('.element')
29+
.find('.child-element')
30+
.find('.child-child-element');
31+
<br>
32+
// locating elements by text
33+
const newsElem = browser.element.findByText('News');
34+
<br>
35+
// use await to retrieve Selenium WebElement instance
36+
const addButtonWebElem = await addButtonElem;
1337
</code></pre></div>
1438

15-
##### Using Nightwatch selector objects
39+
### How it works?
40+
41+
All the `find()` and `findBy*()` commands in the new Element API returns `ScopedElement`, which is nothing but a wrapper around the actual result returned by these commands (`WebElement` instance in this case). This wrapper provides access to a host of commands and assertions that can be performed directly on the element and the actual result from these commands can be obtained by using `await` on them.
42+
43+
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// `find()` and `findBy*()` commands return ScopedElement,
44+
// a wrapper around the actual result, i.e., `WebElement`.
45+
const inputElem = browser.element.find('input[name=q]');
46+
<br>
47+
// This wrapper provides a host of commands and assertions
48+
// available directly on the result.
49+
inputElem.click();
50+
inputElem.sendKeys('Nightwatch.js');
51+
<br>
52+
// No need to await when performing actions or assertions
53+
// on the element.
54+
inputElem.assert.enabled();
55+
inputElem.getText().assert.equals('Nightwatch.js');
56+
<br>
57+
// Use await to retrieve the actual result from the command.
58+
const inputWebElem = await inputElem; // returns a `WebElement` instance
59+
const inputText = await inputElem.getText();
60+
const inputSize = await inputElem.getSize();
61+
</code></pre></div>
1662

17-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">const addButtonEl = element({
18-
selector: 'button[type="button"]',
19-
index: 0
20-
});
63+
Similarly, the `findAll()` and `findAllBy*()` commands return `ScopedElements`, a wrapper around the actual result from these commands, i.e., `WebElement[]` (an array of `WebElement`s). But unlike `ScopedElement`, `ScopedElements` provide two methods:
64+
65+
* **nth()**: returns a wrapper (`ScopedElement`) around the nth element of `WebElement[]`.
66+
* **count()**: returns a count of all the elements present in the actual result, i.e., `WebElement[]`.
67+
68+
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// `findAll()` and `findAllBy*()` commands return ScopedElements,
69+
// a wrapper around the actual result, i.e., `WebElement[]`.
70+
const postElems = browser.element.findAll('.post');
71+
<br>
72+
// get count of all the posts
73+
// use await to get the actual result
74+
const postElemsCount = await postElems.cound();
75+
<br>
76+
// assert that the count is 15
77+
postElems.count().assert.equals(15);
78+
<br>
79+
// assert that the 5th post contains "nightwatch" text
80+
const post5Elem = postElems.nth(4); // 0-based indexing
81+
post5Elem.getText().assert.contains("nightwatch");
82+
<br>
83+
// click on the 2nd post
84+
postElems.nth(1).click();
85+
<br>
86+
// `findAll` can also be chained on `find()`
87+
browser.element.find('body').findAll('.post').nth(1).findByText('Comments');
2188
</code></pre></div>
2289

23-
##### Selenium locators
90+
### Why a new API?
2491

25-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">const locator = by.css('button[type="button"]');
26-
const addButtonEl = element(locator);
27-
</code></pre></div>
92+
The older element APIs had a few shortcomings, like:
2893

29-
##### Selenium WebElements as arguments
94+
* The selector had to be passed repeatedly for every interaction or assertion with element.
95+
* No easy way to find child element apart from using complex CSS selector.
96+
* No easy way to find elements by text, role, label, etc.
97+
* It did a lookup for element for every command and assertion.
3098

31-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// webElement is an instance of WebElement class from Selenium
32-
const addButtonEl = element(webElement);
33-
</code></pre></div>
99+
The newer API aimed at fixing these shortcomings, while also make the API more concise and easy-to-use.
34100

35-
##### Retrieve the Selenium WebElement instance
101+
#### Before
36102

37-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">const addButtonEl = element('button[type="submit"]');
38-
const instance = await addButtonEl.findElement();
103+
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// no await required for normal actions
104+
browser
105+
.click('input[name=q]')
106+
.sendKeys('input[name=q]', 'Nightwatch.js');
107+
<br>
108+
// await required to get the actual command result
109+
const inputText = await browser.getText('input[name=q]');
110+
browser.assert.equal(inputText, 'Nightwatch.js');
111+
<br>
112+
browser
113+
.click('button[name=submit]')
114+
.assert.not.visible('button[name=submit]');
39115
</code></pre></div>
40116

41-
42-
### API Commands
43-
All the existing methods from a regular [WebElement](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html) instance are available. If a method is called, it will be added to the Nightwatch queue accordingly.
44-
45-
**Available element commands:**
46-
- [.clear()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#clear)
47-
- [.click()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#click)
48-
- [.findElement()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#findElement)
49-
- [.findElements()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#findElements)
50-
- [.getAttribute()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getAttribute)
51-
- [.getCssValue()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getCssValue)
52-
- [.getDriver()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getDriver)
53-
- [.getId()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getId)
54-
- [.getRect()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getRect)
55-
- [.getTagName()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getTagName)
56-
- [.getText()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getText)
57-
- [.isDisplayed()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isDisplayed)
58-
- [.isEnabled()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isEnabled)
59-
- [.isSelected()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isSelected)
60-
- [.sendKeys()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#sendKeys)
61-
- [.submit()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#submit)
62-
- [.takeScreenshot()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#takeScreenshot)
63-
64-
### Working Example
65-
66-
The example below navigates to the AngularJS homepage and adds a new to-do item in the sample ToDo App available there.
67-
68-
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">describe('angularjs homepage todo list', function() {
69-
<br>
70-
// using the new element() global utility in Nightwatch 2 to init elements
71-
// before tests and use them later
72-
const todoElement = element('[ng-model="todoList.todoText"]');
73-
const addButtonEl = element('[value="add"]');
74-
<br>
75-
it('should add a todo using global element()', function() {
76-
// adding a new task to the list
77-
browser
78-
.navigateTo('https://angularjs.org')
79-
.sendKeys(todoElement, 'what is nightwatch?')
80-
.click(addButtonEl);
81-
<br>
82-
// verifying if there are 3 tasks in the list
83-
expect.elements('[ng-repeat="todo in todoList.todos"]').count.to.equal(3);
84-
<br>
85-
// verifying if the third task if the one we have just added
86-
const lastElementTask = element({
87-
selector: '[ng-repeat="todo in todoList.todos"]',
88-
index: 2
89-
});
90-
<br>
91-
expect(lastElementTask).text.to.equal('what is nightwatch?');
92-
<br>
93-
// find our task in the list and mark it as done
94-
lastElementTask.findElement('input', function(inputResult) {
95-
if (inputResult.error) {
96-
throw inputResult.error;
97-
}
98-
<br>
99-
const inputElement = element(inputResult.value);
100-
browser.click(inputElement);
101-
});
102-
<br>
103-
// verify if there are 2 tasks which are marked as done in the list
104-
expect.elements('*[module=todoApp] li .done-true').count.to.equal(2);
105-
});
106-
});
107-
</code></pre></div>
117+
#### Now
118+
119+
<div class="sample-test" style="max-width:800px"><pre data-language="javascript" style="padding-top: 10px" class="language-javascript"><code class="language-javascript">// no await required for normal actions
120+
const inputElem = browser.element.find('input[name=q]');
121+
inputElem.click(); // no repetition of selector
122+
inputElem.sendKeys('Nightwatch.js');
123+
<br>
124+
// await required to get the actual command result
125+
const inputText = await inputElem.getText();
126+
browser.assert.equal(inputText, 'Nightwatch.js');
127+
<br>
128+
// assertions can also be done directly
129+
// no await required as we are not storing the actual command result anywhere
130+
inputElem.getText().assert.equals('Nightwatch.js');
131+
<br>
132+
const submitElem = browser.element.find('input[name=submit]');
133+
submitElem.click();
134+
submitElem.assert.not.visible();
135+
<br>
136+
// await the element to get the `WebElement` instance.
137+
const submitWebElem = await submitElem;
138+
</code></pre></div>

0 commit comments

Comments
 (0)