Getting started with Playwright E2E testing
Tags: testing, javascript, webdev, tutorial
Published: 2021-10-05 20:22:58

[Playwright](https://playwright.dev) is a cross-browser web automation framework by Microsoft. It is also a powerful E2E testing tool with its integrated test runner `Playwright Test`.

Some of the highlight features of Playwright:
- Support for 3 browser engines (Chromium, Firefox and WebKit)
- Write tests in **JavaScript** & TypeScript, Python, .NET and, Java
- Runs tests in parallel using worker processes that run at the same time
- Support for screenshots and videos snapshots taking
- Provides a great set of debugging tools
- Wide range of element selectors (CSS, text, X-path and more)
- Intercept network activity for stubbing and mocking network requests (e.g API calls)
- CLI tool to Record user interactions and generate JavaScript code

more...

In this post we' re going to implement two simple E2E test flows for pages that require authentication and look at how we can reuse the login state so that we don't have to repeat the same code across our tests. 

We're going to use **JavaScript** as our language of choice and the beautiful [Vuexy](https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/) admin theme as an *example* domain for testing.

**Let's start!**

## Install dependencies

Playwright and friends

```bash
# install playwright and its test runner
npm i -D @playwright/test

# install browser engines - Chromium, Firefox, and Webkit
npx playwright install

```

We're also going to need the `dotenv` package in order to load the login credentials as environment variables from a `.env` file into our test.

```bash
npm i -D dotenv
```

Let's add some useful commands into the `scripts` section of our project's `package.json`

```json
...
"scripts": {
    "test": "npx playwright test",
    "test-debug": "PWDEBUG=1 npx playwright test",
    "test-debug-windows": "set PWDEBUG=1 && npx playwright test"
},
...
```

- `test` runs the tests
- `test-debug` runs the tests in **debug mode**
- `test-debug-windows` runs the tests in **debug mode** on Windows

## Configure

Playwright uses a global configuration file to specify common settings for each test.
Create `playwright.config.js` in your project's root with the contents below

```js
module.exports = {
  globalSetup: require.resolve('./tests/global-setup.js'),
  use: {
    baseURL: 'https://pixinvent.com',
    headless: true,
    viewport: { width: 1280, height: 720 },
    ignoreHTTPSErrors: true,
    screenshot: 'only-on-failure',
    timeout: 30000,
  }
};
```

These are some commonly used options for various scenarios.

- `baseURL` Define a common Base URL, this allows us to navigate by using just the path inside our tests. In our example we set it as `https://pixinvent.com`
- `globalSetup` This file will be required and run before all the tests. We'll use it to setup our login step before every test.
- `headless` Playwright runs tests in headless mode by default, change this to `false` if you want to view the tests in a "live" browser instance.
- `viewport` Controls the size of the viewport for the tests.
- `ignoreHTTPSErrors` Whether to ignore HTTPS errors during navigation.
- `screenshot` Have a screenshot taken when a test fails.
- `timeout` Time in milliseconds given to each test.

Create a `.env` file in the project's root that will hold our login credentials.

```env
LOGIN_USERNAME=admin@demo.com
LOGIN_PASSWORD=admin
```

We'll also need to insert the `.env` and `tests/state.json` files to our project's `.gitignore` file. The `tests/state.json` will be used to store the authentication state when our tests run, so we don't want that file to be tracked by Git.

`.gitignore`
```bash
# env file
.env

# Login state file
tests/state.json
```

## Create the tests

The `tests` directory will contain our tests and the `globalSetup` file.


`tests/globalSetup.js`
```js
const config = require('../playwright.config');
const { chromium } = require('@playwright/test');
require('dotenv').config();

module.exports = async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(config.use.baseURL + '/demo/vuexy-vuejs-laravel-admin-template/demo-1/login');

  await page.fill('input[type="text"]', process.env.LOGIN_USERNAME);
  await page.fill('input[type="password"]', process.env.LOGIN_PASSWORD);
  await page.click('button:has-text("Sign in")');

  await page.context().storageState({ path: 'tests/state.json' });
  await browser.close();  
};
```

Playwright uses the `globalSetup.js` file to set things up once, before running all tests. In our example we're using it to visit the login page, fill-in the username and password, click on the "Sign in" button and finally, save the authentication state to a `state.json` file that is going to be used from inside our tests.

**Let's add some sample tests**

Playwright Test uses the [expect](https://jestjs.io/docs/expect) library for test assertions, if you have used [Jest](https://jestjs.io/) you will find the syntax familiar.


`tests/dashboard.spec.js`

```js
const { test, expect } = require('@playwright/test');

test.use({ storageState: 'tests/state.json' });

test('dashboard', async ({ page }) => {
  await page.goto('/demo/vuexy-vuejs-laravel-admin-template/demo-1/dashboard/ecommerce');
  const title = await page.locator('.card.card-congratulation-medal p.card-text.font-small-3');
  await expect(title).toContainText('You have won gold medal');
});
```

In this test we' re visiting the Dashboard page and we're checking if the medal card contains the text "You have won gold medal".
Playwright provides many ways to [select elements](https://playwright.dev/docs/selectors), in our case we're using CSS selectors to find the card element in the page. 

`tests/analytics.spec.js`
```js
const { test, expect } = require('@playwright/test');

test.use({ storageState: 'tests/state.json' });

test('analytics', async ({ page }) => {
  await page.goto('https://pixinvent.com/demo/vuexy-vuejs-laravel-admin-template/demo-1/dashboard/analytics');
  await page.click('text=Add Record');
  
  const title = await page.locator('h4.invoice-title');
  await expect(title).toContainText('Invoice');
});
```

In our second test we visit the Analytics, click on the "Add Record" button and the check if the text "Invoice" appears inside a `h4` element on the next page.
Notice that we don't have to write any specific method call for navigation, Playwright auto-waits for the next page.

## Run the tests

In order to tun our tests we can use the commands we added in our `package.json`

```bash
npm run test
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsfzn8p3j3j9pnzkzche.png)

We can also run the tests in [debug mode](https://playwright.dev/docs/debug#run-in-debug-mode). This will open up the browsers in headed mode and also start the [Playwright Inspector](https://playwright.dev/docs/debug#playwright-inspector). Useful when we want to troubleshoot our tests scripts.

```bash
# For Linux and MacOS
npm run test-debug

# For Windows
npm run test-debug-windows
```

## Useful commands

Here's a list of commands that can be useful when writing and running/debugging our test scripts

```bash
# Run a single test
npx playwright test testName

# Run in headed mode
npx playwright test --headed

# Run tests using a specified browser
npx playwright test --browser=firefox

# Run tests using all three browsers
npx playwright test --browser=all
```

**That's all**
Thanks for reading and have fun testing with Playwright!