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.

You will learn
  • How to install merv-client, @cucumber/cucumber, and Playwright
  • How to align @playwright/test versions in package.json (TypeScript projects)
  • How to register merv-client/cucumber-formatter in cucumber.js
  • How to bind the Playwright page for step screenshots
  • How to add info, testdata, and validation rows with MervReporter
  • 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"
    }
  }
}

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.

MERV essentials — add these three calls to your hooks:
  1. BeforeMervPlaywright.setAutomationToolObject(AutomationTool.PLAYWRIGHT, this.page) binds the active page.
  2. AfterStepawait MervPlaywright.screenshot({ testStepId, result }) stashes step PNGs (respects merv.screenshot; failed steps always capture).
  3. AfterMervPlaywright.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'));

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://.

merv-reports/ index.html # Dashboard (all runs) merv-index-data.json # Polled every 5s by index.html 22-06-2026 15-48-55 Merv-Report/ json/merv-report.json # Suite + testcase data (live + final) html/merv-report.html # Final suite report html/merv-report-live.html # Live suite view during run screenshots/ # Step PNGs merv-report-upload.zip # MERV UI import bundle

Live dashboard

  1. Start npx cucumber-js (or your npm test script).
  2. Open merv-reports/index.html in a browser via Live Server or similar.
  3. Within ~5 seconds a new suite card appears with In progress.
  4. Click View Casesmerv-report-live.html for 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.

  1. VS Code: install Live Server, right-click merv-reports/index.html, Open with Live Server.
  2. IntelliJ: right-click index.htmlOpen in Browser.
  3. While tests run, open {runFolder}/html/merv-report-live.html on 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

← Back to Documentation