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; +