From Zero to Hero: Playwright Web Automation

Welcome! I am thrilled to present this comprehensive and interactive Playwright tutorial designed to take you on a learning journey from the foundational basics to advanced mastery. Whether you are new to Playwright or seeking to deepen your knowledge and expertise, this tutorial is crafted with a range of interactive elements and exercises aimed at providing a rich, hands-on learning experience.

Disclaimer: This tutorial is authored by me, Jonas Claes, in my individual capacity. While I am employed at the time of writing by Resillion, the views, thoughts, and opinions expressed in this post are solely my own and do not necessarily reflect the official policy, position, or opinions of Resillion. This tutorial is meant as a helpful resource for individuals interested in learning about Playwright and its features, and any feedback or opinions are welcomed and appreciated. The content provided is intended for informational purposes only and should be used at your own discretion.

Table of contents

You can use the table of contents below to quickly navigate around the document, as this tutorial is packed with content.

Introduction

The objective of this tutorial is to offer a structured and immersive learning path. By the end of this journey, you will be well-versed in the myriad of features Playwright offers, such as screenshot comparisons, network request mocking, advanced configurations, and much more. You will gain practical insights and skills, empowering you to leverage Playwright's capabilities effectively in your projects.

Why Playwright?

Playwright is a powerful, feature-rich automation library for browser and API testing. Its versatility makes it a valuable tool for developers, testers, and automation enthusiasts seeking to perform end-to-end testing, component testing, capture screenshots, interact with web elements, and much more, across different browsers.

Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.

https://playwright.dev/docs/intro

Learning Approach

This tutorial adopts a scaffolded learning approach, ensuring each new concept builds logically upon the last. We start with foundational concepts, offering a clear understanding of Playwright's core components, such as test suites, modules, tests, and steps. Gradually, we will delve deeper into more complex and advanced features, offering practical exercises, challenges, and interactive elements at each step. The focus is on learning by doing, ensuring you gain practical experience and immediate application of the concepts learned.

Foundations of Playwright

This is the first stepping stone in our Playwright journey! In this section, we will lay the groundwork by exploring the fundamental concepts and components of Playwright. Understanding these foundational elements is crucial as it sets the stage for more advanced topics we will delve into in the later sections.

Quick Start

Before we delve into the core concepts, let's get started by setting up Playwright in a simple, quick-start guide. This will include installing Playwright and creating a basic script to understand its working mechanism. By the end of the quick start, you'll have a running Playwright script and a glimpse of what's to come in the subsequent sections.

There are two main variants on how to use Playwright, as a library and as a test runner.

Noteworthy: https://playwright.dev/docs/intro

Prerequisites

In order to interactively follow the exercises in this tutorial, please make sure you have the following tools installed:

Playwright as a library

Create a directory in which you will be working. Next, open your IDE (preferably VS Code) in this folder and open a new terminal.

Installing Playwright into your working directory is as straightforward as running the following command:

npm i --save playwright

Next, we will write a script that opens a chromium browser, navigates to a specific page, takes a screenshot, and exits cleanly. I've written this in index.cjs.

const { chromium } = require("playwright");

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto("https://jonasclaes.be");
  await page.screenshot({ path: `screenshots/example.png` });
  await browser.close();
})();

You can run this example with the following command:

node ./index.cjs

A new chromium browser should open, navigate to https://jonasclaes.be and take a screenshot. Afterwards you should find this screenshot in your filesystem under screenshots/example.png.

You can find this example in the GitHub repository in the example/quick-start branch.

GitHub - jonasclaes/2023-tutorial-zero-to-hero-playwright at example/quick-start
From Zero to Hero: Playwright Web Automation. Contribute to jonasclaes/2023-tutorial-zero-to-hero-playwright development by creating an account on GitHub.

Nice! You've successfully started a browser and taken a screenshot using Playwright web automation!

Playwright as a test runner

Create a directory in which you will be working. Next, open your IDE (preferable VS Code) in this folder and open a new terminal.

Installing the whole Playwright Testing Framework into your working directory is as straightforward as running the following command:

npm init playwright@latest

You will now be guided through creating a project. I would like to recommend you enable the same options as I did if you will be following this tutorial.

Need to install the following packages:
  create-playwright@1.17.130
