Skip to content
| Marketplace
Sign in
Visual Studio Code>Testing>Mirror for PlaywrightNew to Visual Studio Code? Get it now.
Mirror for Playwright

Mirror for Playwright

raamlakshmanravuri

|
6 installs
| (0) | Free
Automatically compare Playwright test runs across QA and PROD, catching element ID, attribute, and value drift with real evidence — not guesswork.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

🪞 Mirror for Playwright

Automatically compares your Playwright test runs across QA and PROD (or any two environments), and tells you exactly what changed — not just that something failed.

What it catches

  • Element ID / attribute drift: #username in QA is #userid in PROD — even when id, name, and placeholder all changed at once.
  • Value drift: a cart total showing $50.00 in QA and $45.00 in PROD, with the same element id.
  • Missing content: a validation message that shows in QA but never renders in PROD.
  • Dialogs: alert() / confirm() / prompt() text differences.
  • Iframes and extra windows/tabs: scoped separately so a field inside a popup is never confused with one on the main page.

All of this works for any page, any form, any workflow — there's no config file, no selector alias list, and no per-page setup. It reads your test's own actions (what you .fill(), what you .click()) to scope evidence to exactly what that test touched.

Zero changes to your existing project

This is the core design principle: Mirror never modifies your real playwright.config.ts, and your spec files never need environment-branching logic.

  • Mirror generates a separate mirror-playwright.config.ts (or .js) that runs your existing spec files — same testDir, same tests — against QA and PROD as two projects with different baseURLs. Your own config is untouched.
  • Because baseURL is handled per-project, your tests just need relative navigation (page.goto('/login')) — no if (projectName === 'QA') { ... } else { ... } needed anywhere.
  • Only "mirror:*" scripts get added to package.json — your own "test" script is never touched.

Works with your actual stack — automatically detected

Mirror detects your project's setup and generates the right files for it, no configuration needed:

Your stack What Mirror generates
Playwright + TypeScript mirror-playwright.config.ts, mirror-fixtures.js
Playwright + JavaScript mirror-playwright.config.js, mirror-fixtures.js
Playwright + TS + Cucumber Both of the above, plus mirror-cucumber.js + .d.ts + a TypeScript hooks example
Playwright + JS + Cucumber Both of the above, plus mirror-cucumber.js + a JavaScript hooks example
Cucumber only (no @playwright/test runner) mirror-cucumber.js + hooks example — no Playwright config needed

Mixed Playwright + Cucumber suites produce one unified report covering both.

Quick start — step by step

Pick the scenario that matches your project. Both work the same way underneath — you just wire in one file differently depending on whether you use Cucumber or not.


🟦 Scenario 1: Playwright only (JavaScript or TypeScript, no Cucumber)

This is for projects where your tests are written directly with Playwright's test() function — no Gherkin/feature files involved.

Step 1 — Install the extension Search "Mirror for Playwright" in the VS Code Extensions tab, or install from the Marketplace link, or run:

code --install-extension raamlakshmanravuri.mirror-for-playwright

Step 2 — Open your project folder in VS Code Just your normal Playwright project — nothing needs to be prepared in advance.

Step 3 — Run the setup command Press Ctrl+Shift+P (Command Palette), type MIRROR, and choose:

MIRROR: Setup Project

Mirror looks at your project and automatically creates:

  • scripts/mirror-fixtures.js — the file that watches your tests and records what happens
  • scripts/mirror-engine.js and scripts/mirror-reporter.js — compare results and build the report
  • mirror-playwright.config.ts (or .js) — a separate config file just for Mirror. Your real playwright.config.ts is never touched.
  • .env — a small file to hold your QA and PROD website addresses

Step 4 — Tell Mirror your QA and PROD URLs Run:

MIRROR: Configure Environments

Type in your QA URL, then your PROD URL. (You can also just open .env and edit it directly if you prefer.)

Step 5 — Connect your existing test files to Mirror Run:

MIRROR: Patch Test Imports

This changes ONE line at the top of each of your spec files:

// before
import { test, expect } from '@playwright/test';

