Absolute Path

Playwright cheat sheet

The locators, the actions and the assertions of the course on one page. Each part names the lesson that teaches it.

A test file

Lesson: Your first test

import { test, expect } from '@playwright/test';

test('opens the todo app', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveTitle(
    'Absolute Path Todos',
  );
});
  • page is the browser tab. Each test gets a new, clean page.
  • page.goto('/') opens the first page of the app.
  • An assertion checks a fact. When the fact is wrong, the test fails.

Find elements by role

Lesson: Find elements by role

// By role and name: the first choice
page.getByRole('button', { name: 'Add' })
page.getByRole('heading', { level: 1 })

// Act on the element
const box = page.getByRole('textbox', {
  name: 'New todo',
});
await box.fill('Buy milk');
const add = page.getByRole('button', {
  name: 'Add',
});
await add.click();
Element Role
<button> button
<a href> link
<input type="text"> textbox
<input type="checkbox"> checkbox
<h1> to <h6> heading

The name is the text that a person sees or hears for the element, such as the text of a button.

Labels, text and test ids

Lesson: Labels, text, and test ids

// A form field, by the text of its label
page.getByLabel('Email')
// A field with no label, by its placeholder
page.getByPlaceholder('Search')
// By the text in the element
page.getByText('Welcome back')
// By the attribute data-testid
page.getByTestId('cart-count')
// A name can be a regular expression
page.getByRole('heading', { name: /^Welcome/ })

Use getByRole first. Use a test id for an element that has no good role, label or text.

Narrow down a locator

Lesson: Narrow down a locator

const list = page.getByRole('list', {
  name: 'Todo list',
});
const rows = page.getByTestId('todo-item');

// An element inside another element
list.getByRole('button', { name: 'Delete' })
// Rows that contain a text
rows.filter({ hasText: 'Write a test' })
// Rows that do not contain a text
rows.filter({ hasNotText: 'Walk the dog' })
// Rows that contain another element
const ticked = page.getByRole('checkbox', {
  checked: true,
});
rows.filter({ has: ticked })

Strict mode: an action fails when its locator matches more than one element. Make the locator narrower.

Actions

Lesson: Click, type, and press keys

Action What it does
click() clicks the element
dblclick() clicks two times quickly
hover() moves the pointer over the element
fill('text') clears a text field and sets its value
clear() clears a text field
pressSequentially('text') types the text one key at a time
press('Enter') presses one key, or keys such as Control+A
check() uncheck() ticks or unticks a checkbox
selectOption('value') chooses an option of a <select> list
focus() blur() moves the focus into or out of the element

Before an action, Playwright waits until the element is visible, stable and enabled, and until no other element covers it.

Assertions

Lesson: Assertions

Assertion Checks that
toBeVisible() toBeHidden() the element is shown, or is not shown
toHaveText('...') the text is exactly this
toContainText('...') the text has this text in it
toHaveValue('...') a form field has this value
toBeChecked() a checkbox is ticked
toBeEnabled() toBeDisabled() a control can be used, or cannot
toBeEmpty() a field or an element has no content
toHaveAttribute('name', 'v') an attribute has a value
toHaveCount(3) the locator matches this many elements
toHaveURL('...') the address of the page is this
toHaveTitle('...') the title of the page is this
const alert = page.getByRole('alert');
await expect(alert).toHaveText('Wrong password.');
await expect(alert).toHaveText(/wrong/i);

// The texts of many elements, in order
await expect(rows).toHaveText(['Tea', 'Milk']);

// Soft: the test goes on after a failure
await expect.soft(alert).toBeVisible();

Waiting

Lesson: Waiting and flaky tests

const rows = page.getByTestId('todo-item');

// Wrong: one read, no waiting
expect(await rows.count()).toBe(3);

// Right: the assertion waits
await expect(rows).toHaveCount(3);

Never wait for a fixed time. A fixed wait is too short on a slow day and too long on a fast day.

