TutorialsLast Updated Jun 29, 20265 min read

Automated testing for Electron applications with continuous integration

Fikayo Adepoju

Fullstack Developer and Tech Author

Visual Studio Code, one of the most popular code editors on the market at the time of writing, Slack, and the desktop version of WhatsApp all have something in common. They were all built with Electron.js. With major companies adopting Electron.js over native software development methods, Electron.js has established itself as a trustworthy framework for developing desktop applications.

No application is complete without testing, and Electron apps are not exempt from this rule. You can write tests that validate Electron.js applications are behaving as you expect them to, just like you can for browser applications. In this tutorial, I will demonstrate how to test Electron.js apps and take that a step further by automating your testing process with continuous integration (CI).

Prerequisites

To follow this tutorial, a few things are required:

  1. Basic knowledge of JavaScript
  2. Node.js installed on your system (>= 20; the latest LTS release is recommended)
  3. A CircleCI account
  4. A GitHub account

With all these installed and set up, you can begin.

Scaffolding an Electron app

We’ll be building this app from the ground up, but if you’d like to see the complete code, you can find the complete code for this project in the CIRCLECI-GWP/electron-test-app repository.

To get started, you need to use a scaffold to create a new Electron.js application. As a head start, you will be using npx with the create-electron-app package to set it up. Run this command:

npx create-electron-app electron-test-app

This scaffolds a new app inside an electron-test-app folder. This folder can be any named whatever you want.

Once the process is done, go into the root of the project (run cd electron-test-app) and run this command:

npm start

This generates a temporary build of the application based on your OS platform and boots up the app.

App first view

Both the application and the Devtools open in the same application window. You don’t want the Devtools exposed, so terminate the application by running Ctrl + C in the same CLI window where the app was run. Then go to the src/index.js file and comment out this line:

mainWindow.webContents.openDevTools();

Run the app again with npm start. This time, only the application screen is displayed with a header containing a “💖” emoji, the text Hello World!", and a paragraph with the text Welcome to your Electron application. Note the contents of the page; you will be testing for these elements soon.

Setting up testing with Playwright

Your next step is to set up the testing frameworks required to perform end-to-end (E2E) testing of your app. You only need to install one testing library for this. Playwright is a popular choice for Electron app end-to-end testing.

Terminate the running process with CTRL + C. Next, install Playwright as a development dependency using this command:

npm install --save-dev @playwright/test

Once these two are installed, you can add a test script to the package.json file. Inside the existing scripts key, add the test key/value pair from snippet:

{
    ...,
    "scripts" : {
        ....,
        "test" : "playwright test"
    }
}

Now you have all you need to run tests on your Electron.js application.

Adding tests

Now it’s time to add tests to your application.

Inside the src folder, create a new folder named __tests__. Inside the __tests__ folder, create a new file named app.spec.js. Paste this code in it:

const { test, expect, _electron: electron } = require('@playwright/test')

test('an h1 contains hello world"', async () => {
  const electronApp = await electron.launch({ args: ['.'] })

  // Wait for the first BrowserWindow to open
  const window = await electronApp.firstWindow()

  // Check for the presence of an h1 element with the text "hello"
  const headerElement = await window.$('h1')
  const headerText = await headerElement.textContent()
  expect(headerText).toBe("💖 Hello World!")

  // Close the app
  await electronApp.close()
})

The test file starts by requiring an import of what you need from Playwright.

You create a single test, which is where all the interesting stuff happens. The test launches your Electron application and waits for the window to launch. Then it checks to see if you have a header (h1) containing the string "💖 Hello World!". After it’s done, it closes the Electron application.

Save the test file and run the created tests. Run the test script at the root of the project:

npm run test

As the tests run, the application boots up, flashes the start-up screen, and then disappears. When the application screen disappears, the tests are done running.

App first view

The test passes.

Automating our tests

Time to write our CI pipeline that automates our testing process. Inside the root of the project, create a new folder named .circleci. Within this folder, create a file named config.yml and enter the following code into it:

version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:lts-browsers
    # Steps to the job
    steps:
      - checkout
      - run: npm install
      - run:
          name: Run Playwright specs
          command: xvfb-run -a npm run test

In the file above, you pull in the cimg/node Docker image, CircleCI’s Node.js convenience image. The -browsers variant adds the system libraries and the virtual display tool (xvfb) that Electron needs to launch in a headless CI environment. The npm install step installs your project’s dependencies, including Electron itself, and xvfb-run provides the display the app requires while your Playwright tests run.

Commit and push these changes to your GitHub repository.

Connecting the project to CircleCI

Now set up your Electron.js project on CircleCI so the pipeline runs on every push.

Log in to the CircleCI web app, open the Projects page from the left sidebar, and click Create Project. Give the project a descriptive name and select GitHub as the version control system.

The first time you connect GitHub, CircleCI installs its GitHub App: choose the organization that owns your repository, choose whether to grant access to all repositories or only this one, and click Install & Authorize. (Accounts created before the GitHub App may instead use the older GitHub OAuth connection. The remaining steps are the same.)

CircleCI detects the .circleci/config.yml you committed earlier and uses it for the pipeline. Click Next: Set Up Triggers, configure the project to start a pipeline on every push, and finish the setup. Your first pipeline runs on your next push to the repository.

Open the Pipelines page to watch the run. A green check on the ci/circleci: build job means it succeeded.

First pipeline running successfully on CircleCI

To see what happened, expand the pipeline and select the build job. From there you can review each step, its output, and your test results.

Your tests are now running on CircleCI.

You can add more tests as you add more features to your application. With every push to your repository, you trigger the pipeline to run all your tests automatically. When a test fails, you are notified by CircleCI and you get information about why tests are failing.

Conclusion

Test-driven development (TDD) combined with continuous integration is a great way to be sure that you have not pushed any bugs to your production environment. In this tutorial, you have learned how to test Electron.js applications and you have automated your testing process using CircleCI.

Happy coding!


Fikayo Adepoju is a LinkedIn Learning (Lynda.com) Author, Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs, and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.