logo
You can record Playwright tests to generate replays for review and debugging. Replay Playwright can be used with your existing @playwright/test suite or it can be used as a Node script importing @playwright.
πŸ“Œ
Replay is up-to-date as of Playwright version 1.19. If you are using a newer version and run into a problem, please open a GitHub issue here to let us know!

Setup

To record Playwright tests, you’ll need to install the following package:
  • @replayio/playwright - installs the Replay browsers and configures Playwright to use the Replay browser(s)
πŸ‘‰
Replay Playwright supports Firefox on Mac and Linux, as well as Chrome (beta) on Linux.
You should already have @playwright or @playwright/test installed in your project.

Recording with @playwright/test

You can use the Replay browser as drop-in replacement for your existing @playwright/test suite.
  1. Update your playwright.config.js file to use the Replay version of your preferred browser with the devices object from @replayio/playwright.
    1. Firefox example:

      typescript
      const { devices } = require("@replayio/playwright"); const config = { forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, use: { trace: "on-first-retry", defaultBrowserType: "firefox", }, projects: [ { name: "replay-firefox", use: { ...devices["Replay Firefox"], }, }, ], }; module.exports = config;
  1. Run npx playwright test as usual to execute the tests and record with Replay.
  1. View the available recordings with npx @replayio/replay ls and upload all with npx @replayio/replay upload-all or each with npx @replayio/replay upload <id>.
πŸ‘‰
Replay will create a recording for each individual test.

Uploading automatically with replayio/action-playwright

You can use our GitHub Action to automatically record your CI test environment, upload any failing tests, and comment on the related PR with links to the failure replays.
To set it up, replace the action step that runs your playwright tests with:
yaml
- uses: replayio/action-playwright@v0.4.9 with: command: npx playwright test issue-number: ${{ github.event.pull_request.number }} project: replay-firefox apiKey: ${{ secrets.RECORD_REPLAY_API_KEY }}
And some of those action parameters will have to be passed in from the workflow:
yaml
jobs: run-playwright-tests: name: Run Playwright Tests runs-on: ubuntu-latest with: project: "replay-firefox" issue-number: ${{ github.event.pull_request.number }} apiKey: ${{ secrets.RECORD_REPLAY_API_KEY }}
You should now get a comment containing links to replays of failing tests on your PR’s! πŸŽ‰
Notes:
  • To protect your secrets, GitHub Actions does not share them with forks of your repo so external contributors may not be able to upload replays just yet. We’re working on improving this soon!
  • We’ve set the command and project parameters in the action above explicitly for demonstration, but you can also leave those off to get the default values (command: npx playwright test and project: "replay-chromium")

Recording with Playwright as a Node script

You can also write tests as a function that uses playwright.[browser].launch(). This can give you more control over which tests are recorded. In this configuration, you’ll use the getExecutablePath() function from @replayio/playwright to ensure the Replay-enabled browser is used.
πŸ‘‰ Passing the RECORD_ALL_CONTENT: 1 env is only required for Firefox.

Metadata

You can add metadata to your playwright recordings using either the RECORD_REPLAY_METADATA or RECORD_REPLAY_METADATA_FILE environment variable. If both are set, RECORD_REPLAY_METADATA_FILE takes precedence.
Currently, this metadata is only available locally except for title
  • RECORD_REPLAY_METADATA_FILE - The path to a file containing JSON-formatted metadata
  • RECORD_REPLAY_METADATA - JSON-formatted metadata string

Examples

New Browser per Test

javascript
// firefox.spec.js const playwright = require("playwright"); const { getExecutablePath } = require("@replayio/playwright"); (async () => { const browser = await playwright.firefox.launch({ headless: true, executablePath: getExecutablePath("firefox"), env: { ...process.env, RECORD_ALL_CONTENT: 1, RECORD_REPLAY_METADATA: JSON.stringify({ title: "Take screenshot of replay.io" }) }, }); const page = await browser.newPage(); await page.goto("<https://replay.io>"); await page.screenshot({ path: "replay.png" }); await page.close(); await browser.close(); })();

Shared Browser between Tests

