playwright-standalone
v0.1.0
Published
Runner-less Playwright suites with a single cross-tab failure video, plus Allure and CTRF reports. One install, no peer or optional dependencies.
Maintainers
Readme
playwright-standalone
Run Playwright suites without a test runner, and get back the one thing Playwright's own video recording cannot produce: a single continuous video that follows the active tab and stops at the step that failed.
npm i playwright-standaloneThat is the only command. No peer dependencies, no optional dependencies, no npx playwright install,
no system ffmpeg, no Google Chrome. Browsers, ffmpeg and both report writers all arrive with the
package.
Why this exists
Playwright's @playwright/test already covers most of what a runner should do — test.describe.serial()
gives you sequential steps that skip after a failure, screenshot: 'only-on-failure' and
video: 'retain-on-failure' capture artifacts, and Trace Viewer is excellent. If those cover your
needs, use them.
This package exists for the cases they don't:
| | Stock Playwright | playwright-standalone |
|---|---|---|
| Video across tab switches | One file per page; a tab you left keeps recording a frozen frame to the end of the run | One stream that follows the active page |
| Video length | Whole test | Cut exactly at the failing step |
| Long waits | Recorded in real time | Idle stretches compressed (maxIdleSeconds) |
| A page that dies mid-run | Test aborts | Recovers with a new page and continues |
| Needs a test runner | Yes | No — it is a plain class you call from a script |
Quick start
import { StandAloneClass } from 'playwright-standalone';
const s = new StandAloneClass();
s.describe('Checkout journey', () => {
s.setup(async (page) => {
await page.goto('https://example.com');
});
s.run('TC-01 | lands on the homepage', async (page) => {
await page.waitForSelector('h1');
});
s.run('TC-02 | continues into a new tab', async (page) => {
const popup = await page.context().newPage();
await popup.goto('https://example.com/cart');
await s.switchToPage(popup); // recording follows automatically
});
});
process.exit(await s.execute({ video: true }));Steps run in registration order. A failure does not stop the run by default — pass
{ skipOnFail: true } on a step to skip everything after it.
Works from JavaScript or TypeScript, in ESM or CommonJS:
const { StandAloneClass } = require('playwright-standalone'); // CJSAPI
new StandAloneClass(options?)
| Option | Default | Purpose |
|---|---|---|
| launchOptions | { headless: false, args: ['--start-maximized'] } | Passed to chromium.launch() |
| contextOptions | {} | Passed to browser.newContext() — e.g. { storageState: './auth.json' } |
| defaultTimeout | 30000 | Action timeout in ms. 0 disables it |
| defaultNavigationTimeout | 30000 | Navigation timeout in ms |
| allureResultsDir | 'allure-results' | Allure result JSON |
| ctrfDir | 'ctrf' | CTRF report |
| artifactsDir | 'test-results' | Videos, traces, intermediate frames |
| screenshotsDir | 'failed-screenshots' | Failure screenshots |
| reporters | all true | { allure, ctrf, terminal } |
| suiteNameFor | describe name | Builds the CTRF suite string — see below |
Methods
describe(name, fn)— declare a suite; callrun()inside itsetup(fn, channel?, headless?)— runs once before the first steprun(name, fn, { skipOnFail? })— register a stepteardown(fn?)— runs once after the last stepswitchToPage(page)— move the active page and the recordingexecute(options?)— run everything; resolves0(all passed) or1(something failed)page— the current active page (getter and setter)
execute() options
| Option | Default | Purpose |
|---|---|---|
| video | false | true for defaults, or an object to override any VideoOptions |
| trace | false | Write a Playwright trace zip |
VideoOptions
| Field | Default | Purpose |
|---|---|---|
| scope | 'journey' | 'journey' starts at step 1; 'step' starts just before the failing step |
| maxIdleSeconds | 1.5 | Cap on stretches where nothing repainted. Infinity for real time |
| clipLeadInMs | 3000 | Context before the failing step, in 'step' scope |
| width / height | 1280 / 720 | Output size |
| fps | 15 | Output frame rate |
| quality | 70 | JPEG quality of captured frames |
| everyNthFrame | 2 | Chromium screencast throttle; pairs with fps: 15 |
| tailSeconds | 1 | How long the closing frame lingers |
await s.execute({ video: { scope: 'step', maxIdleSeconds: 3 } });Videos are only written when a step fails. A clean run discards its frames.
Frames are captured on repaint, which is how Chromium's screencast works. A journey that sits on a page which never repaints yields too few frames to encode; when that happens the frames are kept on disk rather than discarded, and the path is printed.
Reports
Allure and CTRF output are written by this package directly — it does not depend on
allure-playwright or playwright-ctrf-json-reporter, so there is nothing extra to install.
Rendering Allure's HTML report still needs the Allure CLI (allure serve allure-results), which is a
system tool rather than an npm package. The JSON is written either way.
If something downstream needs to recover structure from the CTRF suite field, supply a resolver:
new StandAloneClass({
suiteNameFor: ({ suiteName, specPath }) => `${specPath} > ${suiteName}`,
});Multiple StandAloneClass instances in one process accumulate into a single CTRF report — the
first write of a process starts fresh, later ones append. That makes the common
"one instance per data row" pattern produce one report rather than clobbering itself.
Notes
Using system Chrome. The default browser is Playwright's bundled Chromium, so npm i alone is
enough to run. To drive an installed Google Chrome instead:
new StandAloneClass({ launchOptions: { channel: 'chrome' } });
// or, equivalently, via setup()
s.setup(fn, 'chrome', false);Disabling timeouts. defaultTimeout defaults to 30000 ms. Pass defaultTimeout: 0 to disable
it entirely — but a hung step will then hang the run rather than failing it.
Requirements
- Node.js >= 20
License
MIT © Bhushan Upwanshi — see LICENSE.
Third-party licenses
This package's own code is MIT. Its dependencies are not, and one of them matters:
| Dependency | License |
|---|---|
| playwright | Apache-2.0 |
| fluent-ffmpeg, uuid | MIT |
| @ffmpeg-installer/ffmpeg | LGPL-2.1 wrapper; platform binaries are GPL-licensed (the win32-x64 build declares GPLv3) |
No ffmpeg code is distributed in this package — npm installs it from @ffmpeg-installer's own
registry entry, and it is invoked as a separate process, never linked into this code.
If your organisation blocks GPL-licensed code anywhere in the dependency tree, override the
@ffmpeg-installer/ffmpeg resolution with an LGPL ffmpeg build.
Full detail in THIRD-PARTY-NOTICES.md.
Trademark
Playwright is a trademark of Microsoft Corporation. This is an independent community package, not affiliated with, endorsed by, or sponsored by Microsoft.
