Your first web scenario
In this guide, you'll learn the basics of web testing with Serenity/JS; I'll show you how to run and debug existing tests and write new test scenarios. While we'll focus on helping you get the basics right, I'll point you to the more advanced techniques and patterns when needed.
My goal with all the Serenity/JS guides in this handbook, including this one, is that they're easy to follow whether you're a test automation expert or just starting on your journey. If you found anything here that could have been clearer, please let me know in the comments or submit a correction.
To keep things simple, we'll use a Gitpod.io workspace to work with Serenity/JS in your web browser, so there's no need to install anything on your computer. If you prefer to set up Serenity/JS locally instead, follow the installation instructions in Serenity/JS + Playwright Test project template.
In this tutorial, you'll learn that:
- Serenity/JS works well with popular development environments, including the free Visual Studio Code,
- Serenity/JS tests are just high-quality code, so all your regular programming tools will work as expected,
- Serenity/JS test scenarios are actor-centric and follow the Screenplay Pattern to help you capture business domain vocabulary and business workflows,
- Serenity/JS uses an activity-based composition model, optimised for readability and code reuse,
- Serenity/JS assertions and synchronisation statements are portable across interfaces and integration tools,
- Serenity/JS has first-class support for TypeScript and the asynchronous nature of the JavaScript runtime.
If you get lost or stumble upon a problem you're not quite sure how to solve - ask on the Serenity/JS Community Chat.
Launching your workspace
All Serenity/JS project templates, such as the one we'll use in this chapter, support Gitpod.io workspaces and are configured to make it easy for you to use them in a Visual Studio Code-based development environment. Of course, since Serenity/JS tests are standards-based Node.js code, they'll work just as well in any other modern IDE.
In this tutorial, we'll use Serenity/JS + Playwright Test template, which integrates Serenity/JS with Playwright web testing library and its dedicated Playwright Test test runner. The test suite we'll work on interacts with a simple to-do list app that you can experiment with at todo-app.serenity-js.org.
To launch your workspace, make sure you have a GitHub account, which will make it easier for you to use all the other Serenity/JS resources too.
Next, launch your Gitpod.io web IDE using the "Open in Gitpod" button below and sign in to your Gitpod.io workspace using your GitHub account.
Serenity/JS integrates with several popular test runners, such as Cucumber, Mocha, Jasmine and Playwright Test, as well as various web integration tools, such as Selenium, Playwright and WebdriverIO. Once you know your way around your first Serenity/JS project template, picking the one right for your team and your project will become a breeze.
Running tests in Visual Studio Code
Serenity/JS + Playwright Test template
you've just opened in your Gitpod workspace includes several example test scenarios
located under the spec
directory.
You can view them using your Visual Studio Code Project Explorer sidebar, which should show a directory structure similar to this:
There are two ways to run Playwright tests in Visual Studio Code, and you can do it either by:
- using the play/check mark icon next to the name of the test in a
.spec.ts
file, - using the play icon in the Visual Studio Code Test Explorer panel, which shows all the test scenarios Visual Studio Code has detected in your project, as per the screenshot below:
Once you've inspected the spec/recording_items.spec.ts
,
you will notice that we're using two functions provided by the @serenity-js/playwright-test
module that help to organise a test suite:
Those describe
and it
functions are wrappers around
Playwright Test test.describe
and test
functions, respectively.
In addition to the functionality offered by Playwright test
function, Serenity/JS it
wrapper offers Serenity/JS-specific test fixtures,
such as actor
,
actorCalled
,
or crew
.
I'll tell you about them in a moment, but first, let's run the scenarios.
If Visual Studio Code didn't detect any test scenarios in your project, or if it doesn't display the play/check mark icon next to the name of the test, use the "Refresh Tests" icon in the Test Explorer to reload test configuration.
Exercises
To get familiar with running Serenity/JS test scenarios in Visual Studio Code, conduct the following experiments:
- Open
spec/recording_items.spec.ts
and run test scenarios such asit('should clear text input field when an item is added')
individually using the play/check mark icon next to theit
block. Make sure they're passing. - Run a group of test scenarios by clicking on the play/check mark icon next to the
describe
block, such asdescribe('Todo List App')
. Make sure they're passing. - Run individual test scenarios using Visual Studio Code Test Explorer panel.
- Navigate from a test scenario in a Visual Studio Code Test Explorer panel to its location in the codebase using the "Go to test" icon next to the name of the test.
Writing Serenity/JS test scenarios
Now that you know how to run Serenity/JS test scenarios in VS Code, let's talk about what's involved in writing some new ones.
First, add the below code snippet to your existing test suite in spec/recording_items.spec.ts
by placing it inside the describe('Todo List App')
block.
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some milk'),
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
)
})
// other test scenarios
})
Well done, you've just created your first Serenity/JS test scenario!
Make sure that you can run this new scenario and that it's passing, and we'll analyse its structure line by line in just a moment.
As you can see in the listing above, well-written Serenity/JS test scenarios are concise, easy to read, and easy to understand even to audiences who might not necessarily have background in technology. That's because Serenity/JS is designed to help you express your scenarios in the domain language of your company and your business and avoid incidental detail and low-level implementation noise that could cloud the picture.
Showing the browser window
So far you've been running your Serenity/JS test scenarios, but didn't get a chance to see the actual browser interacting with the web app under test. That's because any web browsers used by Serenity/JS project templates are configured to run in "headless" mode by default so that they don't get in the way and don't interrupt your work.
This setting is configurable, so you can change it when you need to see what's going on under the hood or to access the developer tools offered by your browser to debug your test scenarios or the system under test.
In addition to being able to interact with the browser when running the tests locally, the Serenity/JS + Playwright Test GitPod.io workspace I have prepared for you comes equipped with VNC, so you can see the browser on Gitpod.io as well.
To see the browser:
- Enable "Show browser" checkbox in the Playwright panel
- In the "Ports" panel, open VNC running on port
6080
in "Preview" mode (this step applies only to Gitpod.io)- If your "Ports" panel is empty, click the bell in the lower right corner and click the appropriate "Open preview" button.
- Run the test!
If your development environment is not based on Visual Studio Code,
or if your version of Playwright Test Extension for VSCode displays a warning message saying
Show browser mode does not work in remote vscode
,
you can configure your tests to start the browser
in non-headless mode by setting the headless
flag to false
in Playwright Test configuration file:
import { defineConfig, devices } from '@playwright/test';
import type { SerenityOptions } from '@serenity-js/playwright-test';
export default defineConfig<SerenityOptions>({
use: {
headless: false,
// ...
},
})
Now that you know how to run the tests, let's get back to code.
Designing actor-centred test scenarios
You might have noticed that the scenario you've just added starts with an actor
.
In fact, all Serenity/JS test scenarios are actor-centred and start like that.
This design, based on the Screenplay Pattern, helps your test scenarios
move away from automation and integration tools taking centre stage
and instead helps you focus on actors—people and processes interacting with
the system under test.
So what are actors? Actors can represent end-users, like a shopper interacting with an online store, or a traveller interacting with a flight booking system. However, actors can also represent automated processes and external systems acting upon the system under test. Examples here could include an external financial data provider submitting asset price information to our batch processing system, or a website crawler bot scanning a web UI and receiving a different version than a regular user. You can also use actors to represent components of a software system initiating some interaction with the component we're interested in, like a microservice sending requests to another microservice under test.
Actors represent people and external systems interacting with the system under test.
From the implementation perspective, the Serenity/JS Playwright Test module takes care of initialising and injecting actors into your test scenarios and dismissing them and freeing their resources when the test scenario is finished.
All you need to do is to use the Serenity/JS it
function instead of the default Playwright test
function,
and ask it for an actor
:
// Use Serenity/JS `describe` and `it` wrappers from `@serenity-js/playwright-test` module:
import { describe, it } from '@serenity-js/playwright-test'
// `describe` groups test scenarios together
describe('Todo List App', () => {
// `it` injects the `actor` object when a test scenario requests it
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
// ...
)
})
// ...
})
Serenity/JS test scenarios can involve one or multiple actors. This is useful, for example, when designing scenarios for contexts
where several actors interact with one another, or where several actors are required to complete different parts of a larger workflow.
Examples of such contexts include multi-user workflow systems, messaging systems, video games, and so on.
You can see an example of a basic multi-actor test scenario in spec/multi-actor_scenarios.spec.ts
.
Modelling workflows using reusable activities
The role of Serenity/JS actors is to interact with the system under test by performing activities.
You use activities to model the logical elements of a workflow that an end-user or an external system interacting with the system under test would perform to accomplish some goal.
Actors perform activities to interact with the system under test and accomplish their goals.
From the implementation perspective, Serenity/JS activities are the most basic form of code reuse and manifest themselves as either tasks or interactions. We'll discuss both of those in detail when we talk about the Screenplay Pattern, but for now let's use the following definitions:
- Interactions are the low-level activities that interact directly with an interface of the system under test.
Examples here include an interaction to click on a button in a web UI,
or one to send a
POST
request to a REST API. Serenity/JS provides dozens of such interactions out of the box, and you can create your own when needed. - Tasks are composites of other activities, created to capture business domain vocabulary and help to make steps in your test scenario reflect steps in your business workflow. Examples here might include a task to "record a to-do item", "create a customer account", or to "purchase a plane ticket".
In our example, a task that accomplishes the goal of "record a to-do item" can be achieved by performing three lower-level activities:
- enter the name of the to-do item into the "What needs to be done?" input box,
- press the enter key,
- make sure the item is recorded as expected.
You can try to perform this task yourself at todo-app.serenity-js.org:
To see how a task like that would be implemented with Serenity/JS,
open spec/todo-list-app/TodoItem/tasks.ts
and review the implementation of a task to recordItem
:
import { contain } from '@serenity-js/assertions'
import { Task, Wait } from '@serenity-js/core'
import { Enter, Key, Press } from '@serenity-js/web'
import { newTodoInput } from '../TodoApp'
import { itemNames } from '../TodoList'
export const recordItem = (name: string): Task => // 1 - reusable function
Task.where(`#actor records an item called ${ name }`, // 2 - task description
Enter.theValue(name).into(newTodoInput()), // 3 - sequence of lower-level activities
Press.the(Key.Enter).in(newTodoInput()),
Wait.until(itemNames(), contain(name)),
)
In the listing above, you can see that a definition of a Serenity/JS task is compact, easy to read, and easy to understand. In fact, it reads almost exactly like how I explained the task to you earlier. This design is intentional and makes your code much more accessible and easier to reason about.
To implement custom tasks like this and capture the language of your business domain all you need is just a few lines of code where you define:
- a reusable function, named after the goal an actor will accomplish having performed a given task, e.g.
recordItem
, - a task description, used for test reporting purposes,
- a sequence of lower-level activities that constitute a task.
What's important to reiterate here is that Serenity/JS activities are designed to be reusable.
This means you could write a task like the one to recordItem
just once
and reuse it in any scenario that needs to record to-do items.
If the steps involved in how an actor would go about recording a to-do item in your system were to change,
you'd only have to update them in this one place in your codebase where the task is defined
and not in every single scenario that uses it.
The task decomposition model implemented by Serenity/JS is based on Hierarchical Task Analysis, a technique typically used in User-Centred Design. You can read more about it in my article "User-Centred Design: How a 50-year-old technique became the key to scalable test automation".
You can define as many or as few tasks as it makes sense to reflect the workflows in your domain. You can make your tasks as high-level or as low-level as you wish. You're not limited to interacting with just the web interfaces either! In fact, you can make your Serenity/JS actors interact with any interface of your system under test, be it web UIs, REST APIs, mobile apps, or whatever a Node.js program can talk to.
To make your work easier, Serenity/JS modules provide dozens of lower-level interactions you can compose your custom tasks from.
This will help you focus on modelling your business workflows and designing your test scenarios instead of wasting your time figuring out how to integrate with the given interface or trying to make several incompatible libraries work together.
The Serenity/JS activity-based code reuse model also means that as you evolve the vocabulary of your domain-specific tasks over time, writing test scenarios becomes easier as you're simply re-arranging existing tasks into new scenarios.
Using portable assertions
The Serenity/JS activity-based programming model applies to performing assertions just as well as it applies
to performing tasks and interactions.
In fact, the Serenity/JS assertion to Ensure
is just another interaction
you give to an actor to perform.
Consider the test scenario you wrote earlier:
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some milk'),
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
)
})
})
The last activity in the sequence above is an assertion for the actor to Ensure.that
the names of the items in our to-do list meet our expectation:
import { Ensure, equals } from '@serenity-js/assertions'
await actor.attemptsTo(
// ...
Ensure.that(itemNames(), equals([ 'Buy some milk', ])),
// ^ actual ^ expectation
)
All Serenity/JS assertions follow the same consistent pattern
and all the Serenity/JS expectations (such as equals
)
are interface-agnostic. This design helps to make your test code portable across interfaces
and lower-level integration tools and enables you to use a single pattern no matter the type of interface you interact with.
In addition to interface-agnostic expectations,
Serenity/JS also ships with web-specific expectations like isVisible()
that are portable across web integration tools, such as Playwright, WebdriverIO, or Selenium.
Analysing assertion failures
You've already seen what happens when an assertion passes. But what happens when it fails?
To experience how you'd go about analysing a Serenity/JS assertion failure, modify the scenario you wrote earlier to make it fail:
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some cake'),
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
)
})
})
When you run the scenario again, you'll notice how the advanced support for Visual Studio Code with Playwright Test extension, enabled by Serenity/JS Playwright Test module, makes it easy to see exactly where the assertion failure has happened.
Exercises
- Compare a single-actor scenario in
spec/recording_items.spec.ts
and a multi-actor scenario inspec/multi-actor_scenarios.spec.ts
. What differences and what similarities can you see in how the actors are accessed? - Inspect tasks in
spec/todo-list-app/TodoItem/tasks.ts
. What Serenity/JS web interactions do they use? Can you find their usage examples in the API docs of the@serenity-js/web
module? - Write another test that adds two items to the to-do list and verifies they've been added correctly. What task can you reuse?
- Add another interaction to your new test - an assertion that verifies the number of items in the list:
Ensure.that(itemNames().length, equals(2))
- See if you can create a test that adds 2 items, removes one of them, and then verifies the items that are left. Reuse the existing task to
remove(itemName)
. - Compare the activity-based code reuse model used by Serenity/JS with other code reuse models you've seen used in the context of test automation in the past. How do they compare?
Debugging tests and interactive execution
A standard Node.js debugger used by your IDE is designed to deal with basic, imperative code, and not the declarative programming model used by Serenity/JS. This means that using a Visual Studio Code debugger to set a breakpoint on one of the activities given to an actor to perform will not have the desired effect. That's because your IDE pauses the scenario when the activity is declared and not when it's executed.
Luckily, Serenity/JS has a solution to this problem.
Debugging test scenarios
To help a Node.js debugger bridge the gap between the imperative and declarative programming models,
Serenity/JS offers debugging capabilities and the interaction to Debug
.
To use it, add the following code snippet to your test scenario, making sure to also use Visual Studio Code Quick Fix
feature to add import { Debug } from '@serenity-js/core'
:
Debug.values(() => {
// set breakpoint
}),
Next, set a breakpoint on the line indicated by the comment, and run your test scenario in debug mode to see your IDE pause the execution between actor's activities:
Debugging tasks
The great thing about the interaction to Debug
is that you can use it not just in the top-level
test scenarios, but also in any custom Serenity/JS tasks you define.
To try it out, open spec/todo-list-app/TodoApp/tasks.ts
and modify the task called startWithAnEmptyList
by adding the interaction to Debug
and set a breakpoint on the line indicated by the comment:
import { Ensure, equals } from '@serenity-js/assertions';
import { Task, Debug } from '@serenity-js/core';
import { Navigate, Page } from '@serenity-js/web';
export const startWithAnEmptyList = () =>
Task.where(`#actor starts with an empty todo list`,
Navigate.to('/'),
Ensure.that(
Page.current().title().describedAs('website title'),
equals('Serenity/JS TodoApp'),
),
Debug.values(() => {
// set breakpoint
})
);
// ...
When you run a scenario using the task to startWithAnEmptyList
in debug mode,
you'll notice that the execution pauses where you wanted it to pause.
However, setting breakpoints is not the only thing the interaction to Debug
can do.
Retrieving information using questions
In the Screenplay Pattern, we design our automated tests to model how actors perform their activities to accomplish their goals. However, performing interactions is just one side of the coin. The other thing an actor must do is to determine if the behaviour of the system under test meets its acceptance criteria. To do that, actors need to retrieve information from the system, its execution environment and sometimes additional data sources.
Serenity/JS models the act of information retrieval as questions—an actor answers a question to obtain some information. Since questions represent a way for actor to obtain the information, and not the result itself, they can be used to parameterise actor's activities and resolved whenever the actor gets to performing the parameterised activity.
Actors answer questions to obtain information.
To see examples of how to define Serenity/JS questions, open spec/todo-list-app/TodoList/questions.ts
.
Among several other functions you'll spot itemNames()
, which should be already familiar to you:
import { includes } from '@serenity-js/assertions';
import { Answerable, d, QuestionAdapter } from '@serenity-js/core';
import { By, PageElement, PageElements, Text } from '@serenity-js/web';
export const items = () =>
PageElements.located(By.css('.todo-list li'))
.describedAs('displayed items');
export const itemNames = () =>
Text.ofAll(items())
.map(name => name.trim())
.describedAs('displayed items') as QuestionAdapter<string[]>;
export const itemCalled = (name: Answerable<string>) =>
items()
.where(Text, includes(name))
.first()
.describedAs(d`an item called ${ name }`) as QuestionAdapter<PageElement>;
Just like Serenity/JS activities can be composed into tasks, Serenity/JS questions can be composed with other questions to transform their results.
You can compose questions to transform their results.
In particular:
- the question about
items()
resolves to page elements corresponding to HTML widgets describing entries in the todo-list, - the question about
itemNames()
retrieves the text content of those elements, and applies a simple transformation to trim space characters from both ends of the value, - the question about
itemCalled(name)
uses Serenity/JS Page Element Query Language to retrieve a single item that matches an expectation,
Also note how all the questions define custom descriptions for Serenity/JS to use when reporting.
Debugging questions
To use the interaction to Debug
to analyse what a given question resolves to,
modify the scenario you wrote earlier:
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some milk'),
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
Debug.values(
(results, names) => {
// set breakpoint here
},
// one or more questions to analyse
itemNames()
)
)
})
// other test scenarios
})
When you run the scenario in debug mode and the execution pauses on the line where you've set the breakpoint,
you'll notice that the actor has resolved the question returned by itemNames()
and
that the Run and Debug panel
shows the values of results
and names
which you specified as your function arguments.
To learn more about how this works, check out the debugging guide.
Exploring page element locators
The Serenity/JS Playwright Test module enables you to leverage the
locator tuning
capabilities of the Playwright Test Visual Studio Code extension.
This functionality is provided by Playwright, and more specifically the
page.locator
API that it offers.
To use it, you'll need to:
- use the interaction to
Debug
to pause the scenario at a breakpoint, - use
PlaywrightPage.current().nativePage()
from@serenity-js/playwright
to extract a Playwrightpage
from the Serenity/JSPage
that's wrapping it, - interact directly with the
page.locator
API.
To see how the locator tuning features work in practice, modify the scenario you wrote earlier:
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some milk'),
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
Debug.values(
(results, page) => {
// set breakpoint on the line below
page.locator('h1')
},
PlaywrightPage.current().nativePage(),
)
)
})
// other test scenarios
})
When you run your scenario through a debugger and the execution pauses on the line where you set
the breakpoint, you'll notice that you can modify the selector passed to page.locator
and that your browser
highlights the matching elements live as you do it.
Exercises
To get familiar with debugging Serenity/JS test scenarios in Visual Studio Code, conduct the following experiments:
- Use the interaction to
Debug
to inspect the value ofPage.current().title()
andPage.current().url()
- Write a scenario that adds an item called "walk the dog" to the to-do list, and use the interaction to
Debug
to inspect the value returned byitemCalled('walk the dog').text()
- Explore other methods offered by Serenity/JS
PageElement
returned byitemCalled(name)
. How would you check if an element is clickable or visible? - Read the guide on Serenity/JS debugging and experiment
with live evaluation of locators like
h1
,footer p
, orli.todo
Test reporting
Serenity frameworks are well-known for their state-of-the-art reporting capabilities. Apart from the excellent Serenity BDD reports, however, Serenity/JS can also dramatically improve the standard Playwright Test reports. Reports produced for Playwright Test scenarios that follow the Screenplay Pattern automatically include detailed information about actors' activities and much more thorough assertion error reports thanks to the dedicated Serenity/JS assertions library.
The Serenity/JS + Playwright Test project template you've been using in this tutorial is already set up to produce both the Serenity BDD and Playwright Test reports.
To explore them, run the full test suite by invoking npm test
in your terminal:
When the test suite is finished, you'll find your Serenity BDD reports
generated under target/site/serenity
, and served on port 8080
on Gitpod:
Playwright Test reports are generated
under playwright-report
and served on port 8181
on Gitpod:
Taking screenshots
Serenity/JS can capture screenshots of the system under test either automatically using the Photographer
or on demand using the interaction to TakeScreenshot
.
Automatic screenshots
To enable automatic screenshots, use your playwright.config.ts
file to add the Photographer
to the Serenity/JS
stage crew
available in Playwright Test worker processes,
and add the SerenityBDDReporter
and
ArtifactArchiver
to your reporter process:
import { defineConfig, devices } from '@playwright/test';
import type { SerenityOptions } from '@serenity-js/playwright-test';
export default defineConfig<SerenityOptions>({
reporter: [
[ '@serenity-js/playwright-test', {
crew: [
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
]
// other Serenity/JS config
}]
],
use: {
crew: [
[ '@serenity-js/web:Photographer', {
strategy: 'TakePhotosOfFailures', // <-- pick a strategy
// strategy: 'TakePhotosOfInteractions',
// strategy: 'TakePhotosBeforeAndAfterInteractions',
} ]
],
},
})
Use Playwright Projects to configure
the Photographer
differently for different environments, browsers, or test suites, while keeping the SerenityBDDReporter
and ArtifactArchiver
configuration the same across all of them.
Automatic screenshots are taken using the active actor's web browser when the desired conditions are met, as defined by your configured strategy:
TakePhotosOfFailures
: takes screenshots of the active actor's browser whenever an assertion fails or an error occurs; this is the best strategy in most scenarios as it reduces the size of the HTML report and helps you quickly identify the cause of any failureTakePhotosOfInteractions
: takes screenshots of the system under test after each actor's interaction; useful for producing comprehensive living documentationTakePhotosBeforeAndAfterInteractions
: takes screenshots of the system under test before and after each actor's interaction; helpful in debugging and troubleshooting
Serenity/JS actor-centric model means that in multi-actor scenarios you'll get screenshots from each actor's browser, even when the actors use different browsers.
On-demand screenshots
To take a screenshot on demand, use the interaction to TakeScreenshot
and the ArtifactArchiver
and SerenityBDDReporter
will automatically add your screenshot to the report:
import { TakeScreenshot } from '@serenity-js/web'
import { Ensure, equals } from '@serenity-js/assertions'
// ...other imports
describe('Todo List App', () => {
it('should allow me to add a todo item', async ({ actor }) => {
await actor.attemptsTo(
startWithAnEmptyList(),
recordItem('Buy some milk'),
TakeScreenshot.of('my app'), // <-- take a screenshot with a description
Ensure.that(itemNames(), equals([
'Buy some milk',
])),
)
})
})
Note that you can use both the automatic an on-demand strategies simultaneously.
The most common scenario is to configure the Photographer
to TakePhotosOfFailures
,
and use TakeScreenshot
to capture additional screenshots when needed.
Next steps
Congratulations! 🥳 You've just learnt how to write, run, and debug your first Serenity/JS test scenarios! If you enjoyed this tutorial, please leave a 👍 in the reactions section below.
If you'd like to learn more, I'd suggest for you to:
- Explore the guides on Serenity/JS Core Design Patterns
- Learn more about using Serenity/JS with Playwright Test
- Clone the Serenity/JS + Playwright Test template and try running it locally, perhaps as a starting point for your next big project?
- Make sure to give Serenity/JS a ⭐️ on GitHub and leave a comment or 👍 if you've enjoyed this tutorial!
New tutorials and videos are coming soon, follow Serenity/JS on LinkedIn and subscribe to Serenity/JS YouTube channel to get notified when they're available!
Serenity/JS is a free open-source framework, so we rely on our wonderful GitHub sponsors to keep the lights on.
If you appreciate all the effort that goes into making sophisticated tools easy to work with, please support our work and become a Serenity/JS GitHub Sponsor today!