Merv-Local + Cucumber-js guide
Introduction
Cucumber-js
runs Gherkin scenarios in Node.js. Pair it with
Playwright
for browser automation and Merv-Local for rich HTML/JSON reports via the
merv-client/cucumber-formatter plugin.
Each scenario becomes one MERV test case; each Gherkin
step becomes a MERV step with pass/fail status. Scenario tags (e.g.
@smoke) are stored on the testcase for dashboard filters and KPI charts.
Java Cucumber? See the
Merv-Local + Cucumber (Java) guide for
MervCucumberHandler and Maven setup.
- How to install
merv-client,@cucumber/cucumber, and Playwright - How to align
@playwright/testversions inpackage.json(TypeScript projects) - How to register
merv-client/cucumber-formatterincucumber.js - How to bind the Playwright
pagefor step screenshots - How to add
info,testdata, andvalidationrows withMervReporter - Where live and final reports appear under
merv.report.folder - How to send the same runs to Merv-Server
Installing merv-client
From your BDD project root (where package.json and feature files live):
npm install -D merv-client @cucumber/cucumber @playwright/test
npx playwright install
merv-client requires @cucumber/cucumber ≥ 11 and
@playwright/test ≥ 1.40 as peer dependencies. Use Node.js 18+.
package.json — align Playwright versions (important)
When your project uses merv-client (especially via a local
file:…/merv-client-js link), npm may install a second copy of
@playwright/test / playwright-core under
node_modules/merv-client/node_modules. TypeScript then treats them as different types.
In hooks you pass this.page into
MervPlaywright.setAutomationToolObject or
MervPlaywright.screenshot. If versions differ, the IDE and tsc report errors
such as:
Argument of type 'Page' is not assignable to parameter of type 'Page'.
Type 'Page' is missing the following properties from type 'Page':
ariaSnapshot, cancelPickLocator, …
Fix: pin one @playwright/test version in your project and use npm
overrides so merv-client resolves the same Playwright packages
as your app. Run npm install after editing package.json.
Recommended package.json snippet
{
"devDependencies": {
"@cucumber/cucumber": "^11.0.0",
"@playwright/test": "1.59.1",
"merv-client": "^4.0.0",
"tsx": "^4.19.0",
"typescript": "^5.8.0"
},
"overrides": {
"merv-client": {
"@playwright/test": "$@playwright/test",
"playwright": "$playwright",
"playwright-core": "$playwright-core"
}
}
}
-
Use an exact
@playwright/testversion (no^on the line you override from) — pick the latest stable release your project supports. -
The
$@playwright/testsyntax tells npm to force nested dependencies to use your project’s root Playwright version. -
Verify with:
npm ls @playwright/test playwright-core— every entry should show the same version (e.g.1.59.1). -
If you use
file:…/merv-client-js, keep overrides in the consumer project (your Cucumber repo), not only inside themerv-clientsource tree. -
Restart the TypeScript server after
npm install(TypeScript: Restart TS Server in VS Code / Cursor).
Cucumber 10 projects: if you see ERR_REQUIRE_ESM from
@cucumber/messages, you can also pin messages in overrides, for example
"@cucumber/messages": "24.1.0" alongside the Playwright block above.
Playwright-only projects (no Cucumber) need the same overrides when using a
file:-linked merv-client — see
Playwright guide — package.json overrides.
Configure merv.properties
Create merv.properties at the project root (same folder you run
npx cucumber-js from). The formatter resolves it from process.cwd()
or an ancestor directory.
Minimal Merv-Local configuration
merv.local=true
merv.regression_suite=Cucumber Regression
merv.report.folder=./merv-reports/
merv.screenshot=true
merv.zip.export=true
Common properties
| Property | Role |
|---|---|
merv.local=true |
Write reports on disk (default). Set false for Merv-Server. |
merv.report.folder |
Root for timestamped run folders and index.html. |
merv.regression_suite |
Suite title in JSON and HTML. |
merv.screenshot=true |
Capture a viewport PNG per Gherkin step (requires Playwright hooks below). |
merv.zip.export=true |
Write merv-report-upload.zip for import into the MERV web UI. |
Register the formatter
Cucumber auto-loads a file named cucumber.js (or cucumber.cjs /
cucumber.mjs) from the project root. Add the MERV formatter to the
format array — do not pass --format on the CLI unless
you intend to override the whole config (that disables MERV reporting).
// cucumber.js
module.exports = {
default: {
require: [
'features/support/**/*.js',
'features/step-definitions/**/*.js'
],
format: [
'progress-bar',
'merv-client/cucumber-formatter',
'json:reports/cucumber-report.json'
],
formatOptions: {
snippetInterface: 'async-await',
merv: {}
}
}
};
Load support files before step definitions so global Before hooks
(browser init) run before tagged hooks that set up page objects.
Run tests with:
npx cucumber-js
Or add an npm script: "test": "cucumber-js". Optional:
npx cucumber-js features/home.feature for one feature file.
Playwright hooks
The MERV formatter does not launch a browser. Your Cucumber World starts Playwright; MERV needs three hook calls so step screenshots and custom-step captures work.
Before—MervPlaywright.setAutomationToolObject(AutomationTool.PLAYWRIGHT, this.page)binds the active page.AfterStep—await MervPlaywright.screenshot({ testStepId, result })stashes step PNGs (respectsmerv.screenshot; failed steps always capture).After—MervPlaywright.clear()before you close the browser — releases the page binding and screenshot stash.
| Hook | MERV API | Required? |
|---|---|---|
Before |
MervPlaywright.setAutomationToolObject(…) |
Yes — for step screenshots and validation(…, true) PNGs |
AfterStep |
MervPlaywright.screenshot(…) |
Yes — when merv.screenshot=true or you want failed-step PNGs |
After |
MervPlaywright.clear() |
Yes — call before closeBrowser() |
Before / After |
initBrowser / closeBrowser |
Your project — not MERV (wrap chromium.launch() on the World) |
MERV calls only
const { AutomationTool, MervPlaywright } = require('merv-client');
// 1 — Before: bind page
MervPlaywright.setAutomationToolObject(AutomationTool.PLAYWRIGHT, this.page);
// 2 — AfterStep: stash step screenshot
await MervPlaywright.screenshot({ testStepId, result });
// 3 — After: clear MERV state (before closing the browser)
MervPlaywright.clear();
Full hooks.js example
// features/support/hooks.js
const { Before, After, AfterStep } = require('@cucumber/cucumber');
const { AutomationTool, MervPlaywright } = require('merv-client');
Before(async function () {
await this.initBrowser(process.env.BROWSER || 'chromium'); // your World
MervPlaywright.setAutomationToolObject(AutomationTool.PLAYWRIGHT, this.page);
});
AfterStep(async function ({ testStepId, result }) {
await MervPlaywright.screenshot({ testStepId, result });
});
After(async function () {
MervPlaywright.clear();
await this.closeBrowser(); // your World
});
Set merv.screenshot=true in merv.properties to capture PNGs on
passed Gherkin steps. Failed steps are always captured even when
merv.screenshot=false.
Custom steps (MervReporter)
Gherkin steps map to MERV automatically via the formatter. For extra report rows inside a step
definition — expected/actual checks, request payloads, prerequisite notes — call
MervReporter from merv-client (same API as Java Cucumber, TestNG, and JUnit 5).
MervReporter only works while a scenario is running and
merv-client/cucumber-formatter is registered in cucumber.js.
// features/step-definitions/home-steps.js
const { Then } = require('@cucumber/cucumber');
const { MervReporter } = require('merv-client');
Then('the welcome message is shown', async function () {
const text = await this.page.textContent('h1');
await MervReporter.validation('Welcome heading', 'Welcome', text ?? '');
await MervReporter.info(`Checked at ${new Date().toISOString()}`);
});
Then('I log the cart payload', async function () {
const payload = JSON.stringify({ sku: 'SKU-1', qty: 2 });
await MervReporter.testdata('Cart request', payload);
});
Each call appends a typed row to the current scenario in live and final HTML reports.
| Method | Step type | Role |
|---|---|---|
MervReporter.info(text) |
PREREQUISITE | Info / prerequisite note. |
MervReporter.testdata(name, string) |
TEST_DATA | Inline string or JSON payload. |
MervReporter.testdataFile(name, path) |
TEST_DATA | File attachment (see below). |
MervReporter.validation(name, expected, actual, screenshot?) |
ASSERTION | Expected vs actual; fails the Cucumber step when values differ. |
MervReporter.addCustomStep(name, status?, errorMessage?) |
CUSTOM | Arbitrary row with pass/fail/skipped status. |
MervReporter.addStep(name, type, …) |
varies | Low-level: testdata, assertion, or information. |
Failed validation calls throw and fail the Gherkin step unless
merv.plugin_assertion_soft=true in merv.properties (row is still recorded as failed).
File test data
testdataFile copies the file into {runFolder}/attachments/ and shows a download bar
in the report (large files are not inlined as raw text):
const path = require('node:path');
const { MervReporter } = require('merv-client');
await MervReporter.testdataFile('API payload', path.join('testdata', 'order.json'));
await MervReporter.testdataFile('Expected UI', path.join('testdata', 'expected.png'));
- Text / JSON / CSV — preview in the step when small enough
- Images — inline thumbnail in the report
- Other files — download bar (max 20 MB per file)
Screenshots on custom steps
info, testdata, and testdataFile do not capture a
viewport PNG automatically. To attach a screenshot to a validation row, pass true as the last
argument:
await MervReporter.validation('Page title', 'Home', title, true);
Gherkin step screenshots (after each Cucumber step) are separate — controlled by
merv.screenshot=true and the MervPlaywright.screenshot hook above.
Running your suite
npx cucumber-js
While tests run, MERV writes json/merv-report.json with
"running": true and refreshes merv-index-data.json so the dashboard can
poll live counts. When Cucumber exits, the suite is marked complete and
merv-report-upload.zip is created when merv.zip.export=true.
Viewing Merv reports
Reports live under {merv.report.folder} (for example ./merv-reports/).
Open them through a local web server — not via file://.
Live dashboard
- Start
npx cucumber-js(or your npmtestscript). - Open
merv-reports/index.htmlin a browser via Live Server or similar. - Within ~5 seconds a new suite card appears with In progress.
- Click View Cases →
merv-report-live.htmlfor step-by-step live updates (~2s poll).
Keep index.html open during the run; it patches suite cards and KPI charts from
merv-index-data.json without a full page reload.
Open with a web server
Merv-Local pages load JSON and charts with fetch. Browsers block that on
file:// URLs.
- VS Code: install Live Server, right-click
merv-reports/index.html, Open with Live Server. - IntelliJ: right-click
index.html→ Open in Browser. - While tests run, open
{runFolder}/html/merv-report-live.htmlon the same server.
Merv-Server mode
To stream results to https://merv.online instead of local folders,
set merv.local=false and add your API key and hierarchy UUID. Full walkthrough:
Merv-Server user guide.
merv.local=false
merv.server=https://merv.online/api/v1
merv.api_key=your-api-key
merv.parent_hierarchy=your-hierarchy-uuid
merv.regression_suite=Cucumber Regression
merv.screenshot=true
The same cucumber.js formatter and Playwright hooks apply; no Playwright reporter is required.
Troubleshooting
| Issue | What to check |
|---|---|
| No MERV report after run | Use npx cucumber-js without extra --format flags. Ensure cucumber.js lists merv-client/cucumber-formatter. |
| Dashboard empty until run finishes | Open index.html via http://localhost. Upgrade merv-client to 4.0.0+ for live index updates during Cucumber runs. |
Steps show as Step abc123 |
Upgrade merv-client (Cucumber 11 message format). Ensure duplicate step definitions are removed. |
| No step screenshots | Set merv.screenshot=true. Add all three MERV hook calls (see Playwright hooks): bind page in Before, MervPlaywright.screenshot in AfterStep, MervPlaywright.clear() in After. |
Page type mismatch in hooks / MervPlaywright |
Duplicate @playwright/test versions — add package.json overrides (see Playwright overrides); run npm install; npm ls playwright-core should show one version. |
Multiple step definitions match |
Keep one shared step per pattern in common-steps.js; use World handlers for page-specific logic. |
Custom MervReporter steps missing |
Register merv-client/cucumber-formatter in cucumber.js. Call MervReporter only inside step definitions during a running scenario (not in BeforeAll). |
What’s next
- Cucumber (Java) guide — Maven
MervCucumberHandler - Playwright Test guide —
@playwright/testreporter (non-BDD) - Merv documentation — dashboard tour, JSON contract
- Merv-Server user guide
- Forum / defects
MERV