Ok to proceed? (y)
Getting started with writing end-to-end tests with Playwright:
Initializing project in '.'
√ Do you want to use TypeScript or JavaScript? · TypeScript
√ Where to put your end-to-end tests? · tests 
√ Add a GitHub Actions workflow? (y/N) · false
√ Install Playwright browsers (can be done manually via 'npx playwright install')? (Y/n) · true
Initializing NPM project (npm init -y)…
Wrote to C:\workspace\jonasclaes\2023-tutorial-zero-to-hero-playwright\package.json:

{
  "name": "2023-tutorial-zero-to-hero-playwright",
  "version": "1.0.0",
  "description": "From Zero to Hero: Playwright Web Automation",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


Installing Playwright Test (npm install --save-dev @playwright/test)…

added 3 packages, and audited 4 packages in 2s

found 0 vulnerabilities
Installing Types (npm install --save-dev @types/node)…

added 1 package, and audited 5 packages in 987ms

found 0 vulnerabilities
Downloading browsers (npx playwright install)…
Writing playwright.config.ts.
Writing tests\example.spec.ts.
Writing tests-examples\demo-todo-app.spec.ts.
Writing package.json.
✔ Success! Created a Playwright Test project at C:\workspace\jonasclaes\2023-tutorial-zero-to-hero-playwright

Inside that directory, you can run several commands:

  npx playwright test
    Runs the end-to-end tests.

  npx playwright test --ui
    Starts the interactive UI mode.

  npx playwright test --project=chromium
    Runs the tests only on Desktop Chrome.

  npx playwright test example
    Runs the tests in a specific file.

  npx playwright test --debug
    Runs the tests in debug mode.

  npx playwright codegen
    Auto generate tests with Codegen.

We suggest that you begin by typing:

    npx playwright test

And check out the following files:
  - .\tests\example.spec.ts - Example end-to-end test
  - .\tests-examples\demo-todo-app.spec.ts - Demo Todo App end-to-end tests
  - .\playwright.config.ts - Playwright Test configuration

Visit https://playwright.dev/docs/intro for more information. ✨

Happy hacking! 🎭

You should now have a directory structure that looks like this:

2023-tutorial-zero-to-hero-playwright
├── package-lock.json
├── package.json
├── playwright.config.ts
├── tests
│   └── example.spec.ts
└── tests-examples
    └── demo-todo-app.spec.ts

There are some example tests already defined. Start with the tests/example.spec.ts file. Try to understand the layout and the architecture of the framework. We will go into this deeper in a subsequent section.

To run the tests, execute the following command:

# To run the tests/example.spec.ts file, run:
npx playwright test

To run the tests-examples/demo-todo-app.spec.ts tests, you will need to move this file to the tests/ folder. By default, the Playwright configuration is set to only execute tests located under the tests/ directory.

You can find this example in the GitHub repository in the example/quick-start-test-runner branch.

GitHub - jonasclaes/2023-tutorial-zero-to-hero-playwright at example/quick-start-test-runner
From Zero to Hero: Playwright Web Automation. Contribute to jonasclaes/2023-tutorial-zero-to-hero-playwright development by creating an account on GitHub.

Please see the "Writing tests" page on the official Playwright docs to find more information:

Writing tests | Playwright
Introduction

Congratulations! You've successfully set up a new Playwright project and ran tests!

Core Concepts of Playwright

In this section I will unravel the essence of Playwright by exploring its fundamental building blocks: test suites, modules, tests, and steps. Gaining a good grasp of these concepts is crucial to building effective and efficient Playwright scripts.

Test Step

Steps in Playwright are the sequences of actions or commands within a test. Each step represents a specific interaction with the web application, such as clicking a button, filling a form, or navigating to a page. Steps are the smallest units in the testing hierarchy and serve as the building blocks for creating tests. A test step must always be located inside of a test.

You can use test steps to add structure to your code and your results.

A test step can be defined as follows:

import { test } from "@playwright/test";

await test.step('Your step name goes here', async ({}) => {
  // Your actions and assertions go here
})

Test

Tests are the individual units of testing in Playwright. Each test represents a single functionality or feature to be verified. It's within these tests that we write scripts to interact with the web application, check the responses, and validate the results.

A test can be defined without any other context but may also be defined inside of a test suite.

A test can be defined as follows:

import { test } from "@playwright/test";

test('Your test name goes here', async ({ page }) => {
  // Your test steps go here
})

Test Suites

A test suite combines multiple tests into a group. It’s like a container holding various tests related to a specific feature or a set of features, enabling organized and structured testing. This organization helps in managing and maintaining tests, especially in larger projects.

A test suite can be defined as follows:

import { test } from "@playwright/test";

test.describe('Your group name goes here', () => {
  // Your tests go here
})

Test Modules

Modules in Playwright refer to individual JavaScript or TypeScript .spec files containing one or more tests. They are units of organizing test scripts, allowing you to group related tests together logically. A well-structured module can significantly enhance the readability and maintainability of your test code.

Example test module

This is an example of a test module. Try to identify the test suite, the tests, and the test steps in the example below.

import { test } from "@playwright/test";

test.describe("Authentication", () => {
  test("As a user, I can login with valid credentials", async ({ page }) => {
    await test.step("Go to login page", async () => {});
    await test.step("Enter email", async () => {});
    await test.step("Enter password", async () => {});
    await test.step("Click Sign-in button", async () => {});
  });

  test("As a user, I cannot login with invalid credentials", async ({
    page,
  }) => {
    await test.step("Go to login page", async () => {});
    await test.step("Enter email", async () => {});
    await test.step("Enter password", async () => {});
    await test.step("Click Sign-in button", async () => {});
  });
});

You can find this file in the GitHub repository in the example/test-module branch, in the tests/example.spec.ts file.

GitHub - jonasclaes/2023-tutorial-zero-to-hero-playwright at example/test-module
From Zero to Hero: Playwright Web Automation. Contribute to jonasclaes/2023-tutorial-zero-to-hero-playwright development by creating an account on GitHub.

Configuration and Project Setup

In this section we'll explore the configuration file that Playwright provides and how we can customize this to our needs.

Configuration file

All of Playwright's configuration is stored in the playwright.config.js or playwright.config.ts file.

When Playwright launches, it reads this configuration file, and sets some internal state, which will be used when running tests. With this file we can define several projects, which can mean anything you like. For example, a project can be a different browser, but it could also be your API instead of your Front-end.

It also contains more generalized options that will be reused across all the tests, like parallelism, retries, reporting, ....

Projects

A project is a specific configuration, which can be named. The naming of this is up to you. A configuration can hold any of the following items:

  • Devices (pre-configured profiles)
  • Tracing
  • URLs
  • Credentials
  • HTTP Headers
  • Timeouts
  • Network configuration
  • Service workers
  • Timezone
  • ...

You can find a full list of supported options here:

TestProject | Playwright
Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers.
TestOptions | Playwright
Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.

Typically, you would have a couple of projects.

  • One per browser
    • Chromium
    • Firefox
    • Safari
    • ...
  • Logged in state (for reuse of authenticated session)
    • Logged in
    • Logged out
  • API
  • ...

In the example below, I've pasted an excerpt from an existing configuration that I use.

/* Configure projects for major browsers */
projects: [
  {
    name: 'chromium',
    use: { ...devices['Desktop Chrome'] },
  },
  {
    name: 'firefox',
    use: { ...devices['Desktop Firefox'] },
  },
  {
    name: 'webkit',
    use: { ...devices['Desktop Safari'] },
  },
],

This will run all the tests across 3 browsers.

Reporting

The configuration file has a specific option called reporter. The name would imply that you can only have one, however that is not correct. You can have as many reporters as you like.

Default reporter

By default, Playwright will setup a html reporter. This will generate a HTML report after each test run, which is located in playwright-report/index.html. If you open this file in a web browser, you will be greeted by a nice interactive page, containing the results from your latest test run.

If you make use of tracing, you will need to open the report a little bit differently. Run the following command to spin up a local web server and serve the report:

npx playwright show-report

This command should automatically open your browser once it's ready. If it does not automatically open a browser, navigate to the URL shown in the output.

Built-in reporters

Playwright includes the following built-in reporters:

  • List
    It prints a line for each test being run.
  • Line
    It's more concise than the list reporter. It uses a single line to report last finished test and prints failures when they occur.
  • Dot
    It's more concise than the line reporter. It only produces a single character per successful test run. It is the default on CI and useful where you don't want a lot of output.
  • HTML
    It produces a self-contained folder that contains a report for the test run that can be served as a web page.
  • Blob
    It produces a raw data dump of all the details about the test run and can be used later to produce any other report. Their primary function is to facilitate merging of reports from sharded tests.
  • JSON
    Produces an object with all information about the test run in the JSON format.
  • JUnit
    Produces a JUnit-style XML report.
  • GitHub
    Provides automatic failure annotations for GitHub Actions.

For in-depth configuration, head to the following page:

Reporters | Playwright
Playwright Test comes with a few built-in reporters for different needs and ability to provide custom reporters. The easiest way to try out built-in reporters is to pass --reporter command line option.
Extensibility

Custom reporters can be used in combination with Playwright. You can also build your own reporter, by following the instructions in the official documentation. You can find some of these custom reporters here:

Reporters | Playwright
Playwright Test comes with a few built-in reporters for different needs and ability to provide custom reporters. The easiest way to try out built-in reporters is to pass --reporter command line option.
Testmo

For example, if you want to make use of the Testmo reporter, you should follow the steps defined by the author of the reporter. Usually this will be located in the NPM package README, or in the GitHub README file, like this one:

playwright-testmo-reporter
A Playwright Reporter for the Testmo SaaS.. Latest version: 1.2.1, last published: 14 days ago. Start using playwright-testmo-reporter in your project by running `npm i playwright-testmo-reporter`. There are no other projects in the npm registry using playwright-testmo-reporter.

When you have installed the reporter, and ran the tests, a new folder called testmo should appear, containing a testmo.xml file.

You can find this example in the GitHub repository in the example/testmo-reporter branch, in the playwright.config.ts file.

GitHub - jonasclaes/2023-tutorial-zero-to-hero-playwright at example/testmo-reporter
From Zero to Hero: Playwright Web Automation. Contribute to jonasclaes/2023-tutorial-zero-to-hero-playwright development by creating an account on GitHub.

Practical Understanding of Locators

Basics of Locators

Playwright's locators are a core feature designed to streamline the interaction with web elements during automated testing. They encapsulate the logic necessary to retrieve elements, making your tests more readable and robust, especially in the face of a dynamically changing DOM. Here's an overview of the web-first locators provided by Playwright:

  1. page.getByRole(): This locator allows you to find elements based on their explicit or implicit accessibility attributes. It reflects how users and assistive technology perceive the page, thus promoting accessibility-aware testing.
  2. page.getByText(): This function enables you to locate elements based on their text content, making it intuitive and straightforward for cases where the text on the element is known.
  3. page.getByLabel(): Use this locator to find form controls based on the text of their associated labels. It's a handy function when dealing with forms and input fields.
  4. page.getByPlaceholder(): This locator helps you find input elements based on their placeholder text, which can be particularly useful in form-heavy pages.
  5. page.getByAltText(): Primarily used for locating images based on their text alternative, promoting accessibility while also providing a means to interact with image elements in your tests.
  6. page.getByTitle(): Locate elements based on their title attribute, providing a more semantic way to interact with elements that have descriptive titles.
  7. page.getByTestId(): This function allows for locating elements based on their data-testid attribute, which can be a practical approach in projects where data-testid attributes are used for testing purposes.

These web-first locators prioritize user-visible attributes and explicit contracts over more fragile, implementation-dependent details like CSS selectors or XPaths.

💡
Utilizing these locators will likely result in more durable and maintainable tests.

You can find some of these locators being used in the example in the GitHub repository in the example/quick-start-test-runner branch, in the tests-examples/demo-todo-app.spec.ts file.

Advanced Locator Strategies

As you venture into more complex testing scenarios, understanding and leveraging advanced locator strategies become indispensable. Playwright offers a variety of advanced locator strategies that extend beyond the basics, enabling more precise interactions with web elements, especially in complex or dynamic pages.

XPath and CSS Locators

While Playwright supports XPath and CSS locators, they are not the first choice due to their reliance on DOM structure. Making use of these selectors will often lead to fragile tests which are inherently flawed.

// Using a CSS Selector
const cssLocator = page.locator('.your-css-selector');

// Using an XPath
const xpathLocator = page.locator('xpath=//your-xpath');

Filtering Locators

Filtering locators allow for more precise targeting by filtering based on text, absence of text, or presence/absence of specific child elements.

// Get an element by data-testid and filter for an element containing the text "John Doe"
const filteredLocator = page.getByTestId('table-users-rows').filter({ hasText: 'John Doe' });

Matching Locators and Lists

Playwright supports matching multiple elements, which can be utilized to work efficiently with lists of elements on a page.

const items = page.locator('.list-item');
const count = await items.count();

Chaining

Chaining methods that create a locator, like page.getByText() or locator.getByRole(), help narrow down the search to a particular part of the page, providing more control in locating elements.

// Chaining two locators together
const dualChainedLocator = page.locator('.list').locator('.item');

// Chaining three locators together
const tripleChainedLocator = page.locator('.list').locator('.item').getByText('Example');

Visual Studio Code Integration

The Visual Studio Code plugin for Playwright is designed to improve your testing workflow. It offers features like smart code navigation, autocompletion for Playwright APIs, debugging capabilities, and recording capabilities. By utilizing this plugin, you can enhance your productivity, easily manage and debug your Playwright scripts, and have a more intuitive and supported environment for writing tests with Playwright.

Here is a short list of the features the extension offers:

  • Install Playwright
  • Run tests with a single click
  • Run multiple tests
  • Show browsers
  • Pick locators
  • Debug step-by-step, explore locators
  • Tune locators
  • Record new tests
  • Record at cursor

Installing the VS Code Plugin

The first step towards integrating Playwright with Visual Studio Code is installing the Playwright extension. Here's a step-by-step walkthrough:

  1. Open Visual Studio Code.
  2. Press Ctrl + Shift + X, this will open the extensions panel.
  3. Enter the following extension ID in the search field: ms-playwright.playwright.
  4. Install the Playwright Test for VSCode extension.

Install Playwright

If you don't have the Playwright NPM package installed in your project, or if you are starting with a new testing project, the "Install Playwright" action from the command panel (Ctrl + Shift + P) will help you get started.

Pick the browsers you'd like to use by default, don't worry, you'll be able to change them later to add or configure the browsers used. You can also choose to add a GitHub Action so that you can easily run tests on Continuous Integration on every pull request or push.

The extension automatically detects if you have Playwright installed and loads the browsers, known as Playwright projects, into Visual Studio Code. By default it will select the first project as a run profile. Inside the test explorer in VS Code you can change this behaviour to run a single test in multiple or different browsers.

Run tests with a single click

Click the green triangle next to the test you want to run. You can also run the test from the testing sidebar by clicking the grey triangle next to the test name.

Run multiple tests

You can use the Testing sidebar to run a single test or a group of tests with a single click. While tests are running, the execution line is highlighted. Once the line has completed, the duration of each step of the test is shown.

Progress is shown while the test is running.
Playwright action times are shown after a test has completed.

Show browsers

Check the "show browsers" checkbox to run tests with the browser open so that you can visually see what is happening while your test is running. Click on "close all browsers" to close the browsers.

Pick locators

Click the "pick locator" button and hover over the browser to see the locators available. Clicking an element will store it in the locators box in VS Code. Pressing enter will save it to the clip board so you can easily paste it into your code or press the escape key to cancel.

Debug step-by-step, explore locators

Right click the green triangle next to the test you want to debug and click "Debug test". Set a breakpoint and hover over a value. When your cursor is on some Playwright action or a locator, the corresponding element (or elements) are highlighted in the browser.

Tune locators

You can edit the source code to fine-tune locators while on a breakpoint. Test out different locators and see them highlighted in the browser.

Record new tests

Record new tests by clicking on the "record tests" button in the testing sidebar. This will open a browser window where you can navigate to a URL and perform actions on the page which will be recorded to a new test file in VS Code.

💡
This is a really useful feature if you quickly need to map out an application and gather lots of locators at once.

Record at cursor

This generates actions into the existing test at the current cursor position. You can run the test, position the cursor at the end of the test and continue generating the test.

Network Request Mocking

This section lays the foundation for understanding network request mocking in automated testing. By diving into the core concepts and practical applications of mocking network requests, you will grasp how to simulate network interactions, enabling more controlled and reliable testing scenarios. Whether it's to isolate tests from external dependencies or to replicate various network conditions, mastering network request mocking is pivotal for developing robust test suites. Through hands-on exercises and interactive discussions, we'll embark on a journey to uncover the essentials of network request mocking, setting the stage for more advanced explorations in the following sections.

Fundamentals of Network Request Mocking

Network mocking in Playwright involves intercepting network requests and responses to simulate various network conditions and server behaviours. This is achieved using the route.fulfill() method to provide custom responses, or route.continue() to modify requests before they reach the server. By mocking network interactions, you can test how your application behaves under different scenarios without relying on external servers or conditions. This feature is crucial for testing error handling, loading states, and other UI behaviours related to network activity.

Web APIs are usually implemented as HTTP endpoints. Playwright provides APIs to mock and modify network traffic, both HTTP and HTTPS. Any requests that a page does, including XHRs and fetch requests, can be tracked, modified and mocked. With Playwright you can also mock using HAR files that contain multiple network requests made by the page.

https://playwright.dev/docs/mock
Mock APIs | Playwright
Introduction

Network Mocking Techniques

In this section, we delve into various techniques of network mocking in Playwright, which is a crucial aspect of testing in modern web applications. By simulating different network scenarios, from handling typical requests and responses, to manipulating headers and routing, we can ensure our application behaves correctly under different network conditions. The following techniques provide a means to simulate, control, and analyse network interactions during testing, paving the way for robust and reliable tests. Through hands-on exercises, we'll explore how to implement these techniques in Playwright, enhancing our testing capabilities.

Basic Request and Response Mocking

Concept and Importance

Basic request and response mocking is crucial for simulating server interactions, ensuring your application behaves correctly without reliance on external servers. It's essential for testing error handling, loading states, and other network-dependent behaviours.

Code Examples and Exercises
// Example: Mocking a GET request
await page.route('https://example.com/data', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify({ key: 'value' })
  });
});

