Skip to main content

Playwright Test

Playwright Test is a test runner designed specifically to accommodate the needs of browser-based end-to-end test automation using Playwright integration library.

Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. It also lets you execute test scenarios on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.

Extending existing Playwright Test suites

If you want to add Serenity/JS to an existing Playwright Test suite, check out Extending Playwright Test with Serenity/JS.

In this article, you will learn:

Native Playwright Test HTML report, augmented with information from Serenity/JS Screenplay Pattern APIs, as well as automated screenshots captured by the Photographer

Examples and Project Templatesโ€‹

If you'd like to dive straight into the code, Serenity/JS GitHub repository provides:

Using Serenity/JS reporting servicesโ€‹

To use Serenity/JS reporting services in a Playwright Test project, you need to:

Serenity/JS test runner adapters

Serenity/JS test runner adapters turn internal, test runner-specific events into Serenity/JS domain events that can contribute to test execution reports produced by Serenity/JS reporting services.

@serenity-js/playwright-test module provides a test runner adapter you can attach to your Playwright Test runner just like any other standard Playwright Test reporter.

Serenity/JS + Playwright Test integration architecture

Installing Serenity/JS Playwright and Playwright Test modulesโ€‹

Follow Playwright Test installation instructions to create a new Playwright Test project:

npm init playwright@latest

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
Use TypeScript to get the most out of your development tools

Playwright Test installation wizard will ask you whether you want to use TypeScript or JavaScript to implement your test scenarios. Choosing TypeScript offers improved tooling support in IDEs such as JetBrains and Visual Studio Code.

Next, add Serenity/JS Playwright and web integration modules:

You might also want to install Serenity/JS reporting services, to accompany your native Playwright Test reports:

To do the above, run the following command in your terminal:

npm install --save-dev @serenity-js/core @serenity-js/console-reporter @serenity-js/playwright @serenity-js/playwright-test @serenity-js/web @serenity-js/serenity-bdd

Configuring Serenity/JSโ€‹

To use Serenity/JS reporting services, list them under crew in your playwright.config.ts, alongside any other native Playwright Test reporters you might want to use:

playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test'

const config: PlaywrightTestConfig = {
reporter: [
[ '@serenity-js/playwright-test', {
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
]
}],

// other native Playwright Test reporters
[ 'html', { open: 'never' } ], // built-in Playwright HTML reporter
],

// Other configuration omitted for brevity
// For details, see https://playwright.dev/docs/test-configuration
}

export default config

Learn more about configuring Serenity/JS Playwright Test reporter and Serenity/JS reporting services.

Defining Playwright Test scenariosโ€‹

When Serenity/JS reports on Playwright Test scenarios, it assumes you're following a common convention where the outermost describe block describes the name of the feature or component under test, and any nested describe blocks contribute to the name of the test scenario.

For example, Serenity/JS will report the below scenario as:

  • feature: Todo List App
  • scenario: when the user is a guest their list is empty
import { describe, it } from '@playwright/test'

describe('Todo List App', () => { // - feature or component name

describe('when the user is', () => { // - one or more nested `describe` blocks
describe('a guest', () => { // to group scenarios
describe('their list', () => { // by context in which they apply

it('is empty', async ({ actor }) => { // - expected behaviour of the feature or component

})
})
})
})
})
Feature coverage

Using the same name for the outermost describe block in several different spec files makes Serenity BDD associate the different test scenarios with the same logical "feature" or "component" and produce feature coverage metrics.

Using Serenity/JS Screenplay Pattern APIsโ€‹

Serenity/JS actor model works great with Playwright Test and Serenity/JS Screenplay Pattern APIs can help your team implement Playwright Test scenarios that are easy to read and understand.

The fastest way to get started with Serenity/JS and Playwright Test is to use one of the Serenity/JS + Playwright Test project templates. However, if you're adding Serenity/JS to an existing project or simply want to understand how the integration works, this guide is for you.

