---
title: "E2E web testing locally, remote or in the cloud (BrowserStack, SauceLabs) using TestCafé in JavaScript"
canonical: "https://docs.getxray.app/space/XRAY/301702324/E2E%20web%20testing%20locally%2C%20remote%20or%20in%20the%20cloud%20(BrowserStack%2C%20SauceLabs)%20using%20TestCaf%C3%A9%20in%20JavaScript"
format: markdown
---
# Overview

In this tutorial, we will create some E2E tests in JavaScript using [TestCafé](http://devexpress.github.io/testcafe/).

TestCafé is a Node.js utility for E2E testing that does not use Selenium WebDriver; it uses a proxy mechanism instead. Some of its features include the ability to handle automatically the wait times of page loads/XHR requests for stabler tests.

Besides being easy to setup, it runs in multiple OSes and supports almost, if not all, browsers out there. <span style="color: #404142">It supports desktop, mobile, remote, cloud and parallel testing.</span>

You may also use an IDE ([TestCafé Studio](https://testcafe-studio.devexpress.com/)) to easily record, implement and playback tests.

TestCafé is not just an automation library; it's a complete testing tool that provides assertions and a runner, to implement and run the tests.

# Requirements

- [TestCafé](http://devexpress.github.io/testcafe/)
- [testcafe-reporter-xunit](https://github.com/DevExpress/testcafe-reporter-xunit) module
- [testcafe-browser-provider-saucelabs](https://www.npmjs.com/package/testcafe-browser-provider-saucelabs) module (if using Sauce Labs)
- [testcafe-browser-provider-browserstack](https://www.npmjs.com/package/testcafe-browser-provider-browserstack) module (if using BrowserStack)

# Description

<span style="color: #000000">The following examples are taken from </span>[<span style="color: #000000">TestCafé's documentation</span>](https://devexpress.github.io/testcafe/documentation/getting-started/)<span style="color: #000000">; the first one implements a test in a very simple way although hard to maintain in the long run. </span>

<span style="color: #000000">In the second example, two tests are built using the page object pattern.</span><span style="color: #000000"> </span>

<span style="color: #000000">In TestCafé terminology, a </span>[<span style="color: #000000">fixture</span>](http://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#fixtures)<span style="color: #000000"> is essentialy a group of tests. A Javascript/TypeScript file may contain one or more fixtures and associated to each one there can be multiple tests.</span>

## <span style="color: #000000">Simple message validation after submission of input</span>

<span style="color: #000000">In this test, an input box is filled out and submitted. Afterwards, the submission message is evaluated.</span>

<span style="color: #000000">Hardcoded selectors, directly in the test code, are used to implement the test.</span>


##### **test1.js**

```javascript
import { Selector } from 'testcafe'; // first import testcafe selectors

fixture `Getting Started`// declare the fixture
    .page `https://devexpress.github.io/testcafe/example`;  // specify the start page


//then create a test and place your code there
test('My first test', async t => {
    await t
        .typeText('#developer-name', 'John Smith')
        .click('#submit-button')

        // Use the assertion to check if the actual header text is equal to the expected one
        .expect(Selector('#article-header').innerText).eql('Thank you, John Smith!');
});

```


## <span style="color: #000000">Implementing tests using page object pattern</span>

In this scenario, taken from TestCafé's documentation, we start by defining a page object.


<details>
<summary>page-model.js</summary>

```javascript
import { Selector } from 'testcafe';

const label = Selector('label');

class Feature {
    constructor (text) {
        this.label    = label.withText(text);
        this.checkbox = this.label.find('input[type=checkbox]');
    }
}

class OperatingSystem {
    constructor (text) {
        this.label       = label.withText(text);
        this.radioButton = this.label.find('input[type=radio]');
    }
}

export default class Page {
    constructor () {
        this.nameInput             = Selector('#developer-name');
        this.triedTestCafeCheckbox = Selector('#tried-test-cafe');
        this.populateButton        = Selector('#populate');
        this.submitButton          = Selector('#submit-button');
        this.results               = Selector('.result-content');
        this.commentsTextArea      = Selector('#comments');

        this.featureList = [
            new Feature('Support for testing on remote devices'),
            new Feature('Re-using existing JavaScript code for testing'),
            new Feature('Running tests in background and/or in parallel in multiple browsers'),
            new Feature('Easy embedding into a Continuous integration system'),
            new Feature('Advanced traffic and markup analysis')
        ];

        this.osList = [
            new OperatingSystem('Windows'),
            new OperatingSystem('MacOS'),
            new OperatingSystem('Linux')
        ];

        this.slider = {
            handle: Selector('.ui-slider-handle'),
            tick:   Selector('.slider-value')
        };

        this.interfaceSelect       = Selector('#preferred-interface');
        this.interfaceSelectOption = this.interfaceSelect.find('option');
    }
}
```
</details>


Two tests can easily be implemented using the previous page object, one of them iterating over a bunch of checkboxes.


##### **test2.js**

```javascript
import Page from './page-model';

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

const page = new Page();

test('Text typing basics', async t => {
    await t
        .typeText(page.nameInput, 'Peter')
        .typeText(page.nameInput, 'Paker', { replace: true })
        .typeText(page.nameInput, 'r', { caretPos: 2 })
        .expect(page.nameInput.value).eql('Parker');
});

test('Click check boxes and then verify their state', async t => {
    for (const feature of page.featureList) {
        await t
            .click(feature.label)
            .expect(feature.checkbox.checked).ok();
    }
});
```


# Running the tests

In this example, we'll run the tests locally using Chrome browser. 

```
testcafe chrome test1.js test2.js --reporter xunit > results.xml
```


In case you want ro run your tests concurrently, you can pass an argument to identify the number of sessions and the browsers to use.

```
testcafe -c 10 chrome,firefox test1.js test2.js --reporter xunit > results.xml
```


It is also possible to perform headless testing (e.g. use "chrome:headless" as the browser name, for example).

```
testcafe "chrome:headless"  test1.js test2.js --reporter xunit > results.xml
```


After running the tests and generating the JUnit XML reports (e.g., ), they can be imported to Xray (either by the REST API or through the **Import Execution Results** action within the Test Execution).

![image](media://1f02c7f2-b6c4-4ab8-861f-6b7e16bf4492)


JUnit's Test Case is mapped to a Generic Test in Jira, and the **Generic Test Definition** field contains the name of of the *fixture* concatenated with the name of the test .

The Execution Details of the Generic Test contains information about the Test Suite, which in this case contains the identification of the target environment (i.e. browser + OS).


![image](media://ffaa0da8-c5df-46ac-94a4-ea21c4efee16)


# Running tests using cloud providers

Tests can easily be run using different cloud providers and it's possible to generate a JUnit XML report with the results on a per test basis.

Both in BrowserStack and in Sauce Labs backoffice only a session/test will appear though.


To run them in the cloud using BrowserStack, the command line would be something like this (BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY environment variables would need to be defined beforehand):

```
testcafe "browserstack:Chrome@66.0:Windows 10" test1.js test2.js --reporter xunit > results.xml
```


![image](media://44f058ed-e868-439c-b68e-7902f030a042)


To run them in the cloud using Sauce Labs, the command line would be something like this (SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables would need to be defined beforehand):

```
testcafe "saucelabs:Chrome@66.0:Windows 10" test1.js test2.js --reporter xunit > results.xml
```


![image](media://601972ed-e749-4561-a3a1-4d125c0353e9)

# References

- [https://devexpress.github.io/testcafe/](https://devexpress.github.io/testcafe/)
- [https://devexpress.github.io/testcafe/documentation/getting-started/](https://devexpress.github.io/testcafe/documentation/getting-started/)
- [https://devexpress.github.io/testcafe/documentation/recipes/using-page-model.html](https://devexpress.github.io/testcafe/documentation/recipes/using-page-model.html)
- [https://github.com/DevExpress/testcafe-reporter-xunit](https://github.com/DevExpress/testcafe-reporter-xunit)
- [https://dzone.com/articles/testcafe-e2e-testing-tool](https://dzone.com/articles/testcafe-e2e-testing-tool)
- [https://testcafe-studio.devexpress.com/documentation/getting-started/](https://testcafe-studio.devexpress.com/documentation/getting-started/)
- [https://medium.com/yld-engineering-blog/evaluating-cypress-and-testcafe-for-end-to-end-testing-fcd0303d2103](https://medium.com/yld-engineering-blog/evaluating-cypress-and-testcafe-for-end-to-end-testing-fcd0303d2103)