Skip to content

Commit

Permalink
[docs] Fix selector and client function examples (#3855)
Browse files Browse the repository at this point in the history
  • Loading branch information
VasilyStrelyaev authored and AndreyBelym committed Aug 6, 2019
1 parent 5cfb226 commit 837d176
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 75 deletions.
2 changes: 2 additions & 0 deletions docs/articles/documentation/test-api/actions/upload.md
Expand Up @@ -11,6 +11,8 @@ Use the following actions to work with file upload input elements.
* [Populate File Upload Input](#populate-file-upload-input)
* [Clear File Upload Input](#clear-file-upload-input)

> The [t.setFilesToUpload](#populate-file-upload-input) and [t.clearUpload](#clear-file-upload-input) actions only allow you to manage the list of files for upload. These files are uploaded to the server after you initiate upload, for example, when you [click](click.md) the **Upload** or **Submit** button on a webpage.
## Populate File Upload Input

Populates the specified file upload input with file paths.
Expand Down
Expand Up @@ -7,12 +7,11 @@ permalink: /documentation/test-api/obtaining-data-from-the-client/examples-of-us

This topic describes examples of using client functions in tests.

* [Checking a Page URL](#checking-a-page-url)
* [Obtaining a Browser Alias Within a Test](#obtaining-a-browser-alias-within-a-test)
* [Accessing Child Nodes in the DOM Hierarchy](#accessing-child-nodes-in-the-dom-hierarchy)
* [Check a Page URL](#check-a-page-url)
* [Obtain a Browser Alias Within a Test](#obtain-a-browser-alias-within-a-test)
* [Complex DOM Queries](#complex-dom-queries)

## Checking a Page URL
## Check a Page URL

When performing certain actions on a tested web page (clicking buttons or links), it can redirect a browser to another page and you need to check if the desired page is open. For example, you are testing your web application's login form and need to verify if a particular web page opens after a user logs in.

Expand All @@ -35,7 +34,7 @@ test('Check the page URL', async t => {
});
```

## Obtaining a Browser Alias Within a Test
## Obtain a Browser Alias Within a Test

Sometimes you may need to determine a browser's alias from within a test. For example, you configured a test tasks using the [testCafe.createRunner](../../using-testcafe/programming-interface/testcafe.md#createrunner) function and specified several browsers where the test should run: `runner.browsers(['safari','chrome'])`. However, the test logic should be different for different browsers. To do this, you need to detect which test instance corresponds to `safari` and which one corresponds to `chrome`.

Expand Down Expand Up @@ -71,74 +70,48 @@ test('My test', async t => {
});
```

## Accessing Child Nodes in the DOM Hierarchy

You can use client functions to access child nodes in a DOM hierarchy and obtain their properties.

For instance, you are testing the *example.html* page with the following HTML code ...

```js
<!DOCTYPE html>
<html>
<body id="testedpage">
This is my tested page. <!--This is the first child node of <body>-->
<p>My first paragraph.</p>
<p>My second paragraph.</p>
</body>
</html>
```

... and you need to obtain the text content of the first child node - *This is my tested page*. You can do this within a client function using the [childNodes](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes) property. The following sample demonstrates this.

```js
import { Selector } from 'testcafe';
import { ClientFunction } from 'testcafe';

fixture `My Fixture`
.page `examplepage.html`;

const testedPage = Selector('#testedpage');

const getChildNodeText = ClientFunction(index => {
return getEl().childNodes[index].textContent;
},{ dependencies: { getEl: testedPage } });


test('My Test', async t => {
await t.expect(getChildNodeText(0)).contains('This is my tested page.');
});
```

> Note that the `getChildNodeText` client function uses the `testedPage` selector that is passed to it as a dependency. See [options.dependencies](README.md#optionsdependencies) for more information.
## Complex DOM Queries

Client functions can be helpful for complex DOM queries. For example, a tested page contains a table with some data, and you need to validate data from two columns only. To do this, obtain data from these columns, push them to an array and then compare the returned array with the expected one.

Below is a sample test for the [https://js.devexpress.com/](https://js.devexpress.com/) page that contains a grid. The sample demonstrates how to create a client function that returns an array containing data from the grid’s **Customer** and **Sale Amount** columns.

```js
import { ClientFunction} from 'testcafe';
import { ClientFunction } from 'testcafe';

fixture `Get sale amount`
.page('https://js.devexpress.com/');

const getSaleAmount = ClientFunction(() => {
const elements = document.querySelector('.dx-datagrid-rowsview').querySelectorAll('td:nth-child(3),td:nth-child(7)');
const array = [];
const getSalesAmount = ClientFunction(() => {
const grid = document.querySelector('.dx-datagrid-rowsview');
const rowCount = grid.querySelectorAll('.dx-data-row').length;
const sales = grid.querySelectorAll('td:nth-child(3)');
const customers = grid.querySelectorAll('td:nth-child(7)');

for (let i = 0; i <= elements.length - 2; i+=2) {
const customerName = elements[i+1].textContent;
const saleAmount = elements[i].textContent;
const array = [];

if (customerName && saleAmount)
array.push(`Customer ${customerName}: ${saleAmount}`);
for (let i = 0; i < rowCount; i++) {
array.push({
sales: sales[i].textContent,
customer: customers[i].textContent
});
}

return array;
});

test('My test', async t => {
console.log(await getSaleAmount());
await t
.expect(getSalesAmount()).eql([
{ sales: '$6,370', customer: 'Renewable Supplies' },
{ sales: '$4,530', customer: 'Apollo Inc' },
{ sales: '$1,110', customer: 'Johnson & Assoc' },
{ sales: '$6,600', customer: 'Global Services' },
{ sales: '$2,830', customer: 'Health Plus Inc' },
{ sales: '$6,770', customer: 'Gemini Stores' },
{ sales: '$1,460', customer: 'Discovery Systems' }
]);
});
```
```

You can use [TestCafe selectors](../selecting-page-elements/selectors/README.md) instead of the native `querySelector` and `querySelectorAll` methods. Pass these selectors to the client function's [dependencies](README.md#optionsdependencies) option and call them as regular functions. You can use chains of [selector methods](../selecting-page-elements/selectors/functional-style-selectors.md) instead of complex CSS strings for more transparent syntax.
Expand Up @@ -7,18 +7,16 @@ permalink: /documentation/test-api/selecting-page-elements/examples-of-working-w

This document shows how to work with DOM elements in frequent real-world situations.

* [Accessing Page Element Properties](#accessing-page-element-properties)
* [Getting a Page Element Using Custom Logic](#getting-a-page-element-using-custom-logic)
* [Checking if an Element is Available](#checking-if-an-element-is-available)
* [Enumerating Elements Identified by a Selector](#enumerating-elements-identified-by-a-selector)
* [Access Page Element Properties](#access-page-element-properties)
* [Get a Page Element Using Custom Logic](#get-a-page-element-using-custom-logic)
* [Access Child Nodes in the DOM Hierarchy](#access-child-nodes-in-the-dom-hierarchy)
* [Access Shadow DOM](#access-shadow-dom)
* [Check if an Element is Available](#check-if-an-element-is-available)
* [Enumerate Elements Identified by a Selector](#enumerate-elements-identified-by-a-selector)

## Accessing Page Element Properties
## Access Page Element Properties

To work with page elements, use TestCafe [selectors](selectors/README.md).
Import the `Selector` function and call it passing a CSS selector inside.
This function creates a selector object whose [API](dom-node-state.md) exposes the most used members of HTML element API.

Note that all property getters are asynchronous, so add the `await` keyword.
To work with page elements, use TestCafe [selectors](selectors/README.md). Import the `Selector` function, call it and pass a CSS selector inside. This function creates a selector object [whose API](dom-node-state.md) exposes the most used members of HTML element API.

```js
import { Selector } from 'testcafe';
Expand All @@ -34,28 +32,52 @@ test('My test', async t => {
});
```

If you need to access an element property not included in the selector's API, request it explicitly by using the [addCustomDOMProperties](selectors/extending-selectors.md) method.
If you need to access element properties not included in the selector's API, use the [selector.addCustomDOMProperties](selectors/extending-selectors.md) method to retrieve them from DOM.

```js
import { Selector } from 'testcafe';

fixture `My fixture`
.page `https://devexpress.github.io/testcafe/example/`;
.page `https://example.com`;

test('Check Label HTML', async t => {
const label = Selector('label').addCustomDOMProperties({
innerHTML: el => el.innerHTML
innerHTML: el => el.innerHTML,
tabIndex: el => el.tabIndex,
lang: el => el.lang
});

await t
.expect(label.innerHTML).contains('type="checkbox"')
.expect(label.innerHTML).contains('name="remote"');
.expect(label.tabIndex).eql(2)
.expect(label.lang).eql('en');
});
```

Note that the `await` keyword can be omitted when specifying a selector property in an [assertion](../assertions/README.md). This activates the [Smart Assertion Query Mechanism](../assertions/README.md#smart-assertion-query-mechanism).
You can also use a [client function](../obtaining-data-from-the-client/README.md) to obtain a single element property from the client. In this case, you should pass the selector to client function's [dependencies](../obtaining-data-from-the-client/README.md#optionsdependencies) option.

```js
import { Selector, ClientFunction } from 'testcafe';

fixture `My fixture`
.page `https://devexpress.github.io/testcafe/example/`;

test('Check Label HTML', async t => {
const label = Selector('label');

const getLabelHtml = ClientFunction(() => label().innerHTML, { dependencies: { label } });

await t
.expect(getLabelHtml()).contains('type="checkbox"')
.expect(getLabelHtml()).contains('name="remote"');
});
```

## Getting a Page Element Using Custom Logic
> Note that selector's property getters and client functions are asynchronous. If you need their resulting value in your code, use the `await` keyword.
>
> However, you can omit `await` when you pass a selector property or a client function value into an [assertion](../assertions/README.md). In this instance, TestCafe uses its [Smart Assertion Query Mechanism](../assertions/README.md#smart-assertion-query-mechanism) to wait until the value is available. This makes your tests more stable.
## Get a Page Element Using Custom Logic

Sometimes CSS selectors are not powerful enough to identify the required page element.
In this instance, you can introduce a function that picks the desired element.
Expand All @@ -81,7 +103,92 @@ test('My test', async t => {
});
```

## Checking if an Element is Available
## Access Child Nodes in the DOM Hierarchy

The [selector API](selectors/functional-style-selectors.md) allows you to filter matching elements and search through adjacent elements in the DOM tree.

Selector API contains two types methods:

* methods that enumerate all node types
* methods that enumerate DOM elements only

<!-- markdownlint-disable MD033 -->
Methods | Enumerated Nodes
------------------------------------------------------ | -------
[filter(function)](selectors/functional-style-selectors.md#filter)<br/>[find](selectors/functional-style-selectors.md#find)<br/>[parent](selectors/functional-style-selectors.md#parent) | All nodes
[nth](selectors/functional-style-selectors.md#nth)<br/>[withText](selectors/functional-style-selectors.md#withtext)<br/>[withExactText](selectors/functional-style-selectors.md#withexacttext)<br/>[withAttribute](selectors/functional-style-selectors.md#withattribute)<br/>[filter(string)](selectors/functional-style-selectors.md#filter)<br/>[filterVisible](selectors/functional-style-selectors.md#filtervisible)<br/>[filterHidden](selectors/functional-style-selectors.md#filterhidden)<br/>[child](selectors/functional-style-selectors.md#child)<br/>[sibling](selectors/functional-style-selectors.md#sibling)<br/>[nextSibling](selectors/functional-style-selectors.md#nextsibling)<br/>[prevSibling](selectors/functional-style-selectors.md#prevsibling) | Elements only
<!-- markdownlint-enable MD033 -->

The following example illustrates the difference between these methods and shows how to get a child text node for a given parent element.

Consider the following *example.html* page:

```js
<!DOCTYPE html>
<html>
<body>
This is my tested page. <!--This is the first child node of <body>-->
<p>My first paragraph.</p>
<p>My second paragraph.</p>
</body>
</html>
```

Let's write a test that verifies text content of the body's first child node (*'This is my tested page'*).

To select this node, use the [find](selectors/functional-style-selectors.md#find) method that enumerates all nodes. Compare it with the [child](selectors/functional-style-selectors.md#child) method that skips the text node and returns the `<p>` element.

```js
import { Selector } from 'testcafe';

fixture `My Fixture`
.page `example.html`;

const body = Selector('body');
const firstChildElement = body.child(0); // <p>
const firstChildNode = body.find((node, index) => { // text node
return index === 0;
});

test('My Test', async t => {
await t
.expect(firstChildElement.textContent).eql('My first paragraph.')
.expect(firstChildNode.textContent).eql('\n This is my tested page. ');
});
```

## Access Shadow DOM

CSS selectors passed to the [Selector](selectors/creating-selectors.md) constructor cannot identify elements in the shadow DOM.

To select a shadow element, initialize a selector with client-side code and use the [shadowRoot](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot) property to get and return the required element from shadow DOM.

The following example shows the `paragraph` selector that returns `<p>` from the shadow DOM.

```js
import { Selector } from 'testcafe';

fixture `My fixture`
.page `https://chrisbateman.github.io/guide-to-web-components/demos/shadow-dom.htm`;

const demoPage = Selector('#demo1');

const paragraph = Selector(() => {
return demoPageSelector().shadowRoot.querySelectorAll('p');
}, { dependencies: { demoPageSelector: demoPage } });

test('Get text within shadow root', async t => {
await t.click(paragraph.nth(0));

var text = await paragraph.nth(0).textContent;

await t.expect(paragraph.nth(0).textContent).eql('These paragraphs are in a shadow root.');
});
```

The `paragraph` selector obtains [shadowRoot](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot) from the `#demo1` element. The `demoPage` selector that identifies `#demo1` is passed as a [dependency](selectors/selector-options.md#optionsdependencies).

## Check if an Element is Available

Generally speaking, introducing conditions in tests is not considered good practice because it indicates that your tests are non-deterministic.
The tested website should guarantee that the test writer knows the page state at any moment. If it is so, you need no conditions in test code.
Expand All @@ -105,7 +212,7 @@ test('My test', async t => {
});
```

## Enumerating Elements Identified by a Selector
## Enumerate Elements Identified by a Selector

Another common case is creating a selector that matches several elements to perform certain actions using all of them.

Expand Down
Expand Up @@ -272,7 +272,7 @@ Method | Description
`child(cssSelector)` | Finds all child elements (not nodes) of all nodes in the matching set and filters them by `cssSelector`.
`child(filterFn, dependencies)` | Finds all child elements (not nodes) of all nodes in the matching set and filters them by the `filterFn` predicate. Use an optional `dependencies` parameter to pass functions, variables or objects to the `filterFn` function. See [Filtering DOM Elements by Predicates](#filtering-dom-elements-by-predicates).

> Important! To learn how to access child nodes, see [Accessing Child Nodes in the DOM Hierarchy](../../obtaining-data-from-the-client/examples-of-using-client-functions.md#accessing-child-nodes-in-the-dom-hierarchy).
> Important! To learn how to access child nodes, see [Access Child Nodes in the DOM Hierarchy](../examples-of-working-with-dom-elements.md#access-child-nodes-in-the-dom-hierarchy).
```js
// Selects all children of all ul elements.
Expand Down

0 comments on commit 837d176

Please sign in to comment.