Referring to actors in test scenariosโ€‹

To start using Serenity/JS Screenplay Pattern APIs in your Playwright Test scenarios, define your test scenarios using the describe and it functions from @serenity-js/playwright-test instead of @playwright/test:

- import { describe, it, test } from '@playwright/test'
+ import { describe, it, test } from '@serenity-js/playwright-test'

That's it!

Serenity/JS Playwright Test module provides Playwright fixtures that automatically configure all the actors injected via actor and actorCalled with abilities to BrowseTheWebWithPlaywright and TakeNotes.

import { describe, it } from '@serenity-js/playwright-test'
import { Navigate, PageElements, By } from '@serenity-js/web'
import { Ensure, equals } from '@serenity-js/assertions'

describe('Todo List', () => { // - feature or component name

const displayedItems = () =>
PageElements.located(By.css('.todo-list li'))
.describedAs('displayed items')

describe('when the user is', () => { // - one or more nested `describe` blocks
describe('a guest', () => { // to group scenarios
describe('their list', () => { // by context in which they apply


it('is empty', async ({ actor }) => { // - verify expected behaviour
await actor.attemptsTo( // using a default `actor`
Navigate.to('https://todo-app.serenity-js.org/'),
Ensure.that(displayedItems().count(), equals(0))
)
})
})
})
})
})

Configuring a custom cast of actorsโ€‹

If you'd like to change the default settings, you can override the relevant configuration options either in playwright.config.ts, or in your test file, depending on the type of override you want to make.

For example, you can change the name given to the default actor and register a Photographer service in playwright.config.ts (note the PlaywrightTestConfig import from @serenity-js/playwright-test instead of the default @playwright/test):

playwright.config.ts
import type { PlaywrightTestConfig } from '@serenity-js/playwright-test'

const config: PlaywrightTestConfig = {
use: {
defaultActorName: 'Tess',
crew: [
// [ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ]
[ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfInteractions' } ]
],
},

reporter: [
[ '@serenity-js/playwright-test', {
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
]
}],
],
}

export default config

If you'd like to use a custom Cast of actors instead of the default one, you'll need to do that in a spec file as the ability to BrowseTheWebWithPlaywright requires access to Playwright browser object, which the config file doesn't offer.

For example, you could define a custom Cast where each actor gets their own browser, but they share their notes:

import { describe, it, test } from '@serenity-js/playwright-test'
import { Cast, Notepad, TakeNotes, notes } from '@serenity-js/core'
import { BrowseTheWebWithPlaywright } from '@serenity-js/playwright'
import { Navigate, PageElements, By } from '@serenity-js/web'
import { Ensure, equals } from '@serenity-js/assertions'

interface SharedNotes {
numberOfItemsThatAliceSaw: number
}

test.use({
actors: async ({ browser, contextOptions }, use) => {
const sharedNotepad = Notepad.empty<SharedNotes>();

const cast = Cast.where(actor => actor.whoCan(
BrowseTheWebWithPlaywright.using(browser, {
...contextOptions,
userAgent: `${ actor.name }`
}),
TakeNotes.using(sharedNotepad),
))

await use(cast)
},
})

describe('Todo List App', () => {

const displayedItems = () =>
PageElements.located(By.css('.todo-list li'))
.describedAs('displayed items')

it('support multiple users', async ({ actorCalled }) => {
// Alice and Bob use separate browser windows
await actorCalled('Alice').attemptsTo(
Navigate.to('https://todo-app.serenity-js.org/'),
notes<SharedNotes>().set('numberOfItemsThatAliceSaw', displayedItems().count())
)

// Bob doesn't have access to the browser that Alice uses,
// but he can access their shared notepad
await actorCalled('Bob').attemptsTo(
Ensure.that(notes<SharedNotes>().get('numberOfItemsThatAliceSaw'), equals(0)),
)
})
})