// Exercise: Mock a POST request and verify the mocked response

Dynamic Response Mocking

Explanation and Use Cases

Dynamic response mocking enables varying mocked responses based on request data, testing how the application handles different server responses.

Code Snippets and Practice Exercises
// Example: Dynamic mocking based on request data
await page.route('https://example.com/data', (route, request) => {
  const postData = request.postData();
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: postData ? JSON.stringify({ response: 'data received' }) : JSON.stringify({ error: 'no data' })
  });
});

// Exercise: Mock a request dynamically based on a query parameter

Error Simulation

Definition and Significance

Error simulation involves mocking network errors to test application resilience and error handling mechanisms.

Code Examples and Exercises
// Example: Simulating a network error
await page.route('https://example.com/data', route => {
  route.abort('failed');
});

// Exercise: Simulate a timeout error and observe how your application handles it

Header Manipulation

Concept and Importance

Header manipulation involves altering request or response headers to test application behaviour under various server-client interactions.

Code Snippets and Practice Exercises
// Example: Modifying a request header
await page.route('https://example.com/data', route => {
  route.on('request', request => {
    const headers = Object.assign({}, request.headers(), {
      'new-header': 'new-value'
    });
    request.continue({ headers });
  });
});

// Exercise: Modify a response header and verify the behavior of your application

