Serenity/JS 3.47: component testing with Playwright
Serenity/JS 3.47 brings web UI component testing into the same Screenplay Pattern architecture as the rest of your suite. The new story fixture mounts a Playwright component story, and the newly introduced Question.as(Constructor) lets you turn it into an Interaction Object.
That means the interaction model you use to exercise the component in isolation can be the same one your end-to-end scenarios use when exercising the running application.
This release also introduces new guides to help you set up component testing and keep your web test code portable with Serenity/JS web modules.
Test components using the same language
A component test needs to mount a desired UI state and interact with it at a lower level than an end-to-end test, but this should not force you to abandon the Tasks, Questions, and Interaction Objects that make the rest of your test suite readable. If a component test uses raw locators while an end-to-end scenario uses a domain model, those tests duplicate both selectors and intent.
Serenity/JS 3.47 adds the story fixture to @serenity-js/playwright-test, leveraging the new component testing mechanism available in Playwright Test 1.63.
Give it the path of a component story and any serialisable props, then use .as() to construct an Interaction Object around the mounted root:
import { Ensure, equals } from '@serenity-js/assertions';
import { describe, it } from '@serenity-js/playwright-test';
import { UserCard } from './UserCard.serenity';
describe('UserCard', () => {
it('displays the user name', async ({ actor, story }) => {
const card = story('components/UserCard/Default', { name: 'Alice' })
.as(UserCard);
await actor.attemptsTo(
Ensure.that(card.name(), equals('Alice')),
);
});
});
The fixture mounts the story only when the Actor first resolves it and keeps that mount for the test. The as(UserCard) call uses the constructor form of Question.as() to map the mounted root Page Element to the Interaction Object. The Interaction Object owns the selectors and UI mechanics; the scenario remains a description of the behaviour under test.
Keep the same component model at every level
Testing a component in isolation should not create a second model of the same UI. When component tests use raw locators while integration scenarios use different Tasks and selectors, the two descriptions drift and a markup change can leave one level of the suite behind.
Serenity/JS gives you the tools and patterns to avoid that duplication: wrap the root of a mounted story in the same Interaction Object your integration tests use. The Interaction Object exposes the component's public, user-observable API as Questions and Tasks, while keeping selectors and UI mechanics inside the model.
Component tests then give fast feedback on the behaviour of a desired state, and integration scenarios confirm that the same model works with the running application. Because both levels exercise the same public API, they validate exactly the contract your end-to-end journeys depend on.
The new component testing guide shows how to create a story gallery, organise stories and Interaction Objects, and mount stories with the story fixture. It also explains when a focused browser-level contract is better served by Playwright's mount fixture and locators than by an Interaction Object.
Make web behaviour portable too
Browser integration tools evolve, and a move from one to another should not force you to rewrite the user journeys your suite already describes. When Tasks, Questions, and Page Elements use a browser tool's APIs directly, that migration spreads through the code that models your application instead of remaining a change to its browser integration layer.
@serenity-js/web separates those responsibilities. It provides portable Page Elements, web Interactions, Questions, and Expectations, while integration modules such as @serenity-js/playwright and @serenity-js/webdriverio give an Actor the concrete Ability to control a browser.
The choice of browser tool belongs where Actors receive their Abilities, not in test scenarios—and certainly not in the Tasks, Questions, or Page Element definitions that describe the application. That boundary lets the same sign-in or checkout Task serve a component test, an end-to-end scenario, or a synthetic check, and keeps that application-facing code largely unchanged as you change browser integrations.
The Serenity/JS web modules guide explains how to establish this boundary in your own suite.
Getting started
If you're using Playwright Test, start with the component testing guide.
Mount a meaningful component state with the story fixture, then expose that state through the same Interaction Object your integration and end-to-end tests use.
If your suite spans more than one browser integration, or you want to keep the option of migrating in the future, take a look at the Serenity/JS web modules guide. It shows how to keep browser-specific choices at the Actor Ability boundary.
The goal is simple: test components in isolation without isolating them from the language of your application.
Enjoy Serenity! 🎉
