Specflow-emulator
Write your Gherkin steps once, bind them everywhere
Declare your Gherkin step definitions once. specflow-emulator keeps them in a pool and binds them to every .feature scenario automatically — shared steps, scoped steps and a scenario context included. Runs on Jest, Vitest and Vitest browser mode.
One step pool
Declare each Given / When / Then once. The pool is matched against every scenario in your .feature files — no test() boilerplate.
Shared steps, zero glue
A step reused across scenarios or features just works. Share across feature files with a tag instead of exporting helper functions.
Scoped on purpose
Restrict a step to a feature, a scenario or a tag so identically worded steps never collide.
Scenario context
Every scenario gets a fresh context object to pass data between steps — no module-level mutable state.
Errors that point at the line
When a step matches zero or several definitions you get a formatted error naming the feature, scenario, step and file.
Jest, Vitest & the browser
Same API on Jest and Vitest. A dedicated specflow-emulator/browser entry point covers Vitest browser mode (alpha).
Three files, no wiring
Write your steps in a *.stepdefinitions file, point defineFeature at your .feature, and the matcher does the rest.
calculator.stepdefinitions.js
import { defineSteps } from "specflow-emulator";
export const stepDefinitions = defineSteps(
[{ feature: "Simple Calculator" }],
({ Given, When, Then }) => {
Given(/^number "(.*)"$/, (ctx) => (n) => {
ctx.numbers = [...(ctx.numbers ?? []), n];
});
When("I add them", (ctx) => () => {
ctx.result = ctx.numbers.reduce((a, b) => a + +b, 0);
});
Then(/^the result should be "(.*)"$/, (ctx) => (expected) => {
expect(ctx.result).toBe(+expected);
});
}
);
calculator.steps.js
import { defineFeature } from "specflow-emulator";
// The step pool is bound to every scenario automatically.
defineFeature("./calculator.feature");
Stop rewiring the same steps
Before
// raw jest-cucumber: rewire given/when/then in every test()
const addThem = (when) =>
when("I add them", () => {/* ... */});
test("Simple addition", ({ given, when, then }) => {
given(/^number "(.*)"$/, (n) => {/* ... */});
given(/^number "(.*)"$/, (n) => {/* ... */});
addThem(when);
then(/^the result should be "(.*)"$/, (r) => {/* ... */});
});
test("Simple multiplication", ({ given, when, then }) => {
// ...copy every step again...
});
After
// specflow-emulator: declare each step once
defineSteps([{ feature: "Simple Calculator" }], ({ Given, When, Then }) => {
Given(/^number "(.*)"$/, (ctx) => (n) => {/* ... */});
When("I add them", (ctx) => () => {/* ... */});
When("I multiply them", (ctx) => () => {/* ... */});
Then(/^the result should be "(.*)"$/, (ctx) => (r) => {/* ... */});
});
// every matching scenario is wired for you
Ready to try it?
npm i -D specflow-emulator