API reverse engineering techniques

In this section, we'll delve into a methodical approach to reverse-engineering an API response using Chrome DevTools, followed by crafting mocks via the Builder pattern in TypeScript. This methodology shines when dealing with intricate nested JSON responses, enabling their dissection into smaller, logical entities, such as items within a cart, making the data structure more manageable and mimicable.

Inspecting the API Response

  • Launch Chrome, navigate to a page initiating an API call, a good example to get started on would be https://httpbin.org/get.
  • Right-click anywhere on the page, select "Inspect" to open DevTools.
  • Transition to the "Network" tab, refresh the page to log the network requests.
  • Locate and click on the desired API request, switch to the "Response" tab to view the raw JSON response.

Dissecting the Response

  • Scrutinize the JSON response to grasp its structure and the relationships among various data entities.
  • For instance, a JSON response representing a shopping cart with multiple items can be divided into two primary logical entities: Cart and Item.
  • Document the structure and data types for various segments of the response as a blueprint for your mocks. This step is important later on.

Implementing the Builder Pattern

The builder pattern is one of the design patterns that works really well for mocking API responses. We can use it as follows:

  • Create a builder class for each logical entity identified, like CartBuilder and ItemBuilder.
  • Introduce a withDefaults() method in each builder to supply a default set of values for object creation.
  • Ensure that your builder classes have methods to set values for all fields corresponding to the API response segments.

