Implementation:DevExpress Testcafe Selector API
| Knowledge Sources | |
|---|---|
| Domains | Testing, Web_Automation |
| Last Updated | 2026-02-12 04:00 GMT |
Overview
Concrete implementation of selector filtering and traversal in TestCafe through the `addAPI()` function, which decorates Selector instances with chainable methods and properties for DOM navigation and element property access.
Description
The `addAPI()` function dynamically adds a comprehensive set of filtering, traversal, and property access methods to Selector instances. These methods enable complex DOM queries through method chaining, with each method returning a new Selector. Property accessors return promises that resolve to element values. All methods are automatically available on Selector instances without requiring separate imports.
Usage
Use Selector API methods to refine selections and navigate the DOM. Chain methods to build complex queries. Access properties to extract element information for assertions.
Code Reference
Source Location
- Repository: testcafe
- File: src/client-functions/selectors/add-api.js
- Lines: 1-802
Signature
// Filtering methods
selector.nth(index: number): Selector
selector.withText(text: string | RegExp): Selector
selector.withExactText(text: string): Selector
selector.withAttribute(name: string, value?: string | RegExp): Selector
selector.filter(cssSelector: string | Function, dependencies?: object): Selector
selector.filterVisible(): Selector
selector.filterHidden(): Selector
// Traversal methods
selector.find(cssSelector: string | Function, dependencies?: object): Selector
selector.parent(index?: number | cssSelector?: string | Function): Selector
selector.child(index?: number | cssSelector?: string | Function): Selector
selector.sibling(index?: number | cssSelector?: string | Function): Selector
selector.nextSibling(index?: number | cssSelector?: string | Function): Selector
selector.prevSibling(index?: number | cssSelector?: string | Function): Selector
// Property accessors (return promises)
selector.count: Promise<number>
selector.exists: Promise<boolean>
selector.visible: Promise<boolean>
selector.innerText: Promise<string>
selector.textContent: Promise<string>
selector.value: Promise<string>
selector.checked: Promise<boolean>
selector.tagName: Promise<string>
selector.id: Promise<string>
selector.classNames: Promise<string[]>
selector.attributes: Promise<object>
selector.style: Promise<object>
selector.boundingClientRect: Promise<object>
Import
// Methods automatically available on Selector instances
import { Selector } from 'testcafe';
const selector = Selector('.item');
// All API methods are available immediately
selector.nth(0).visible;
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| index | number | Varies | Zero-based index for nth(), child(), sibling() methods |
| text | RegExp | Varies | Text content to filter by in withText() and withExactText() |
| name | string | Yes (for withAttribute) | Attribute name to filter by |
| value | RegExp | No | Attribute value to match (optional for existence check) |
| cssSelector | Function | Varies | CSS selector or filter function for filter(), find(), parent(), child(), sibling() |
| dependencies | object | No | Variables to inject into filter/find functions |
Outputs
| Name | Type | Description |
|---|---|---|
| selector | Selector | New selector instance with refined selection (for methods) |
| value | Promise<T> | Promise resolving to property value (for properties) |
Usage Examples
Index Filtering with nth()
import { Selector } from 'testcafe';
fixture`Filtering`
.page`https://example.com`;
test('Select by index', async t => {
const items = Selector('.list-item');
const firstItem = items.nth(0);
const thirdItem = items.nth(2);
await t
.expect(firstItem.textContent).contains('First')
.click(thirdItem);
});
Text Filtering
import { Selector } from 'testcafe';
test('Filter by text', async t => {
const buttons = Selector('button');
const submitButton = buttons.withText('Submit');
const deleteButton = buttons.withExactText('Delete');
const saveButtons = buttons.withText(/^Save/);
await t
.click(submitButton)
.expect(deleteButton.visible).ok();
});
Attribute Filtering
import { Selector } from 'testcafe';
test('Filter by attribute', async t => {
const inputs = Selector('input');
const requiredInputs = inputs.withAttribute('required');
const emailInput = inputs.withAttribute('type', 'email');
const placeholderPattern = inputs.withAttribute('placeholder', /Enter/);
const count = await requiredInputs.count;
await t.expect(count).gt(0);
});
Custom Filter Function
import { Selector } from 'testcafe';
test('Custom filter', async t => {
const products = Selector('.product');
const expensiveProducts = products.filter(node => {
const price = parseFloat(node.querySelector('.price').textContent);
return price > 100;
});
await t.expect(expensiveProducts.count).gt(0);
});
DOM Traversal
import { Selector } from 'testcafe';
test('Navigate DOM', async t => {
const checkbox = Selector('input[type="checkbox"]').withAttribute('id', 'agree');
const label = checkbox.parent('label');
const container = checkbox.parent('.form-group');
const helpText = checkbox.nextSibling('.help-text');
// Click label to toggle checkbox
await t
.click(label)
.expect(checkbox.checked).ok();
});
Finding Descendants
import { Selector } from 'testcafe';
test('Find descendants', async t => {
const form = Selector('#registration-form');
const submitButton = form.find('button[type="submit"]');
const inputs = form.find('input');
await t
.expect(inputs.count).eql(5)
.click(submitButton);
});
Property Access
import { Selector } from 'testcafe';
test('Access properties', async t => {
const heading = Selector('h1');
const input = Selector('#username');
const checkbox = Selector('#terms');
// Properties return promises
const headingText = await heading.innerText;
const inputValue = await input.value;
const isChecked = await checkbox.checked;
const isVisible = await heading.visible;
const elementCount = await Selector('.item').count;
await t
.expect(headingText).contains('Welcome')
.expect(elementCount).gte(3);
});
Complex Chaining
import { Selector } from 'testcafe';
test('Chain multiple operations', async t => {
// Find the third visible product card with "Sale" badge,
// then find its Add to Cart button
const saleProducts = Selector('.product-card')
.filterVisible()
.withText('Sale');
const thirdSaleProduct = saleProducts.nth(2);
const addToCartButton = thirdSaleProduct.find('button.add-to-cart');
await t.click(addToCartButton);
});
Visibility Filtering
import { Selector } from 'testcafe';
test('Filter by visibility', async t => {
const allModals = Selector('.modal');
const visibleModals = allModals.filterVisible();
const hiddenModals = allModals.filterHidden();
await t
.expect(visibleModals.count).eql(1)
.expect(hiddenModals.count).gte(0);
});