Sooner or later, all of us working in test automation come to the situation where we wish our test scripts were documented better. I want to know which actions script is performing so that I can
Playwright gives us, out of the box, a very nice and clean HTML report. They even map out test step within, to make it easier for us to understand what goes on. But, can you tell me, just by looking at picture below, what am I testing? Probably not, but after looking at it for a minute or two, you might get an idea. A minute spent on understanding test steps is too much.
Now have a look at the test case. Arguably, it's more readable than the report. That is because main actions performed in the test case, consisting of clicks and keyboard stokes, are wrapped in these nice functions navigateToPage() and switchLanguageTo().
If you noticed that navigateToPage() is looking nice in the report, congratulations. It does, and that's because it already makes use of Playwright's test.step functionality, introduced in v1.10. Here it is:
But you have to admit, it looks kind of funny, even if we disregard URL check. Having to call async function which takes an async function as parameter for the sole purpose of making my report look nicer is not optimal. And I have to do this for all major test actions at least. I don't like it.
If you know me, or if you're familiar with my style, you know I want to keep things simple. And that I'm willing to go above and beyond if pursuit of simplicity. So I wanted to have something like this:
This looks cleaner, nicer, can be written faster and does the same job. Here's how it would look like on switchLanguageTo(item):
Let's run the tests now. Now report looks something like this:
I won't go into details of the implementation, but the idea is to create mentioned Typescript decorator, with purpose to create a structured, named step for reporting purposes
Focus on the wrapped test.step function. parameter 'name' is what I want to write for a test step in report, and async function parameter contains only the decorated action from my page object.
Logic around initialization of 'name' variable can be disregarded for now, it's purpose is to make my 'stepName' configurable so that I can inject values into it, like we saw in switchLanguageTo(item) action:
@Step('Switch language to: {item.lang} and verify url {item.url}')
async switchLanguageTo(item: { lang: string, url: string }) {
// ...
}