javascript
const path = require("path"); const { writeFileSync } = require("fs"); const playwright = require("playwright"); const { getExecutablePath } = require("@replayio/playwright"); (async () => { const metadataFilePath = path.join( process.env.HOME, "replay-metadata-file.json" ); const browser = await playwright.firefox.launch({ headless: true, executablePath: getExecutablePath("firefox"), env: { ...process.env, RECORD_ALL_CONTENT: 1, RECORD_REPLAY_METADATA_FILE: metadataFilePath, }, }); writeFileSync( metadataFilePath, JSON.stringify({ title: "Screenshot of replay.io" }) ); let page = await browser.newPage(); await page.goto("<https://replay.io>"); await page.screenshot({ path: "replay.png" }); await page.close(); writeFileSync( metadataFilePath, JSON.stringify({ title: "Screenshot of google.com" }) ); page = await browser.newPage(); await page.goto("<https://google.com>"); await page.screenshot({ path: "google.png" }); await page.close(); await browser.close(); })();

Uploading recordings

You can then use node firefox.spec.js to execute and record your test. This will generate a single recording of all the test code in the file. View the available recordings with npx replay ls and upload all with npx @replayio/replay upload-all or each with npx @replayio/replay upload <id>.
You can also use the node API of @replayio/replay to upload recordings.
javascript
const cli = require("@replayio/replay"); async function uploadAll() { const recordings = cli.listAllRecordings(); console.log( "Processing", recordings.length, "recordings" ); let failed = []; let success = []; for await (let r of recordings) { try { success.push(await cli.uploadRecording(r.id, { verbose: true })); } catch (e) { failed.push(e); } } failed.forEach((reason) => { console.error("Failed to upload replay:", reason); }); return success; } async function main() { try { const recordingIds = await uploadAll(); console.log("Uploaded", recordingIds.length, "replays"); } catch (e) { console.error("Failed to upload recordings"); console.error(e); return []; } } main().then(() => { console.log("Done!"); });

Using expect with Node script configuration

You can still use expect from @playwright/test in your test code. Import the command directly like in the example below.
javascript
// firefox-expect.spec.js const playwright = require("playwright"); const { expect } = require('@playwright/test'); const { getExecutablePath } = require("@replayio/playwright"); (async () => { const browser = await playwright.firefox.launch({ headless: true, executablePath: getExecutablePath("firefox"), env: { ...process.env, RECORD_ALL_CONTENT: 1, }, }); const page = await browser.newPage(); await page.goto('https://demo.playwright.dev/todomvc'); const TODO_ITEMS = [ 'buy some cheese', 'feed the cat' ]; // Create 1st todo. await page.locator('.new-todo').click(); await page.locator('.new-todo').fill(TODO_ITEMS[0]); await page.locator('.new-todo').press('Enter'); // Create 2nd todo. await page.locator('.new-todo').fill(TODO_ITEMS[1]); await page.locator('.new-todo').press('Enter'); // Assert todo content await expect(page.locator('.view label')).toHaveText([ TODO_ITEMS[0], TODO_ITEMS[1] ]); await page.close(); await browser.close(); })()

Uploading automatically as a Node script

Writing your tests as a Node script allows you to upload failed recordings automatically using @replayio/replay as a Node module.
For the example below, useΒ REPLAY_API_KEY=123 node upload-failure.spec.js to execute and record the test.
javascript
//upload-failure.spec.js const playwright = require("playwright"); const { expect } = require('@playwright/test'); const { getExecutablePath } = require("@replayio/playwright"); const replayCli = require("@replayio/replay"); async function test() { const browser = await playwright.firefox.launch({ headless: true, executablePath: getExecutablePath("firefox"), env: { ...process.env, RECORD_ALL_CONTENT: 1, }, }); const page = await browser.newPage(); await page.goto('https://demo.playwright.dev/todomvc'); const TODO_ITEMS = [ 'buy some cheese', 'feed the cat', 'book a doctors appointment' ]; // Create 1st todo. await page.locator('.new-todo').click(); await page.locator('.new-todo').fill(TODO_ITEMS[0]); await page.locator('.new-todo').press('Enter'); // Create 2nd todo. await page.locator('.new-todo').fill(TODO_ITEMS[1]); await page.locator('.new-todo').press('Enter'); // This purposefully fails to trigger an upload await expect(page.locator('.view label')).toHaveText([ TODO_ITEMS[1], TODO_ITEMS[2] ]); await page.close(); await browser.close(); }; async function testRun() { try { await test() } catch (e) { const recordingId = await replayCli.viewLatestRecording({apiKey: `${process.env.REPLAY_API_KEY}`}) console.log({e, recordingId}) process.exit(1) } } testRun()