TestController

The test controller object exposes the test API’s methods. The test controller is passed to each function that can run server-side test code (like test, beforeEach or afterEach).

Use the test controller to call test actions, handle browser dialogs, use the wait function, or execute assertions.

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

test('Submit data', async t => {
    await t
        .setNativeDialogHandler(() => true)
        .click('#populate')
        .click('#submit-button');

    const location = await t.eval(() => window.location);

    await t.expect(location.pathname).eql('/testcafe/example/thank-you.html');
});

The test runner uses the test controller to access the internal context required for the test API to operate. When you call selectors and client functions from Node.js callbacks, pass the test controller explicitly, because the API cannot retrieve it from the context.

Implicit Test Controller Use

You may need to call the test API from outside the test code. For instance, your page model can contain methods that perform common operations used in different tests (e.g., authentication).

Note

Promises, asynchronous functions, setTimeout/setInterval callbacks, and other asynchronous routines do not preserve the original stack frame. TestCafe cannot obtain the test controller instance from these routines.

For this reason, use imported test controllers in a synchronous context.

import { Selector } from 'testcafe';

class Page {
    constructor () {
        this.loginInput    = Selector('#login_field');
        this.passwordInput = Selector('#password');
        this.submitButton  = Selector('input[type="submit"]');
    }

    async login (t) {
        await t
            .typeText(this.loginInput, 'MyLogin')
            .typeText(this.passwordInput, 'Pa$$word')
            .click(this.submitButton);
    }
}

export default new Page();

In this example, the page model’s login method uses the test controller to perform authentication actions.

You do not have to pass the test controller object explicitly. TestCafe can implicitly resolve the test context when you import the test controller.

import { Selector, t } from 'testcafe';

class Page {
    constructor () {
        this.loginInput    = Selector('#login_field');
        this.passwordInput = Selector('#password');
        this.submitButton  = Selector('input[type="submit"]');
    }

    async login () {
        await t
            .typeText(this.loginInput, 'MyLogin')
            .typeText(this.passwordInput, 'Pa$$word')
            .click(this.submitButton);
    }
}

export default new Page();