From TestCafe 1.0 to TestCafe 2.0: 11 Most Notable Enhancements

The summer of 2022 saw the first major TestCafe update — TestCafe 2.0. This article outlines the most valuable additions to the framework leading up to the new release.

If you’re reluctant to update TestCafe, or just haven’t kept up with the news, you might find a few reasons to upgrade in this article.

  1. Run tests in multiple browser windows (v1.11)
  2. New scroll actions (v1.14)
  3. Fire DOM Events (v1.15)
  4. Global test hooks (v1.17)
  5. Debug Selectors and Client Functions (v1.18)
  6. Manage cookies (v1.19)
  7. Test Run hooks (v1.19)
  8. Global request hooks (v1.19)
  9. API testing (v1.20)
  10. Run Chrome User Flow Replays (v1.20)
  11. New ways to skip JavaScript errors (v2.0)

Run tests in multiple browser windows (v1.11)

TestCafe can switch between multiple browser windows in the course of a single test. You can test websites with pop-up windows and OAuth login forms, debug complex multi-window applications, or run multiple instances of the same web app side-by-side.

When your page launches a new window, the test automatically continues in the newly opened window. When that window is closed, the test switches back to its parent.

image

New scroll actions (v1.14)

When you execute a page action, TestCafe scrolls the page to reveal the action target. This approach fits most test cases, but some circumstances demand more flexibility. That’s why TestCafe includes three dedicated scroll actions.

await t.scroll('.container', 'bottomRight'); 
await t.scrollBy('.container', 500, -200) 
await t.scrollIntoView('.container')

Fire DOM Events (v1.15)

TestCafe users can now dispatch DOM events. If you need to perform a page action that TestCafe does not support out of the box, break it down into discrete DOM events, and use the t.dispatchEvent method to fire them.

The example below fires the mousedown and mouseup events to simulate a 5-second-long left-click action:

await t.dispatchEvent('.button', 'mousedown')
    .wait(5000)
    .dispatchEvent('.button', 'mouseup');

Global test hooks (v1.17)

TestCafe now allows users to set suite-wide test hooks and fixture hooks. Define a single test hook in your configuration file, and TestCafe will execute this hook before every test in your suite.

import { admin } from 'roles';

module.exports = {
    hooks: {
        test: {
            before: async t => {
                t.ctx = 'test data';

                await t.useRole(admin); 
            }
        },
    },
};

Debug Selectors and Client Functions (v1.18)

Note

This capability is experimental.

If you launch TestCafe with the --experimental-debug flag, you can debug Selectors and Client Functions in editors such as VSCode and WebStorm, as well as in Chrome Developer Tools.

testcafe chrome index.js --experimental-debug

Open the “Watch” panel of your Node.js debugger. Input Selectors and Client Functions without the await keyword to evaluate them:

Selector('body')().innerText
ClientFunction(() => location.href)()

Manage cookies (v1.19)

The TestCafe team developed a dedicated Cookie Management API. You can use the following methods to manage cookies: getCookies, setCookies and deleteCookies.

// Retrieve cookies
let cookies = await t.getCookies();

// Set cookies
await t.setCookies({
   name:  'apiCookie2',
   value: 'value2',
}, 'https://devexpress.github.io/testcafe/example/thank-you.html');

// Delete cookies
await t.deleteCookies({ domain: 'devexpress.github.io', path: '/testcafe/example/thank-you.html' });

Test Run hooks (v1.19)

You can now define test run hooks. This long-requested capability can help you set up and tear down your testing environment. The before test run hook launches as soon as you launch TestCafe. TestCafe executes the after test run hook just before the TestCafe process terminates.

import { utils } from './my-utils.js';

module.exports = {
    hooks: {
        testRun: {
            after: async ctx => {
                utils.terminateServer(ctx.serverId);
            },
        },
    },
};

Global request hooks (v1.19)

There are new ways to set request hooks, too. You can define global request hooks in your configuration file. Global request hooks apply to all the tests or fixtures in your suite.

const { RequestMock } = require('testcafe');
const mock = RequestMock()
    .onRequestTo('https://api.mycorp.com/users/id/135865')
    .respond({
        name:     'John Hearts',
        position: 'CTO',
    }, 200, { 'access-control-allow-origin': '*' })
    .onRequestTo(/internal.mycorp.com/)
    .respond(null, 404);

module.exports = {
    hooks: {
        request: mock,
    },
};

API testing (v1.20)

Perhaps, the largest of the recent additions to the TestCafe framework is its new API testing capabilities. You can incorporate API tests into your TestCafe test suite, or issue arbitrary HTTP requests for other purposes.

const responseBody = await t.request(`http://localhost:3000/helloworld`).body;

t.expect(responseBody).contains('Hello World');

Run Chrome User Flow Replays (v1.20)

Note

This capability is experimental.

Google Chrome users can record website interactions with the experimental Recorder tab. If you export these recordings as JSON files, you can play them back with TestCafe:

testcafe all my-chrome-recording.json

TestCafe generates test reports for Chrome recordings, just like it would for native test files.

New ways to skip JavaScript errors (v2.0)

TestCafe tests fail when you encounter JavaScript errors. This behavior is intentional — most JavaScript errors are signs of application malfunction. However, some third-party modules yield errors that testers cannot act upon. That’s why TestCafe introduced the skipJsErrors setting.

Previously, TestCafe users could only ignore JavaScript errors on a per-run basis. In TestCafe 2.0, you can ignore errors for particular fixtures, tests, or parts of tests. What’s more, you can fine-tune this process, and filter out errors with particular messages, call stacks and URLs.

fixture `Authentication tests`
    .page `https://devexpress.github.io/testcafe/`
    .skipJsErrors({
        message: /.*User.*/ig
    });