End-to-end Testing using React, Jest, and the TestProject JavaScript OpenSDK by@thawkin3

End-to-end Testing using React, Jest, and the TestProject JavaScript OpenSDK

Read on Terminal Reader
react to story with heart
react to story with light
react to story with boat
react to story with money
TestProject is a free, open-source test automation platform for e2e testing that helps simplify the web, mobile, and API testing. The TestProject JavaScript OpenSDK has language support for Java, C#, Python, and, most recently, JavaScript. This article will show how we can use TestProject to test a React app with Jest as our test framework. The app is written in React and has a simple request form in which a user can enter their first name, last name, and email address.
Tyler Hawkins HackerNoon profile picture

Tyler Hawkins

Senior software engineer. Continuous learner. Educator.

linkedin social icongithub social icon

With a long list of end-to-end (e2e) test frameworks available to choose from, it’s hard to know which one you should be using. Cypress and Selenium are leading the market as the most widely used options, but there’s also Appium for mobile app testing, Puppeteer for automating tasks in Chrome, and Protractor for Angular and AngularJS applications, just to name a few.

Recently a newcomer has joined the pack: TestProject, a free, open-source test automation platform for e2e testing that helps simplify the web, mobile, and API testing. The TestProject SDK has language support for Java, C#, Python, and, most recently, JavaScript.

This article will show how we can use the TestProject JavaScript OpenSDK to test a React app with Jest as our test framework.

Ready to get started?

App Overview

To begin, let’s take a look at the demo app that we’ll be testing. This app is relatively straightforward: just a simple request form in which a user can enter their first name, last name, and email address.


Demo app: request form

If the form is submitted without being properly filled out, error messages are shown below each invalid input.


Demo app: invalid input

Upon successful form submission, the app shows some confirmation text.


Demo app: filling out the form


Demo app: confirmation page

Simple enough, right? If you’d like to see the demo in action, you can find the demo app hosted here or view the source code on GitHub.

Now, let’s look at how the app was made.

Creating the React App

As noted above, this app is written in React. To simplify the boilerplate code and dev tooling, I used the create-react-app tool to bootstrap the app.

npx create-react-app testproject-demo

With the skeleton app generated, I then removed the default app content and wrote a simple form component in a file called

. Here’s the request form code reproduced in full:

import React, { useState } from 'react'
import './RequestForm.css'