Here you can see an example of an ItemBuilder:

class ItemBuilder {
    private name: string = "";
    private price: number = 0.0;

    withDefaults(): this {
        this.name = "default name";
        this.price = 10.0;
        return this;
    }

    withName(name: string): this {
        this.name = name;
        return this;
    }

    withPrice(price: number): this {
        this.price = price;
        return this;
    }

    build(): Item {
        return new Item(this.name, this.price);
    }
}

For more information on design patterns, I would like to refer you to the following resource:

Builder
Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

Creating Mocks

  • Utilize the builders to create mock objects mirroring the structure and data of the actual API response.
  • These mocks can now be employed in your tests to simulate API interactions sans actual network requests.

Here you can see an example on how to use the builder:

class Mocks {
    static createMockCart(): Cart {
        const item1 = new ItemBuilder()
          .withDefaults()
          .withName("Item 1")
          .withPrice(10.0)
          .build();
        const item2 = new ItemBuilder()
          .withDefaults()
          .withName("Item 2")
          .withPrice(15.0)
          .build();
        const cart = new CartBuilder()
          .withDefaults()
          .addItem(item1)
          .addItem(item2)
          .build();
        return cart;
    }
}

You could also add a method to the CartBuilder that builds a default cart with 2 items. This has the benefit that your code becomes smaller in your tests. It does however increase the dependencies between code.