// Until a loading message is gone
const loading = page.getByText('Loading');
await expect(loading).toBeHidden();
// Until the element is there
await page.getByText('Saved').waitFor();
// Until the address changes
await page.waitForURL('/account');
// Until the result of a function is right
await expect.poll(() => total()).toBe(3);
// Until a block of code stops failing
await expect(async () => {
  await expect(rows).toHaveCount(3);
}).toPass();

Tests that catch bugs

Lesson: Tests that catch bugs

A test that passes proves little. A good test fails when the app has a bug.

  • Check the result, not the action. A test that clicks and checks nothing catches nothing.
  • Use exact values. toHaveText('1 item left') finds a count that does not change. toBeVisible() does not.
  • Check what must stay and what must go. After a delete, check that the other items are still there.

Groups and hooks

Lesson: Groups and hooks

test.describe('sign in', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('shows the heading', async ({ page }) => {
    await test.step('check the title', async () => {
      // ...
    });
  });
});
Hook Runs
test.beforeEach before each test of the file or the group
test.afterEach after each test, also after a failed test
test.beforeAll one time, before the first test
test.afterAll one time, after the last test

test.skip(...) does not run a test. test.only(...) runs this test and no other. Do not save a test.only in a shared project.

Page objects

Lesson: Page objects

class ShopPage {
  constructor(page) {
    this.page = page;
    this.cards = page.getByTestId('product-card');
  }

  async open() {
    await this.page.goto('/');
  }

  async addToCart(name) {
    const card = this.cards.filter({
      hasText: name,
    });
    await card
      .getByRole('button', { name: 'Add to cart' })
      .click();
  }
}

const shop = new ShopPage(page);
await shop.open();
await shop.addToCart('Blue Mug');

A page object holds the locators and the steps of one page. A step that changes the page waits for its result.

Fixtures

Lesson: Fixtures

import { test as base } from '@playwright/test';

const test = base.extend({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.open(); // the setup
    await use(loginPage);   // the test runs here
    // The cleanup goes here.
  },
});

test('signs in', async ({ loginPage }) => {
  // ...
});

A test gets a fixture when it lists the name of the fixture in its arguments. The code after use runs also after a failed test.

Mock the network

Lesson: Mock the network

// Set the route before the page loads.
await page.route('**/api/todos', (route) =>
  route.fulfill({ json: [] }),
);
await page.goto('/');
Call What the page sees
route.fulfill({ ... }) an answer that you write: json, status, body
route.abort() a network failure
route.continue() the real answer of the server

Use a mock for rare states, for errors such as the status 500, and for a page that must not depend on a server.

Requests and the API

Lesson: Wait for requests and call the API

// Start to wait before the click.
const responsePromise = page.waitForResponse(
  (res) => res.url().includes('/api/cart'),
);
await addButton.click();
const response = await responsePromise;
expect(response.ok()).toBeTruthy();

Call the API without a page

test('lists products', async ({ request }) => {
  const res = await request.get('/api/products');
  expect(res.ok()).toBeTruthy();
  expect(await res.json()).toHaveLength(6);
});

On your own computer

Lesson: Playwright on your own computer

npm init playwright@latest        # a new project
npx playwright test               # all tests
npx playwright test --headed      # show the browser
npx playwright test --ui          # UI mode
npx playwright test --debug       # step by step
npx playwright test -g "sign in"  # by title
npx playwright test --last-failed
npx playwright show-report
npx playwright show-trace trace.zip
npx playwright codegen https://example.com

In playwright.config.js:

  • use.baseURL is the address of your app, so that page.goto('/') works.
  • retries runs a failed test again. A test that fails and then passes is flaky.
  • use.trace records a trace for a look at a failed run.
  • projects runs the tests in each browser.
  • webServer starts your app before the tests.
  • forbidOnly fails the run when a test.only is in the code.