diff --git a/docs/api/element/index.md b/docs/api/element/index.md index 24f6b13e..e73db886 100644 --- a/docs/api/element/index.md +++ b/docs/api/element/index.md @@ -1,107 +1,138 @@ ## Element APIs -### Overview -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. +### Introduction -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()`. +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. -### Usage +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()`. -##### Using regular CSS (or Xpath) selectors +### Usage -
const addButtonEl = element('button[type="submit"]');
+// using regular css selectors
+const submitElem = browser.element.find('button[name=submit]');
+
+// using Nightwatch selector objects
+const addButtonElem = browser.element.find({
+ selector: '//button[@type="button"]',
+ locateStrategy: 'xpath',
+ index: 1
+});
+
+// using Selenium `by` locator
+const addButtonElem2 = browser.element.find(
+ by.xpath('//button[@type="button"]')
+);
+
+// locating child elements
+const childChildElem = browser.element
+ .find('.element')
+ .find('.child-element')
+ .find('.child-child-element');
+
+// locating elements by text
+const newsElem = browser.element.findByText('News');
+
+// use await to retrieve Selenium WebElement instance
+const addButtonWebElem = await addButtonElem;
-##### Using Nightwatch selector objects
+### How it works?
+
+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.
+
+// `find()` and `findBy*()` commands return ScopedElement,
+// a wrapper around the actual result, i.e., `WebElement`.
+const inputElem = browser.element.find('input[name=q]');
+
+// This wrapper provides a host of commands and assertions
+// available directly on the result.
+inputElem.click();
+inputElem.sendKeys('Nightwatch.js');
+
+// No need to await when performing actions or assertions
+// on the element.
+inputElem.assert.enabled();
+inputElem.getText().assert.equals('Nightwatch.js');
+
+// Use await to retrieve the actual result from the command.
+const inputWebElem = await inputElem; // returns a `WebElement` instance
+const inputText = await inputElem.getText();
+const inputSize = await inputElem.getSize();
+
-const addButtonEl = element({
- selector: 'button[type="button"]',
- index: 0
-});
+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:
+
+* **nth()**: returns a wrapper (`ScopedElement`) around the nth element of `WebElement[]`.
+* **count()**: returns a count of all the elements present in the actual result, i.e., `WebElement[]`.
+
+// `findAll()` and `findAllBy*()` commands return ScopedElements,
+// a wrapper around the actual result, i.e., `WebElement[]`.
+const postElems = browser.element.findAll('.post');
+
+// get count of all the posts
+// use await to get the actual result
+const postElemsCount = await postElems.cound();
+
+// assert that the count is 15
+postElems.count().assert.equals(15);
+
+// assert that the 5th post contains "nightwatch" text
+const post5Elem = postElems.nth(4); // 0-based indexing
+post5Elem.getText().assert.contains("nightwatch");
+
+// click on the 2nd post
+postElems.nth(1).click();
+
+// `findAll` can also be chained on `find()`
+browser.element.find('body').findAll('.post').nth(1).findByText('Comments');
-##### Selenium locators
+### Why a new API?
-const locator = by.css('button[type="button"]');
-const addButtonEl = element(locator);
-
+The older element APIs had a few shortcomings, like:
-##### Selenium WebElements as arguments
+* The selector had to be passed repeatedly for every interaction or assertion with element.
+* No easy way to find child element apart from using complex CSS selector.
+* No easy way to find elements by text, role, label, etc.
+* It did a lookup for element for every command and assertion.
-// webElement is an instance of WebElement class from Selenium
-const addButtonEl = element(webElement);
-
+The newer API aimed at fixing these shortcomings, while also make the API more concise and easy-to-use.
-##### Retrieve the Selenium WebElement instance
+#### Before
-const addButtonEl = element('button[type="submit"]');
-const instance = await addButtonEl.findElement();
+// no await required for normal actions
+browser
+ .click('input[name=q]')
+ .sendKeys('input[name=q]', 'Nightwatch.js');
+
+// await required to get the actual command result
+const inputText = await browser.getText('input[name=q]');
+browser.assert.equal(inputText, 'Nightwatch.js');
+
+browser
+ .click('button[name=submit]')
+ .assert.not.visible('button[name=submit]');
-
-### API Commands
-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.
-
-**Available element commands:**
-- [.clear()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#clear)
-- [.click()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#click)
-- [.findElement()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#findElement)
-- [.findElements()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#findElements)
-- [.getAttribute()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getAttribute)
-- [.getCssValue()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getCssValue)
-- [.getDriver()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getDriver)
-- [.getId()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getId)
-- [.getRect()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getRect)
-- [.getTagName()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getTagName)
-- [.getText()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#getText)
-- [.isDisplayed()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isDisplayed)
-- [.isEnabled()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isEnabled)
-- [.isSelected()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#isSelected)
-- [.sendKeys()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#sendKeys)
-- [.submit()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#submit)
-- [.takeScreenshot()](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html#takeScreenshot)
-
-### Working Example
-
-The example below navigates to the AngularJS homepage and adds a new to-do item in the sample ToDo App available there.
-
-describe('angularjs homepage todo list', function() {
-
- // using the new element() global utility in Nightwatch 2 to init elements
- // before tests and use them later
- const todoElement = element('[ng-model="todoList.todoText"]');
- const addButtonEl = element('[value="add"]');
-
- it('should add a todo using global element()', function() {
- // adding a new task to the list
- browser
- .navigateTo('https://angularjs.org')
- .sendKeys(todoElement, 'what is nightwatch?')
- .click(addButtonEl);
-
- // verifying if there are 3 tasks in the list
- expect.elements('[ng-repeat="todo in todoList.todos"]').count.to.equal(3);
-
- // verifying if the third task if the one we have just added
- const lastElementTask = element({
- selector: '[ng-repeat="todo in todoList.todos"]',
- index: 2
- });
-
- expect(lastElementTask).text.to.equal('what is nightwatch?');
-
- // find our task in the list and mark it as done
- lastElementTask.findElement('input', function(inputResult) {
- if (inputResult.error) {
- throw inputResult.error;
- }
-
- const inputElement = element(inputResult.value);
- browser.click(inputElement);
- });
-
- // verify if there are 2 tasks which are marked as done in the list
- expect.elements('*[module=todoApp] li .done-true').count.to.equal(2);
- });
-});
-
\ No newline at end of file
+#### Now
+
+// no await required for normal actions
+const inputElem = browser.element.find('input[name=q]');
+inputElem.click(); // no repetition of selector
+inputElem.sendKeys('Nightwatch.js');
+
+// await required to get the actual command result
+const inputText = await inputElem.getText();
+browser.assert.equal(inputText, 'Nightwatch.js');
+
+// assertions can also be done directly
+// no await required as we are not storing the actual command result anywhere
+inputElem.getText().assert.equals('Nightwatch.js');
+
+const submitElem = browser.element.find('input[name=submit]');
+submitElem.click();
+submitElem.assert.not.visible();
+
+// await the element to get the `WebElement` instance.
+const submitWebElem = await submitElem;
+