By following the aforementioned steps, you've dissected a complex API response, comprehended its structure, and constructed useful mocks using the builder pattern in TypeScript. This modular, readable, and maintainable approach facilitates handling complex data structures in your tests efficiently.

HAR files

A HAR (HTTP Archive) file is a JSON formatted log of network requests and responses. It captures all network interactions, such as HTTP requests and responses, that occur when a page is loaded in the browser. This includes the URLs requested, the HTTP methods used, headers, cookies, and other information about the requests and responses. HAR files can be invaluable for debugging network issues, analysing performance, or replaying a sequence of network interactions in a controlled environment for testing purposes.

To record and replay network interactions using Playwright and HAR files, follow these steps:

  1. Recording HAR File:
    1. Use page.routeFromHAR() or browserContext.routeFromHAR() with the path to the HAR file and optionally specify a URL pattern to filter requests.
    2. Set the update option to true to populate the HAR file with actual network data.
    3. Navigate to the page and perform interactions to capture network requests.
  2. Modifying HAR File (Optional):
    1. Open and edit the hashed .txt file in your hars/ folder to modify the HAR data as needed.
  3. Replaying from HAR:
    1. Utilize page.routeFromHAR() again, this time with update set to false, to serve responses from the HAR file during your tests, effectively mocking the network interactions.