// after — Mirror automatically changes it to this
import { test, expect } from '../scripts/mirror-fixtures';

That's it — nothing else in your test files changes. Every Playwright command you already know (page.fill, page.click, expect(...)) still works exactly the same. This one line just lets Mirror quietly watch and record what's happening.

Step 6 — Run the comparison Run:

MIRROR: Run Full Comparison

Mirror runs your tests against QA, then against PROD, and builds a report comparing them.

Step 7 — Open the report Open the file mirror-report/mirror-report.html in your browser. Green rows mean QA and PROD matched. Red rows mean something's different — click "🔍 Mismatch" on any red row to see exactly what changed (which element, which attribute, which value).

Done! That's the whole flow for a plain Playwright project.


🟩 Scenario 2: Playwright + Cucumber (JavaScript or TypeScript)

This is for projects where your tests are written as .feature files (Gherkin: Given, When, Then) run through cucumber-js, with step definitions that use Playwright underneath.

Steps 1–4 are identical to Scenario 1 — install the extension, open your project, run MIRROR: Setup Project, run MIRROR: Configure Environments.

The difference starts at Step 3: because this is a Cucumber project, Mirror automatically creates different files instead of mirror-fixtures.js:

  • scripts/mirror-cucumber.js — the Cucumber version of the "watcher" file
  • scripts/mirror-cucumber.d.ts — so TypeScript understands it (TypeScript projects only)
  • scripts/mirror-cucumber-hooks.example.js (or .ts) — a ready-made example showing exactly what to add to your project

Step 5 (different from Scenario 1) — Add two lines to your existing hooks

Cucumber doesn't have spec files to auto-patch the way Playwright does — instead, you add two small lines yourself, by hand, to your project's existing Before and After hooks (the file where you already create your browser and page — usually called something like hooks.js or world.js).

Open the example file Mirror created (scripts/mirror-cucumber-hooks.example.js or .ts) — it shows you exactly what to copy. It looks like this:

const { mirrorBeforeScenario, mirrorAfterScenario } = require('./mirror-cucumber');

Before(async function () {
  this.browser = await chromium.launch();
  this.page = await this.browser.newPage();

  // ✅ ADD THIS ONE LINE — right after you create the page
  this.mirror = mirrorBeforeScenario(this.page, this.browser);
});

After(async function (scenario) {
  // ✅ ADD THIS BLOCK — right before you close the browser
  await mirrorAfterScenario(this.mirror, {
    title: scenario.pickle.name,
    project: (process.env.ENV || 'unknown').toUpperCase(),
    status: scenario.result.status === Status.PASSED ? 'passed' : 'failed',
    duration: scenario.result.duration ? scenario.result.duration.seconds * 1000 : 0,
    error: scenario.result.message || undefined
  });

  await this.browser.close();
});

You're just adding these two pieces to hooks you already have — nothing about how you write .feature files or step definitions changes at all.

Step 6 — Run the comparison Cucumber doesn't have Playwright's built-in "run against two projects" feature, so Mirror runs your whole suite twice instead — once with ENV=QA, once with ENV=PROD:

MIRROR: Run Full Comparison

(Behind the scenes this runs npm run mirror:run:qa then npm run mirror:run:prod, both added to your package.json automatically.)

Step 7 — Open the report Same as Scenario 1 — open mirror-report/mirror-report.html in your browser.

Done! Same report, same evidence, just wired in through your hooks instead of an import line, since that's how Cucumber projects work.


🟨 Bonus: using Playwright AND Cucumber together in the same project

Some projects have both — some tests written directly in Playwright, others in Cucumber. Mirror detects this automatically and sets up both Scenario 1 and Scenario 2's files together. Just follow both sets of steps above; Mirror combines everything into one single report at the end, so you don't need two separate reports for two separate test styles.

Running one test, a few tests, or everything

No file changes needed for any of these — just different commands. (Works the same for plain Playwright and Playwright + Cucumber, since both go through mirror-playwright.config.ts.)

Run ONE test:

npm run mirror:run:single -- "exact test name"

Only that one test runs — against QA and PROD both — nothing else in your suite runs.

Run SEVERAL specific tests: List the spec files you want, directly:

npx playwright test --config=mirror-playwright.config.ts tests/login.spec.ts tests/cart.spec.ts

Or, if the tests share part of their name, -g also matches multiple at once:

npm run mirror:run:single -- "Login|Cart"

(Runs every test whose name contains "Login" or "Cart".)

Run ALL tests:

npm run mirror:run

Running in a visible browser, or a specific browser

See the browser while it runs (headed mode):

npm run mirror:run:headed

Pick a specific browser (defaults to Chrome if you don't choose):

npm run mirror:run:chromium
npm run mirror:run:firefox
npm run mirror:run:webkit
npm run mirror:run:edge

Combine any of these freely — headed mode, a specific browser, and a single test together:

cross-env MIRROR_BROWSER=firefox npx playwright test --config=mirror-playwright.config.ts --headed -g "Login"

Then build and open the report, same as always:

npm run mirror:report
npm run mirror:open

or just npm run mirror:full to do both automatically after any of the runs above.

When your environments are one host with different pages, not different baseURLs

Most setups have QA and PROD on genuinely different hosts (qa.example.com vs example.com), so mirror-playwright.config.ts's per-project baseURL handles the switch automatically — your spec files never need to know which environment they're running against.

But some setups (common with static demo pages, or pre-launch environments served from one box) use the same host with different page paths instead — e.g. /QA-app.html vs /PROD-app.html. In that case, baseURL alone can't distinguish them, and your beforeEach genuinely does need a small amount of environment-aware logic. This is the one legitimate exception to "zero test changes" — here's the recommended pattern:

test.describe('QA Incident Ticket Application - Test Suite', () => {

  test.beforeEach(async ({ page }) => {
    const projectName = test.info().project.name || 'QA';
    const baseURL = 'http://localhost:8000';

    if (projectName === 'QA') {
      await page.goto(`${baseURL}/QA-INCEDENT_TICKET_APP.html`);
    } else {
      await page.goto(`${baseURL}/PROD-INCEDENT_TICKET_APP.html`);
    }

    await page.waitForLoadState('networkidle');
    await page.waitForTimeout(500);
  });

  // ... your test cases below, unchanged
});

test.info().project.name reads the current Playwright project's name — mirror-playwright.config.ts defines these as 'QA' and 'PROD', so this branch reliably tells you which environment the test is currently running against, without needing separate spec files or any change to how you write the tests themselves.

Commands

Command What it does
MIRROR: Setup Project Detects your framework combo and scaffolds .env, scripts/, and (for Playwright) mirror-playwright.config.ts
MIRROR: Configure Environments Set QA/PROD URLs
MIRROR: Patch Test Imports Rewrites Playwright spec imports to use mirror-fixtures.js; for Cucumber-only projects, points you to the hooks example instead
MIRROR: Run Full Comparison Runs mirror:run + mirror:report and opens the result
MIRROR: Regenerate Scripts Updates scripts/mirror-*.js to the latest bundled version

How it works (briefly)

Mirror ships plain JavaScript files into your project's scripts/ folder — nothing hidden inside the extension, all inspectable and version-controllable:

  • mirror-fixtures.js (Playwright projects) — a thin wrapper around @playwright/test that captures a snapshot of the page (plus every iframe and window) whenever a test runs, and tracks which elements you actually interacted with.
  • mirror-cucumber.js (Cucumber projects) — the same capture logic, exposed as plain functions (mirrorBeforeScenario/mirrorAfterScenario) to call from your own Before/After hooks, since Cucumber has no fixture system to hook into automatically.
  • mirror-engine.js — reads results from both Playwright's test-results.json and Cucumber's mirror-raw/ folder, merging both into one comparison. Matches fields by what a user would actually recognize (label, placeholder, data-testid) rather than by id/name (usually the thing that drifted), and falls back to positional matching when even those changed.
  • mirror-reporter.js — renders the comparison as a dark-themed HTML report with screenshots and a searchable mismatch table.

Requirements

  • Node.js 18+
  • @playwright/test and/or @cucumber/cucumber (Mirror will offer to install whichever's missing)

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft