Selector.addCustomMethods Method
Adds custom client-side methods to the Selector object.
Use the addCustomMethods
method to implement Selector methods specific to your web app.
Selector().addCustomMethods({
method1: fn1,
method2: fn2,
/* ... */
}, options) → Selector
Parameter | Type | Description |
---|---|---|
method1 , method2 , … |
String | Method name. |
fn1 , fn2 , … |
Function | Client-side function that calculates the method’s return value. |
options |
Object | Options. |
Client functions (fn1
, fn2
, …) accept the following parameters:
Parameter | Type | Description |
---|---|---|
node |
Object | Array | A DOM node or an array of DOM nodes. See returnDOMNodes. |
param1 , param2 , … |
Any | Additional parameters. |
DOMNodeState objects include the values of custom user-defined methods.
Options
Option | Type | Description | Default |
---|---|---|---|
returnDOMNodes |
Boolean | true if the method returns a single DOM node; false if it returns a serializable object. |
false |
Return single DOM nodes to define targets for actions and assertions, as well as examine DOM Elements.
If some of your custom methods return arrays, and some — single nodes, call the addCustomMethods
method twice:
Example
import { Selector } from 'testcafe';
fixture`Selector.addCustomMethods`
.page`https://js.devexpress.com/`;
test('Check table', async t => {
const myTable = Selector('.dx-datagrid-table')
.nth(1)
.addCustomMethods({
getExpandButtonCell: (table, rowIndex) => {
return table[0].querySelectorAll('.dx-group-row')[rowIndex].cells[0];
},
// ...
// Other methods that return DOM nodes.
}, {
returnDOMNodes: true,
})
.addCustomMethods({
getCellText: (table, rowIndex, columnIndex) => {
return table.rows[rowIndex].cells[columnIndex].innerText;
},
// ...
// Other methods that return serializable objects.
});
await t
.expect(myTable.getCellText(3, 1)).contains('Europe')
.click(myTable.getExpandButtonCell(0))
.expect(myTable.getCellText(1, 1)).contains('North America');
});
If you use TypeScript, extend the Selector interface with your custom methods:
import { Selector } from 'testcafe';
interface CustomSelector extends Selector {
getCellText (rowIndex: number, columnIndex: number): Promise<any>;
getExpandButtonCell (rowIndex: number): SelectorPromise;
}
fixture`Selector.addCustomMethods with TS`
.page`https://js.devexpress.com/`;
test('Check table', async t => {
const myTable = <CustomSelector>Selector('.dx-datagrid-table')
.nth(1)
.addCustomMethods({
getExpandButtonCell: (table: HTMLTableElement, rowIndex: number) => {
return table[0].querySelectorAll('.dx-group-row')[rowIndex].cells[0];
},
// ...
// Other methods that return DOM nodes.
}, {
returnDOMNodes: true,
})
.addCustomMethods({
getCellText: (table: HTMLTableElement, rowIndex: number, columnIndex: number) => {
return table.rows[rowIndex].cells[columnIndex].innerText;
},
// ...
// Other methods that return serializable objects.
});
await t
.expect(myTable.getCellText(3, 1)).contains('Europe')
.click(myTable.getExpandButtonCell(0))
.expect(myTable.getCellText(1, 1)).contains('North America');
});
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.
});