Examples of Working with DOM Elements

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

Access Page Element Properties

To work with page elements, use TestCafe selectors. Import the Selector function, call it and pass a CSS selector inside. This function creates a selector object whose API exposes the most used members of HTML element API.

import { Selector } from 'testcafe';

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

test('My test', async t => {
    const element     = Selector('#developer-name');
    const clientWidth = await element.clientWidth;

    // do something with clientWidth
});

If you need to access element properties not included in the selector's API, use the selector.addCustomDOMProperties method to retrieve them from DOM.

import { Selector } from 'testcafe';

fixture `My fixture`
    .page `https://example.com`;

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

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

You can also use a client function to obtain a single element property from the client. In this case, you should pass the selector to client function's dependencies option.

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"');
});

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. In this instance, TestCafe uses its 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.

import { Selector } from 'testcafe';

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

test('My test', async t => {
    const checkBoxesStartingWithR = Selector(() => {
        let labels = document.querySelectorAll('label');

        labels = Array.prototype.slice.call(labels);

        const targetLabels = labels.filter(label => label.textContent.match(/^R/));

        return targetLabels.map(label => label.children[0]);
    });

    await t.click(checkBoxesStartingWithR.nth(0));
});

Access Child Nodes in the DOM Hierarchy

The selector API 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

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:

<!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 method that enumerates all nodes. Compare it with the child method that skips the text node and returns the <p> element.

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 constructor cannot identify elements in the shadow DOM.

To select a shadow element, initialize a selector with client-side code and use the 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.

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 from the #demo1 element. The demoPage selector that identifies #demo1 is passed as a dependency.

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.

However, in practice, things are usually a bit different. Many websites contain elements that may be invisible or non-existent at times. In this instance, it may be a good idea to check the element availability before taking actions on it.

import { Selector } from 'testcafe';

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

test('My test', async t => {
    const element = Selector('#developer-name');

    if(await element.exists && await element.visible)
        await t.typeText(element, 'Peter Parker');

    // ...
});

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.

The following example clicks through a number of check boxes on the example page.

import { Selector } from 'testcafe';

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

test('My test', async t => {
    const checkboxes    = Selector('legend').withText('Which features are important to you:').parent(0).find('input');
    const checkboxCount = await checkboxes.count;

    for(let i = 0; i < checkboxCount; i++)
        await t.click(checkboxes.nth(i));
});

More Examples

If you encounter a difficult situation while working with DOM elements, let us know by posting on StackOverflow. We review and answer questions with the TestCafe tag. If you are not alone we will add an example to this topic.