Merv-Local + Playwright guide
Introduction
Playwright Test
is an end-to-end framework for modern web apps. Merv-Local adds rich HTML/JSON reports,
a run dashboard, and Java-style plugin steps on top of your existing Playwright project via the
merv-client npm package.
- How to install and configure
merv-client - How to align
@playwright/testversions inpackage.json(TypeScript / local links) - What gets created under your report folder
- How to run tests and open the Merv dashboard
- How to keep
import from '@playwright/test'(Option A) - How to add
data,validation, andinforows in reports
Installing merv-client
inside your Playwright project root (where package.json and
playwright.config live), add the package inside package.json:
npm install -D merv-client
Note: merv-client expects @playwright/test ≥ 1.40.0 as a peer
dependency. Use Node.js versions supported by your Playwright release.
package.json — align Playwright versions (important)
as merv-client using the same version of Playwright as your project, you need to align the versions in your package.json in order to avoid conflicts. You can do this by pinning @playwright/test at the project root and add npm
overrides so nested merv-client dependencies resolve the same packages.
{
"devDependencies": {
"@playwright/test": "1.59.1",
"merv-client": "^4.0.0"
},
"overrides": {
"merv-client": {
"@playwright/test": "$@playwright/test",
"playwright": "$playwright",
"playwright-core": "$playwright-core"
}
}
}
After editing, run npm install and confirm with
npm ls @playwright/test playwright-core — all entries should show one version. Restart the
TypeScript server in your IDE. This will ensure that merv-client uses the same version of Playwright as your project.
What’s installed
Merv does not replace Playwright. After you configure the reporter and run tests, each execution creates
a timestamped folder under user defined folder for merv.report.folder key in merv.properties file:
playwright.config stays your central Playwright configuration (browsers, timeouts,
projects). Add one reporter entry for Merv alongside list or the built-in HTML reporter —
use ['merv-client/playwright-reporter', {}] (see Register the reporter).
Testcase tags are filled automatically: any @word in the test title becomes a tag
(e.g. Verify Login @login with @abc → @login, @abc). When the title or
Playwright project name contains chromium, the browser tag chrome is added for
consolidated and KPI filters.
Configure merv.properties
Create merv.properties next to package.json. The reporter and test wrapper read
it from the project root (or an ancestor directory).
Sample Configuration
merv.local=true
merv.regression_suite=Playwright Regression
merv.report.folder=./merv-reports/
Download sample merv.properties
Common properties
| Property | Role |
|---|---|
merv.local |
true — Merv-Local: write HTML/JSON reports on disk under
merv.report.folder (no cloud API).false — Merv-Server: send suites, cases, and steps to the MERV
cloud API (set merv.server, API key, and hierarchy as well).
|
merv.report.folder |
Root for timestamped run folders. |
merv.regression_suite |
Suite title in JSON and HTML. |
merv.screenshot |
true — capture a viewport PNG after major Playwright actions
and after each expect() matcher (requires wrapped test /
expect from merv-client/playwright-test).false — do not capture step screenshots (smaller report
folders; failed tests still show errors without PNGs).
|
merv.debug=true |
Include hooks, fixtures, and all test.step categories in the report. |
merv.report.extra_step_types |
Surface tagged test.step types when debug is off. |
merv.plugin_assertion_soft=true |
Record failed plugin validations without failing the Playwright test. |
Register the reporter
In playwright.config.js or playwright.config.ts, register Merv in the
reporter array using the dedicated subpath
merv-client/playwright-reporter — not the root
merv-client package. Playwright loads config before your tests run; the subpath loads only
the reporter entry point and avoids pulling in the wrapped test shim (which can trigger
“Requiring @playwright/test second time” warnings).
Reporter entry (required)
reporter: [
['list'],
['merv-client/playwright-reporter', {}],
],
Full playwright.config example
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
reporter: [
['list'],
['merv-client/playwright-reporter', {}],
],
use: {
// baseURL, trace, screenshot, …
},
});
['merv-client/playwright-reporter', {}] in reporter: [].Don’t:
import … from 'merv-client' in playwright.config to register the reporter — use the subpath string only.
Running your tests
Run from the directory that contains merv.properties. By default Playwright runs headless;
Merv writes JSON and refreshes reports as tests complete.
npx playwright test
npx playwright test --headed.
Run one file: npx playwright test tests/example.spec.js.
Open UI mode: npx playwright test --ui.
With only the reporter registered, you can keep import { test, expect } from '@playwright/test'
— the report shows Playwright API and expect steps. For plugin steps and screenshots, use
the wrapped test (see below).
Viewing Merv reports
After a run, reports live under {merv.report.folder} (for example
./merv-reports/). Open these paths through a local web server — not by
double-clicking HTML in the file manager (file://).
index.html— dashboard: suite grid, KPI charts, consolidated views{runFolder}/html/merv-report.html— final suite report for one run{runFolder}/html/merv-report-live.html— live suite view while tests run (pollsjson/merv-report.json)
Open with a web server
Merv-Local reports load JSON, charts, and screenshots with fetch. Browsers often block that
on file:// URLs. Use your IDE’s built-in static server (or any local HTTP server) so
the address bar shows http://localhost:….
- Install the Live Server extension (search Live Server in the Extensions view).
-
In the Explorer, open your report folder (for example
merv-reports/). -
Right-click
index.htmland choose Open with Live Server (or click Go Live in the status bar when that folder is the workspace root). -
Your browser opens something like
http://127.0.0.1:5500/merv-reports/index.html— use that URL for the dashboard. -
For one run: open
{runFolder}/html/merv-report.htmlormerv-report-live.htmlvia the same server (from the dashboard links or by pasting the path after the Live Server host).
merv-report-live.html open on Live Server.
Merv blocks Live Server’s full-page reload when JSON changes; the live page and dashboard
update in place instead of flashing a full refresh.
-
In the Project tool window, locate
merv-reports/index.html(or yourmerv.report.folderpath). -
Right-click
index.htmland choose Open in Browser (or Preview in some editions). IntelliJ serves the file from its built-in web server (typicallyhttp://localhost:63342/…). -
Use the browser tab URL as your dashboard link. Open suite reports the same way:
right-click
merv-report.htmlormerv-report-live.htmlunder{runFolder}/html/and Open in Browser. -
If the page is blank or requests fail, confirm the address is
http://…, notfile://…. Use Tools → Web Browsers to pick Chrome, Edge, or Firefox if needed.
The same steps apply in WebStorm and other JetBrains IDEs that include the embedded static server.
Other editors and CI
Any local static server works: for example
npx serve merv-reports or
python3 -m http.server --directory merv-reports, then open
http://localhost:…/index.html. In CI, publish the merv-reports folder as an
artifact and serve it from your pipeline’s report viewer or object storage with HTTP access.
Add Custom Step and Screenshot Types
To add Custom Steps and many other merv feature to make your tests more readable and maintainable. Recommended for large suites: keep Playwright-style imports, but resolve them to Merv’s wrapped
test via a one-time bridge, Pick your project language below.
-
Create
tests/support/playwright-test-bridge.js:export * from 'merv-client/playwright-test'; -
Add
jsconfig.jsonat the project root (strict JSON — no//comments):{ "compilerOptions": { "baseUrl": ".", "allowJs": true, "checkJs": false, "paths": { "@playwright/test": ["./tests/support/playwright-test-bridge.js"] } }, "include": [ "playwright.config.js", "tests/**/*.js", "tests/support/**/*.js" ] } -
Add
package.jsonimports(required for.jsat runtime):{ "type": "module", "imports": { "@playwright/test": "./tests/support/playwright-test-bridge.js" } } -
Import everything from
@playwright/testin specs — includingMervPlaywrightHandler: (not mandatory as of now, check below in sample code)import { expect, test, MervPlaywrightHandler } from '@playwright/test'; - Verify:
npx playwright test --list, thennpm test.
import { test } from '@playwright/test' with
import { MervPlaywrightHandler } from 'merv-client' — plugin steps and screenshots need a single import source.
TypeScript projects use the same bridge as JavaScript, but path aliases must apply to
spec files only. Playwright does not use your test
tsconfig when loading playwright.config.ts — if the root
tsconfig.json maps @playwright/test to the bridge, the MERV shim loads
during config evaluation while specs still resolve to stock @playwright/test. That
triggers errors such as
Playwright Test did not expect test.describe() to be called here or
You have two different versions of @playwright/test.
my-project/
├── tsconfig.json ← no @playwright/test alias
├── playwright.config.ts
├── pages/
├── tests/
│ ├── tsconfig.json ← bridge alias + extends root
│ ├── support/playwright-test-bridge.ts
│ └── *.spec.ts
└── merv.properties
-
Create
tests/support/playwright-test-bridge.ts:export { test, expect, defineConfig, devices, chromium, firefox, webkit, mergeTests, mergeExpects, MervPlaywrightHandler, } from 'merv-client/playwright-test'; export type * from 'merv-client/playwright-test';Explicit runtime exports give IDE completions for
testandexpect.export type *re-exports every Playwright type (Page,Locator,FrameLocator, fixtures) for specs and page objects. -
Add root
tsconfig.json(no@playwright/testalias) forplaywright.config.tsandpages/:{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "types": ["node"] }, "include": ["playwright.config.ts", "pages/**/*.ts", "utils/**/*.ts"] } -
Add
tests/tsconfig.json(path alias for specs — not the project root). Use"baseUrl": ".."(project root), never"baseUrl": "."(that file lives undertests/, so"."breaks the bridge path):{ "extends": "../tsconfig.json", "compilerOptions": { "baseUrl": "..", "paths": { "@playwright/test": ["tests/support/playwright-test-bridge.ts"] }, "types": ["node", "@playwright/test"] }, "include": ["./**/*.ts"], "exclude": [] }Required:
"extends": "../tsconfig.json"(or inline"moduleResolution": "bundler"below) somerv-client/playwright-testresolves in the bridge. Without bundler resolution you getCannot find module 'merv-client/playwright-test'even though types exist on disk.No root
tsconfig.json? Use a self-contained tests file instead (omitextends):{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "baseUrl": "..", "paths": { "@playwright/test": ["tests/support/playwright-test-bridge.ts"] }, "types": ["node", "@playwright/test"] }, "include": ["./**/*.ts"], "exclude": [] } -
Point Playwright at the tests tsconfig in
playwright.config.ts:import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ tsconfig: './tests/tsconfig.json', reporter: [ ['list'], ['merv-client/playwright-reporter', {}], ], // testDir, projects, use, … }); -
Optional:
npm install -D typescript@~5.8and in VS Code choose TypeScript: Select TypeScript Version → Use Workspace Version. - Verify:
npx playwright test --list,npx tsc -p tests/tsconfig.json --noEmit, thennpm test. Restart the TypeScript server after editingtsconfigfiles.
merv-client/playwright-reporter in
reporter: [] — do not import the reporter from root merv-client in config
(avoids loading the test shim twice). Import MervPlaywrightHandler from
@playwright/test only — do not mix with
import { … } from 'merv-client' in the same suite.
"ignoreDeprecations": "6.0" unless your IDE and CLI both use
TypeScript 6 — VS Code’s built-in TypeScript 5.x rejects that value when inherited via
extends.
merv-client/playwright-test in every spec (Option D). Same wrapped
test / expect, but every file uses the explicit subpath.
playwright.config still registers merv-client/playwright-reporter in
reporter: [] — do not import the reporter from root merv-client in config.
For TypeScript, set tsconfig: './tests/tsconfig.json' in config and keep
the @playwright/test bridge alias in tests/tsconfig.json only (see TypeScript
tab above).
Adding Custom Steps in Reports
Use MervPlaywrightHandler with Playwright’s testInfo to add explicit rows
merged into the same testcase as Playwright steps.
import { expect, test, MervPlaywrightHandler } from '@playwright/test';
test('checkout', async ({ page }, testInfo) => {
const merv = new MervPlaywrightHandler(testInfo);
await merv.data('Request', JSON.stringify({ sku: 'SKU-1' }));
await merv.validation('Cart total', '$42.00', '$42.00', true);
await merv.info('Environment: staging');
await page.goto('/cart');
await expect(page.getByRole('heading')).toBeVisible();
});
| Method | Role |
|---|---|
data(name, testdata, screenshot?) |
to pass test-data to the report |
validation(name, expected?, actual?, screenshot?) |
to pass validation to the report |
info(text) |
to pass info to the report |
More Configuration
In order to implement these configurations, we need to implement bridge setup properly as mentioned in the previous section
Debug mode
By default each testcase shows Playwright API actions and expect steps. To include hooks,
fixtures, and every test.step category:
merv.debug=true
Screenshots
for custom steps — pass true as the last argument:
await merv.data('Order Id', '12349893', true);
await merv.validation('Status', '200', '200', true);
In plaugin manner, in merv.properties, it will add screenshot automatically after each locator/page action and after each expect matcher :
merv.screenshot=true
Captures a viewport PNG after each locator/page action and after each expect matcher (pass or fail).
Requires wrapped test and expect from merv-client/playwright-test.
Soft plugin failure
Record a failed validation in the report without failing the Playwright test:
merv.plugin_assertion_soft=true
MervReport (explicit)
For a full custom-report guide (no Playwright reporter) with Java, JavaScript, and TypeScript tabs, see Custom report guide.
Use MervReport when you want the same folder layout and JSON contract without
registering the Playwright reporter — for scripts or hand-built testcase rows.
Setup
- Keep
merv.propertiesat the project root. import { MervReport } from 'merv-client';const report = MervReport.open();— creates a new run folder.
Report object
const report = await MervReport.open();
await report.createTest('Login API', 'POST /auth with valid user');
await report.testdata('Request body', JSON.stringify({ user: 'a@b.com' }));
await report.validation('Status code', '200', '200');
await report.validation('Title', 'Home', 'Home', true); // optional screenshot
await report.info('Using staging tenant');
await report.finalize();
Custom steps on MervReport: info, testdata (string or
new MervReportFile(path)), and validation (with optional screenshot flag).
Step object
Each step row in JSON includes teststepName, stepType, status,
optional expected / actual, testdata, screenshots, and
errorMessage when applicable.
Troubleshooting
| Issue | What to check |
|---|---|
No reports under merv.report.folder |
['merv-client/playwright-reporter', {}] in config; merv.properties on disk; run from project root. |
| Plugin steps missing | Import wrapped test (Option A or merv-client/playwright-test); import MervPlaywrightHandler from the same source. |
data(…, true) has no screenshot |
Rebuild merv-client; use wrapped test; call after page exists. |
| Option A not applied | Add package.json imports (JavaScript); valid jsconfig.json (no comments). For TypeScript, use tests/tsconfig.json + tsconfig in playwright.config.ts. |
Page type mismatch / duplicate Playwright types |
Add package.json overrides (see Playwright overrides); run npm install; verify with npm ls playwright-core. |
test.describe() / test() “not expected here” |
Bridge alias on root tsconfig.json loads MERV shim in config while specs use stock @playwright/test. Move alias to tests/tsconfig.json; set tsconfig: './tests/tsconfig.json' in config. |
merv.screenshot=true but no step screenshots |
Wrapped test / expect not active — check tests/tsconfig.json uses "baseUrl": ".." (not ".") and path tests/support/playwright-test-bridge.ts; set tsconfig: './tests/tsconfig.json' in config. |
Cannot find module 'merv-client/playwright-test' in bridge |
tests/tsconfig.json missing "moduleResolution": "bundler" (often because extends was removed). Add "extends": "../tsconfig.json" or inline bundler in the tests file. |
Invalid value for ignoreDeprecations in tests/tsconfig.json |
Inherited "ignoreDeprecations": "6.0" but IDE uses TypeScript 5.x. Remove that option from tsconfig; optional TS 6-only silencer. |
{ page } implicitly any in specs |
Bridge types not loading — same root cause as expect / merv-client resolution. Use bundler + export type * in bridge; restart TypeScript server. |
IDE: expect / test “not exported” (tests still run) |
"moduleResolution": "bundler" via extends or inline; merv-client ≥ 4.0.0; npm install; restart TypeScript server. Root tsconfig.json (no alias) for pages/. |
| “Requiring @playwright/test second time” | Reporter imported from root merv-client in config. Use merv-client/playwright-reporter in reporter: [] only. |
What’s next
- Run tests in headed mode or UI mode — Running tests
- Read the full integration guide in
merv-client-js/FRAMEWORK_INTEGRATION.md - Merv-Local documentation — dashboard, JSON contract, other frameworks
- API documentation — server mode and REST APIs
MERV