playwright-step-logger
Logs every Playwright action and assertion as a reporter step with readable, value-bearing titles (which field, what value, which element) — the detail native Playwright steps don't surface. Allure by default; the logger is pluggable.
How it works
- Assertions — a thin
expectwrapper logs a step, then delegates to the real Playwright matcher. Native behavior is preserved (auto-retry, diffs,.not, timeouts); nothing is reimplemented. - Actions — a recursive
Proxyover a page/locator/frame wraps actions (click,fill, …) in steps, re-proxies returned locators (so chains keep logging), and preserves return values. - New matchers —
toBeExist/toBeAbsentare added viaexpect.extend.
Note: built-in matchers are wrapped, not overridden — Playwright's
expect.extendcannot replace built-ins. Synchronous value assertions (expect(2).toBe(2)) pass straight through (no step) to keep their sync throw-on-failure semantics; async (Locator/Page/APIResponse) assertions are logged.
Install
npm i -D playwright-step-logger
# optional, for the default Allure logging:
npm i -D allure-js-commons
@playwright/test is a peer dependency.
Usage
Three steps to wire it into a suite:
1. Register once in a setup/fixtures file that loads before tests. Prefer
Playwright's test.step as the logger — native steps are captured reliably by
allure-playwright across a full multi-test run, whereas allure.step can be dropped:
import { test } from '@playwright/test'
import { registerMatchers } from 'playwright-step-logger'
registerMatchers({
// Recommended: log through native test.step (guard so it no-ops outside a test).
step: (title, body) => (getCurrentTestInfo() ? test.step(title, body) : body()),
defaultTimeout: 45_000,
})
// Simplest alternative: omit `step` to use the built-in Allure default.
2. Use the wrapped expect everywhere assertions are made. The whole suite
must import expect from this package (or a re-export of it) — assertions made
via @playwright/test's own expect are NOT logged:
// re-export once, then import from there across the suite
// helpers/expect.ts:
export { expect } from 'playwright-step-logger'
3. Wrap pages for action logging.
import { expect, pageHandler } from 'playwright-step-logger'
class SomePage {
constructor(page: Page) {
this.page = new Proxy(page, pageHandler) // actions now logged
}
}
await expect(locator).toHaveText('Hello') // logged: Check "..." has text "Hello"
await expect(locator).toBeExist() // custom matcher
Pluggable logger
type StepWrapper = <T>(title: string, body: () => Promise<T>) => Promise<T>
registerMatchers({ step: (title, body) => test.step(title, body) })
Custom step titles (optional)
registerMatchers({
actionDescriptions: {
fill: ({ locator, value }) => `Заполнить "${locator}" значением "${value}"`,
},
assertionDescriptions: {
toHaveText: ({ target, args, isNot }) => `Текст "${target}" ${isNot ? '≠' : '='} "${args[0]}"`,
},
})
Types
For expect(locator).toBeExist() to type-check, ensure the package's ambient
types are picked up (e.g. "types": ["playwright-step-logger"] in
tsconfig, or any import of the package).
Build (contributors)
npm run build # tsc -> dist/ (CommonJS + d.ts)
npm run typecheck