For more information, see the following page:

Mock APIs | Playwright
Introduction

Screenshot Comparisons

Introduction to Screenshot Comparisons

Screenshot comparisons are an integral part of visual regression testing. They help in identifying unintentional UI changes by comparing the current state of the UI with a previously captured baseline screenshot. Playwright provides the ability to automate this process, making it easier to integrate into the testing workflow.

Perfecting Screenshot Comparisons

In order to perfect screenshot comparisons using Playwright, the following steps and considerations are essential:

🚧
Playwright captures screenshots per platform. This means that if you develop tests on a Windows machine, and run them on a Linux machine, your screenshot comparisons will not work. You will have to generate the screenshots for Linux machines as well.
  1. Capturing Baseline Screenshots:
    Utilize the await expect(page).toHaveScreenshot() method in Playwright to capture baseline screenshots during the first execution of your test. This method will generate reference screenshots that will be used for comparisons in subsequent test runs.
  2. Organizing Baseline Screenshots:
    Maintain a well-structured directory for storing baseline screenshots. Playwright saves screenshots in a folder named with the pattern <test-file-name>-snapshots. It's crucial to have an organized storage system to easily manage and retrieve baseline screenshots.
  3. Naming Conventions:
    Employ clear naming conventions for your screenshots to easily identify them. By default, Playwright generates names for the screenshots, but you can specify custom names using await expect(page).toHaveScreenshot('custom-name.png').
  4. Handling Platform Variance:
    Be aware that screenshots may vary between different browsers and platforms due to rendering differences. If you are testing across multiple platforms, ensure you have baseline screenshots for each platform.
  5. Updating Baseline Screenshots:
    When the UI changes, update the baseline screenshots using the --update-snapshots flag with Playwright test command, like so: npx playwright test --update-snapshots.
  6. Setting a Pixel Difference Threshold:
    In some cases, minor pixel differences might not be significant. Set a threshold for pixel differences using the maxDiffPixels option. For example, await expect(page).toHaveScreenshot({ maxDiffPixels: 100 }) will only flag differences if more than 100 pixels are different.
  7. Sharing Configuration:
    If you wish to share a common configuration for screenshot comparisons across tests, specify the default values in the playwright.config.ts file. This way, you can centralize the configuration for the toHaveScreenshot method.

