My Transition to Playwright for Frontend End-to-End Testing

Nyef
Nyef
·
My Transition to Playwright for Frontend End-to-End Testing

In this blog post, I'll discuss my transition from Cucumber & Selenium to Playwright for frontend end-to-end testing of web applications. As a software developer, it's important for me to adequately test all parts of my web applications with modern technologies.

Why Frontend Testing

If you read some of my case studies, you'll notice that I always write tests using various techniques. Every part of a software solution requires different testing methods and techniques. For instance, I unit test backend code using JUnit. For everything visual, like web applications that end users interact with, I write frontend tests.

Frontend Testing Techniques

There are different ways to test the frontend of your web application. For example, you can test each individual component in isolation. In my case, I implement end-to-end tests for use cases. At least, that's when I'm working on web applications, which is what I assume in this blog post.

End-to-end tests are tests where you go through the flow of various functionalities and check if everything works as expected. This includes not only testing success scenarios but also scenarios where things go wrong. Think of an incorrectly filled form—in that case, you'd expect an error message.

Over the past few years, I've created these kinds of tests with Cucumber & Selenium.

End-to-End Testing with Cucumber

With Cucumber, a so-called behavior-driven development (BDD) tool, you write test scenarios in a language readable to everyone. This language is called Gherkin, here's an example scenario for TheorieGO:

Gherkin

1Feature: Sign in
2
3  Scenario: Successfully authenticate with username and password
4    Given that I have navigated to the admin portal
5    When I type "admin@example.com" in the field "E-mail address"
6    And I type "************" in the field "Password"
7    And I press the button "Sign in"
8    Then the "Dashboard - TheorieGO Admin" page should be opened

You can read in fairly clear language what happens in the scenario and what's expected. However, Gherkin steps don't work magically—you need to implement them yourself.

In such a step definition, you implement the browser's behavior and can check if the result is correct. You do the latter using assertions, for example, with the assertions library Chai.js.

In the next code snippet, you see an example of a step definition. Here, I check if a specific page has opened based on the page title:

Typescript

1Then("the {string} page should be opened",
2    async (expectedTitle: string) => {
3        const pageTitle: string = await driver.getTitle();
4        expect(pageTitle).to.equal(expectedTitle);
5    }
6);

One of the advantages of this method is that non-programmers can write scenarios themselves in Gherkin syntax.

Drawback of Cucumber for Me

Since I mostly work on relatively small projects as a freelancer, this way of frontend testing creates a lot of overhead for me. In my case, there's no need for BDD or for non-programmers to write test scenarios.

Skipping the Gherkin scenarios will save time. Additionally, I needed certain features that Cucumber, at the time of writing this blog, doesn't support. I'll explain these later.

Based on various recommendations, the features offered, the community (& support), and the fact that it's open-source and free, I decided to start using Playwright.

First Impression of Playwright

With the help of the Getting Started docs from Playwright, you can get started pretty quickly. Initially, you get an example project that you can use as a base.

In Playwright test scenarios, you do two things: perform actions and check the results (“assert the state against expectations”).

A test scenario like the one I gave in the previous Cucumber example looks like this:

Typescript

1test.describe('Sign in', () => {
2
3    test('Successfully authenticate with username and password',
4        async ({page}) => {
5            await page.goto('http://localhost:3000/login');
6            await expect(page)
7                .toHaveTitle('Sign in - TheorieGO Admin');
8
9            await page.getByLabel('E-mail address')
10                .fill('admin@example.com');
11            await page.getByLabel('Password')
12                .fill('************');
13            await page.getByRole('button', {name: 'Sign in'})
14                .click();
15
16            await expect(page)
17                .toHaveTitle('Dashboard - TheorieGO Admin');
18    });
19
20});

Using various design patterns, scenarios can be optimized, such as reusing (parts of) scenarios or selectors.

This felt quite familiar to me. The only difference compared to Cucumber was that I didn't have to write Gherkin steps.

Advantages of Playwright

For the Events Access App, it was that first time that I applied Playwright to test all flows of the web application. Here are some advantages I discovered.

Expect API

As I mentioned earlier, I previously used Chai.js for assertions. With Playwright, that's no longer necessary since it comes with a built-in assertions API.

Example:

Typescript

1await expect(page.getByText('Showing 1 to 15 of 100 results'))
2    .toBeVisible();
3await expect(exampleDomElement).toBeVisible();

Auto-Wait & Auto-Retrying

Playwright comes with a so-called auto-wait functionality. When you want to perform an action with an element, Playwright waits until that's possible—until the element is actionable. This eliminates the need for arbitrary delays.

For example, if you want to click on an element, Playwright checks if the locator exists, is visible, stable (not animating), can receive events, and is enabled:

Typescript

1await page.getByRole('button', {name: 'Inloggen'}).click();

Additionally, the assertion library has auto-retrying assertions. These are assertions that are automatically retried until they pass or a timeout occurs:

Typescript

1await expect(exampleDomElement).toBeVisible();

Locator API

With Playwright, there's no need to use an external selector library like Selenium. Instead, Playwright offers a variety of built-in locators.

Example:

Typescript

1const loginButton: Locator = 
2    page.getByRole('button', {name: 'Inloggen'});

Code Generator & Inspector

With the code generator tool, codegen, you can record flows or inspect elements to generate the right selector.

Playwright codegen inspector with the application in the background

Trace Viewer & HTML Report

The Trace Viewer GUI tool lets you view traces of executed tests. You can view what the application looked like during each step of your test flow. Plus, you can search for and inspect locators from these snapshots.

Multi-Browser & Platform

One of Playwright's main selling points is its cross-browser functionality. You can run your test cases on different rendering engines, such as Chromium, WebKit, and/or Firefox.

You can quickly set this up via the Playwright configuration file.

Playwright cross-browser configuration

The tests will then be executed with the different engines.

Playwright cross-browser test results

Visual Comparisons

Playwright has a built-in functionality that lets you compare the visual state of the page with a screenshot. Such screenshots can be generated or updated with Playwright.

If the page doesn't look visually as expected, you can see that in the trace viewer.

Playwright visual comparisons

Transition to Playwright

Although Cucumber (with Selenium) is a suitable framework for frontend end-to-end testing, I'll be switching to Playwright for future projects.

What tipped the scales for me are the aforementioned advantages, like the trace viewer and built-in cross-browser functionality. Additionally, it's an increasingly popular tool in the industry with a large community.

Beyond the Playwright features mentioned in this blog post, there are many more. I'll definitely be experimenting with these in the near future!

Share

Translation disclaimer: This blog post was originally written in Dutch and is translated to English. This translation may contain errors and can be inaccurate.

Need an experienced software engineer?

I help companies with software solutions (SaaS, automation and more) — from backend to frontend. Feel free to contact me to see how I can contribute to your project.

  • Robust backend: Java/Kotlin, SQL, Spring Framework
  • User-friendly front-end: Next.js, React, Typescript, ES6
  • Rapid development: Continuous integration & deployment, Jenkins
  • Efficient collaboration: Agile, Scrum, Jira, Git, Bitbucket, GitHub
  • Freelance software developer from Arnhem, The Netherlands