Selector.addCustomDOMProperties Method

Adds custom properties to the Selector object.

Use the addCustomDOMProperties method to retrieve DOM element properties not included in the standard Selector API.

Selector().addCustomDOMProperties({
    property1: fn1,
    property2: fn2,
    /* ... */
}) → Selector
Parameter Type Description
property1, property2, … String Property name
fn1, fn2, … Function Client-side function that calculates the property value.

Functions that calculate property values (fn1, fn2, …) accept the following parameter:

Parameter Type Description
node Object The DOM node.

Example

import { Selector } from 'testcafe';

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

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

    await t.expect(label.innerHTML).contains('input type="checkbox" name="remote"');
});

If you use TypeScript, extend the Selector interface with your custom properties:

interface CustomSelector extends Selector {
    innerHTML: Promise<any>;
}

interface CustomSnapshot extends NodeSnapshot {
    innerHTML: string;
}

// via selector property
const label = <CustomSelector>Selector('label').addCustomDOMProperties({
    innerHTML: el => el.innerHTML
});

await t.expect(label.innerHTML).contains('input type="checkbox" name="remote"');

// via element snapshot
const labelSnapshot = <CustomSnapshot>await label();

await t.expect(labelSnapshot.innerHTML).contains('input type="checkbox" name="remote"');

Propagation

TestCafe propagates all custom properties and methods down every selector chain.

import { Selector } from 'testcafe';

fixture`Selector.addCustomDOMProperties propagation`
    .page`https://devexpress.github.io/testcafe/example/`;

test('Propagate custom properties', async t => {
    const div = Selector('div').addCustomDOMProperties({
        innerHTML: el => el.innerHTML,
    });

    await t
        .expect(div.innerHTML).contains('<header>')
        .expect(div.nth(2).innerHTML).contains('<fieldset>')
        .expect(div.withText('Submit').innerHTML).contains('<button');
});

Additions to your Selector chain change the target of your custom method. You may unwittingly request a property that does not exist on the client. Make sure your code does not fail due to propagation:

import { Selector } from 'testcafe';

fixture`Selector.addCustomDOMProperties propagation in the chain`
    .page`https://devexpress.github.io/testcafe/example/`;

test('Check Label HTML', async t => {
    const fieldSet = Selector('fieldset')
        .addCustomDOMProperties({
            legend: el => el.querySelector('legend').innerText,
        })
        .addCustomMethods({
            getLabel: (el, idx) => {
                return el[0].elements[idx].labels[0];
            },
        }, {
            returnDOMNodes: true,
        });

    // This assertion passes.
    await t.expect(fieldSet.nth(1).legend).eql('Which features are important to you:');

    // This line throws an error.
    try {
        await t.expect(fieldSet.nth(1).getLabel(3).textContent)
            .eql('Easy embedding into a Continuous integration system');
    }
    catch (err) {
        await t.expect(err.errMsg).eql('TypeError: Cannot read properties of null (reading \'innerText\')');
    }

    // When TestCafe evaluates "getLabel(3)", it also tries to propagate
    // the "legend" property to the result. So, it queries for a
    // <legend> inside the <label> element, which returns nothing.
});