export const RequestForm = () => {
  const [firstName, setFirstName] = useState('')
  const [lastName, setLastName] = useState('')
  const [email, setEmail] = useState('')

  const handleFirstNameChange = e => {

  const handleLastNameChange = e => {

  const handleEmailChange = e => {

  const [firstNameError, setFirstNameError] = useState('')
  const [lastNameError, setLastNameError] = useState('')
  const [emailError, setEmailError] = useState('')

  const [submitted, setSubmitted] = useState(false)

  const handleSubmit = e => {

    setFirstNameError(firstName ? '' : 'First Name field is required')
    setLastNameError(lastName ? '' : 'Last Name field is required')
    setEmailError(email ? '' : 'Email field is required')

    if (firstName && lastName && email) {

  return submitted ? (
    <p id="submissionConfirmationText">
      Thank you! We will be in touch with you shortly.
  ) : (
    <form className="requestForm" onSubmit={handleSubmit}>
      <div className={`formGroup${firstNameError ? ' error' : ''}`}>
        <label htmlFor="firstName">First Name</label>
      {firstNameError && (
        <p className="errorMessage" id="firstNameError">
      <div className={`formGroup${lastNameError ? ' error' : ''}`}>
        <label htmlFor="lastName">Last Name</label>
      {lastNameError && (
        <p className="errorMessage" id="lastNameError">
      <div className={`formGroup${emailError ? ' error' : ''}`}>
        <label htmlFor="email">Email</label>
      {emailError && (
        <p className="errorMessage" id="emailError">
      <button type="submit" id="requestDemo">
        Request Demo

As you can see, we have a function component that displays three inputs for the user’s first name, last name, and email address. There is a “Request Demo” submit button at the bottom of the form. When the form is submitted, error messages are shown if there are any invalid inputs, and a confirmation message is shown if the form is submitted successfully.

That’s really all there is to the app. Now, on to the fun part. How can we configure our end-to-end tests with TestProject?

Getting Started with TestProject

To begin, we’ll need to first create a free TestProject account. After that, we can download the TestProject agent. There are options to either download the agent for desktop or for Docker. Which one you choose is up to you, but I chose to download the desktop app for Mac. You then need to register your agent to link your agent to your TestProject account.

Next, we’ll generate a developer token to use in our project. Once we have a developer token, we’ll create an 

file in the root directory of our project and add the following line of code to store our token in the
environment variable:


You’ll note that we tell Git in our 

file to ignore our 
file so that our token or other environment secrets don’t get committed into our version control and accidentally shared with others.

Finally, we’ll need to install a couple of npm packages as

to use the TestProject JavaScript OpenSDK in our app:

yarn add --dev @tpio/javascript-opensdk selenium-webdriver

With that, we’ve laid most of the groundwork to begin using TestProject with our e2e tests.

Configuring Jest

Next up, we need to configure Jest. Since we used create-react-app to bootstrap our app, our project uses react-scripts to run Jest and React Testing Library with some default configuration options. However, it’d be nice if we could configure Jest and add a few more npm scripts to be able to run unit tests and e2e tests separately.

To do this, I added the following npm scripts to the “scripts” section of my

file. Each one contains some specific Jest CLI configuration options:

"scripts": {
  ...other scripts here
  "start": "react-scripts start",
  "test:e2e": "wait-on http://localhost:3000/testproject-demo/build/ && react-scripts test --testPathPattern=\"(\\.|/)e2e\\.(test|spec)\\.[jt]sx?$\" --testTimeout=30000 --runInBand --watchAll=false",
  "test:e2e:ci": "run-p start test:e2e",
  "test:e2e:watch": "wait-on http://localhost:3000/testproject-demo/build/ && react-scripts test --testPathPattern=\"(\\.|/)e2e\\.(test|spec)\\.[jt]sx?$\" --testTimeout=30000 --runInBand",
  "test:unit": "react-scripts test --testPathPattern=\"(\\.|/)unit.(test|spec)\\.[jt]sx?$\" --watchAll=false",
  "test:unit:coverage": "react-scripts test --testPathPattern=\"(\\.|/)unit.(test|spec)\\.[jt]sx?$\" --watchAll=false --coverage",
  "test:unit:watch": "react-scripts test --testPathPattern=\"(\\.|/)unit.(test|spec)\\.[jt]sx?$\""

That’s a lot to take in! Let’s break down each of these commands while highlighting some of the key pieces of this code.

First, we see the

script. That one is easy enough: it runs our app locally in development mode. This is important because e2e tests require the app to be running to work properly.

Next, we see the

script. This command waits for the app to run locally on port 3000 before running any tests. It then uses the
test command to run our app’s tests but with several Jest CLI configuration options applied.


option tells Jest to only run our tests that end in
(and a few other variations). The
option increases Jest’s default timeout of 5 seconds per test to 30 seconds per test since e2e tests take a little longer to run than simple unit tests.


option tells Jest to run our test files serially instead of in parallel since we only have one TestProject agent installed on our machine.

And finally, the

option makes it so that the tests do not run in “watch” mode, which is the default setting for Jest with react-scripts. Whew, that was a lot!

The third script is

. This command is a combination of the
commands to help simplify the testing process. In order to use the original
command, we first must be running the app locally.

So we’d need to first run

yarn start
and then run
yarn test:e2e
. That’s not a huge deal, but now we have an even simpler process in which we can just run
yarn test:e2e:ci
to both start the app and run the e2e tests.

The fourth script,

, is very similar to the
script but runs the tests in “watch” mode in case you want your tests to be continuously running in the background as you make changes to your app.

The last three scripts are for running unit tests. The

script runs the unit tests with Jest and React Testing Library and only looks for tests that end in
(and a few other variations).


script runs those same unit tests but also includes a test coverage report. And finally, the
script runs the unit tests in watch mode.

This may seem like a lot of information to take in, but the takeaway here is that we’ve now created several helpful npm scripts that allow us to easily run our unit and e2e tests with short and simple commands.

All of the hard configuration work is out of the way, so now we can focus on writing the actual tests.

Writing Tests with the JavaScript OpenSDK

We now have Jest and TestProject configured for our project, so we’re ready to write our first e2e test. End-to-end tests typically focus on critical workflows of the app represented by user journeys.

For our request form, I can think of two important user journeys: when a user tries to submit an invalid form and when a user successfully submits a properly filled out form. Let’s write an e2e test for each workflow.

Our complete

file looks like this:

import { By } from 'selenium-webdriver'
import { Builder } from '@tpio/javascript-opensdk'

describe('App', () => {
  const testUrl = 'http://localhost:3000/testproject-demo/build/'

  let driver

  beforeEach(async () => {
    driver = await new Builder()
      .withProjectName('TestProject Demo')
      .withJobName('Request Form')

  afterEach(async () => {
    await driver.quit()

  it('allows the user to submit the form when filled out properly', async () => {
    await driver.get(testUrl)
    await driver.findElement(By.css('#firstName')).sendKeys('John')
    await driver.findElement(By.css('#lastName')).sendKeys('Doe')
    await driver.findElement(By.css('#email')).sendKeys('[email protected]')
    await driver.findElement(By.css('#requestDemo')).click()

    await driver

  it('prevents the user from submitting the form when not filled out properly', async () => {
    await driver.get(testUrl)
    await driver.findElement(By.css('#requestDemo')).click()

    await driver.findElement(By.css('#firstNameError')).isDisplayed()
    await driver.findElement(By.css('#lastNameError')).isDisplayed()
    await driver.findElement(By.css('#emailError')).isDisplayed()

In our first test, we ensure that a user can successfully submit the form. We navigate to our app’s url, use the

method to enter text into the three input fields, and then click the submit button. We then wait for the confirmation text to appear on the screen to validate that our submission was successful.

You’ll note that all of the selectors look just like normal Selenium selectors. You will typically find elements using CSS selectors or using the XPath selector.

In our second test, we ensure that a user is prevented from submitting the form when there are invalid inputs on the page. We first navigate to our app’s url and then immediately click the submit button without filling out any of the input fields. We then verify that the three error messages are displayed on the screen.

You’ll also note that we’ve extracted out some of the shared test setup and teardown into the

blocks. In the
block, we create our web driver for Chrome. In the
block, we quit the driver.

Running Our E2E Tests

Here’s the moment of truth: Let’s try running our end-to-end tests. In our terminal, we’ll run

yarn test:e2e:ci
to start the app and run the e2e tests. And… the two tests pass! You should see the app open on the Chrome browser, see the steps for each test be executed, and then see the test results back in the terminal:


Running our e2e tests in the terminal

TestProject even provides its own free, built-in report dashboards as well as local HTML and PDF reports so that you can see the test results within the browser. This is perfect when viewing tests that are run as part of a CI pipeline. Here is my report after running the test suite twice:


TestProject report dashboard


Well, we did it! We successfully wrote and ran end-to-end tests using React, Jest, and the TestProject JavaScript OpenSDK. Mission accomplished.

The exciting thing is that this is just the tip of the iceberg. TestProject is full of other hidden gems like the ability to write tests directly within the browser using a GUI, a plethora of add-ons for commonly needed test actions, and integrations with apps like Slack for sending test reports notifications.

Who knows what the future will hold for the world of end-to-end test automation, but TestProject is certainly a platform worth keeping your eye on.

react to story with heart
react to story with light
react to story with boat
react to story with money
. . . comments & more!