Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Getting started with Playwright with Jest and TypeScript

December 02, 2020
typescript

Playwright with Jest and TypeScript

Playwright is a newish end-to-end cross-browser testing tool from Microsoft. I usually use Cypress for these kinds of tests on SPAs, but Playwright is quickly gaining traction, so I thought it was time to give it a try.

We’ll cover how to get up and running with Playwright using Jest as the test runner and how to set up the project so that we can use TypeScript to write the tests. Towards the end of the post, we’ll cover how to debug tests in VS Code.

Installing

To install Playwright, run the following command in a terminal:

npm install --save-dev playwright

Adding TypeScript

Playwright tests are written in JavaScript by default. To write our tests in TypeScript, we need to install TypeScript into our project:

npm install --save-dev typescript

Let’s add the following tsconfig.json to configure TypeScript:

{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "strict": true,
    "module": "commonjs",
    "noEmit": true
  },
  "include": ["src"]
}

This assumes our test files will be in the src folder.

Adding Jest

By default, Playwright tests are executed with Node. Adding Jest gives us a test runner to work with that has a great API. Let’s install it:

npm install --save-dev jest

We also need to install ts-jest and Jest’s types because we want to use TypeScript:

npm install --save-dev ts-jest @types/jest

There is also a Jest preset for Playwright that we are going to leverage:

npm install --save-dev jest-playwright-preset

Let’s add a jest configuration file called jest.config.js that has the following content:

module.exports = {
  preset: "jest-playwright-preset",
  testMatch: ["**/__tests__/**/*.+(ts|js)", "**/?(*.)+(spec|test).+(ts|js)"],
  transform: {
    "^.+\\.(ts)$": "ts-jest",
  }
};

Here we have told Jest to use the preset we just installed. We have also told it to pick up TypeScript files (as well as JavaScript files) and process them with ts-jest.

The last step before writing a test is to add an npm script to run the tests in package.json:

{
  ...,
  "scripts": {
    "test": "jest",
    "test.watch": "jest --watchAll"
  },
  ...
}

Writing a simple Playwright test

We will write a straightforward test on the home page of this blog. We are going to check that the title of the page is correct.

We configured Jest to look for tests in files with names that end with .spec.ts or .test.ts. Let’s create a file in a src folder called home.test.ts with the following content:

import { chromium, Browser, Page } from "playwright";

We’ve imported chromium, which we will use later to launch Chrome. We’ve also imported TypeScript types for the browser and page we are going to interact with.

Let’s add the code that executes before and after the tests run:

let browser: Browser;
beforeAll(async () => {
  browser = await chromium.launch();
});
afterAll(async () => {
  await browser.close();
});

This code will launch Chrome before the tests are run and close it after they have all finished running.

Let’s add the following code which executes before and after each test:

let page: Page;
beforeEach(async () => {
  page = await browser.newPage();
});
afterEach(async () => {
  await page.close();
});

We are creating a new tab in the browser before each test and closing it after the test has finished running.

Now, let’s write our test:

it("Home page should have the correct title", async () => {
  await page.goto("https://www.carlrippon.com/");
  expect(await page.title()).toBe("All posts | Building SPAs");
});

Let’s run the test by entering the following command in a terminal:

npm test

The test successfully runs.

Passing test

Nice! 😀

The code in the beforeAll, afterAll, beforeEach, and afterEach functions is code we’ll need to write in each test file. Also, our test is only executing on Chrome - ideally, we want to have our test run across all the browsers that Playwright supports.

jest-playwright-preset enables us to remove this boilerplate code and to test across different browser types easily. Add the following code into jest.config.js:

module.exports = {
  ...,
  testEnvironmentOptions: {    "jest-playwright": {      browsers: ["chromium", "firefox", "webkit"]    },  }};

We’ve specified that our tests should be run on Chromium, Firefox, and Webkit.

Let’s remove the beforeAll, afterAll, beforeEach, and afterEach functions in our test. We don’t need the import statement as well.

We get a type error where page is referenced in our test. The Jest Playwright preset has created a page variable that we can use, but TypeScript doesn’t realize that.

Type error on page

We can add the following global declaration to resolve this. Add a file called globalTypes.ts in the src folder with the following content:

import { Browser, Page } from "playwright";

declare global {
  const page: Page;
  const browser: Browser;
  const browserName: string;
}

page, browser, and browserName are global variables that we can use in our tests.

The type errors are resolved now. 😀

Let’s rerun the test by entering npm test in the terminal.

All tests passing

The test is executed successfully on each browser type. 😀

Debugging our test

First, we’ll use a couple of old school approaches to debug our test.

We can output useful information to the console using console.log:

it("Home page should have the correct title", async () => {
  await page.goto("https://www.carlrippon.com/");
  console.log("page title", await page.title());  expect(await page.title()).toBe("All posts | Building SPAs");
});

We then see this output to the terminal:

Console log

We can also take screen shots at various points in the test:

it("Home page should have the correct title", async () => {
  await page.goto("https://www.carlrippon.com/");
  await page.screenshot({ path: `./screenshots/home-${browserName}.png` });  expect(await page.title()).toBe("All posts | Building SPAs");
});

The screenshots will be placed in a screenshots folder after the test has finished running.

browserName is a global variable that gives the name of the browser currently being tested.

A more productive debugging approach is to use the node debugger within VS Code. To set this up, we first need to add the following launch.json file in a .vscode folder:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Jest Tests",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": ["--runInBand"],
      "windows": {
        "program": "${workspaceFolder}/node_modules/jest/bin/jest"
      },
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

We have called our launch configuration Debug Jest Tests and we are simply telling the node process to execute Jest.

Our test has been running in headless mode so far. Let’s change it so that we can see the browsers being opened and pages being interacted with. In jest.config.js we can add the following launch option to do this:

module.exports = {
  ...,
  testEnvironmentOptions: {
    "jest-playwright": {
      browsers: ["chromium", "firefox", "webkit"],
      launchOptions: {        headless: false,      },    },
  },
  ...
};

Let’s set a breakpoint in our test by clicking in the margin on the line of code we want to break on.

If we open the run section in the left-hand panel, we’ll see our configuration next to a green triangle icon:

Run in debug mode

We can click on this green triangle icon to start the debugger.

The test will run, and we’ll see the first browser open. Eventually the test will stop on our breakpoint:

Debugging

We can step through code, watch variables, and see the call stack to diagnose problems quickly.

We also have full access to the DevTools in the browser that the test opened, which is handy for debugging visual problems.

Nice. 😀

Did you find this post useful?

Let me know by sharing it on Twitter.
Click here to share this post on Twitter

If you to learn more about TypeScript, you may find my free TypeScript course useful:

Take a look