By adhering to these guidelines and leveraging the features provided by Playwright, you can greatly enhance the effectiveness and accuracy of screenshot comparisons in your visual regression testing workflow.

For more information, see the following page:

Visual comparisons | Playwright
Playwright Test includes the ability to produce and visually compare screenshots using await expect(page).toHaveScreenshot(). On first execution, Playwright test will generate reference screenshots. Subsequent runs will compare against the reference.

Techniques

Pro Tips and Best Practices

In this section, you will learn some best practices for Playwright.

Integrating Playwright into CI/CD

Seamless integration of Playwright into your CI/CD pipeline ensures that visual testing is an integral part of the development and deployment process. This integration enables timely detection and fixing of visual regressions.

Web-first Locators

Web-first locators are a principle in automated testing that emphasizes the importance of reliable and robust locators for interacting with web elements during tests. They form the foundation upon which all interactions in your automated tests are built.

Importance of web-first locators

  1. Robustness
    Web-first locators contribute to the robustness of your test suite. By ensuring that your locators are precise and reliable, you reduce the likelihood of flaky tests caused by elements not being found or interacted with correctly.
  2. Maintainability
    Tests need to be maintainable. A well-structured locator strategy makes your tests easier to update and maintain. When locators are thoughtfully crafted and organized, updating tests due to UI changes becomes a straightforward task.
  3. Readability
    Good locators make your tests easier to read and understand. They act as a form of documentation, explaining what elements your tests are interacting with.
  4. Performance
    Efficient locators contribute to faster test execution. By reducing the time it takes to find elements, you can significantly speed up the overall execution time of your test suite.

Test tagging

Tagging tests using a convention such as the @ syntax in Playwright offers a streamlined way to categorize and manage tests in a project. This built-in convention can be adhered to by teams for better organization, filtered test execution, and enhanced reporting. By appending tags like @smoke and @regression directly to the test name, teams can quickly identify the purpose of each test.

Syntax and Implementation

In this convention, you would append or prepend the @ tag directly to the test name. Here's an example:

test('@smoke calculates sum correctly', ({ page }) => {
  // ...
});

In this example, @smoke is a tag indicating that this is a smoke test.

Next, you can run all the smoke tests specifically with the following command:

npx playwright test --grep @smoke

You can also skip all the tests by inverting the grep like so:

npx playwright test --grep-invert @smoke

To run multiple tags (logical OR operator), add a pipe character in between tags like so:

npx playwright test --grep "@smoke|@regression"

Conclusion and Next Steps

As we draw this comprehensive Playwright tutorial to a close, it's essential to reflect on the journey we’ve undertaken together. From the foundational concepts to the more intricate and advanced features, we've delved deep into Playwright's capabilities, ensuring a thorough grasp of its functionalities.

Playwright, with its robust features and user-centric approach, empowers developers and testers alike to craft efficient, precise, and scalable test scripts for web applications. Throughout this tutorial, we aimed not just to understand but to practically apply the knowledge, ensuring that the theoretical merges seamlessly with the hands-on, culminating in a holistic learning experience.

Next Steps

  1. Practice Makes Perfect:
    While the tutorial offered interactive exercises, there's no substitute for individual practice. Explore new projects, set up different configurations, and test diverse web applications.
  2. Stay Updated:
    Playwright, like most modern frameworks, undergoes constant updates and improvements. Regularly check for updates and keep abreast of the latest features and enhancements.
  3. Explore Advanced Use-Cases:
    This tutorial set the foundation. But Playwright has more to offer. Dive into advanced topics like CI/CD integration, parallel testing, and cross-browser testing.
  4. Share and Mentor:
    Knowledge grows when shared. Consider writing about your experiences, tips, and tricks with Playwright. Alternatively, mentor peers and newcomers, solidifying your understanding while aiding others.