Gherkin contracts and structured YAML fixtures run by Vitest, with typed steps for TypeScript and ReScript.
@epure/vitestis the new name ofvitest-bdd. See Migration from vitest-bdd.
pnpm add --save-dev @epure/vitest vitest// vitest.config.ts
import { epureVitest } from "@epure/vitest";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [epureVitest()],
test: {
include: ["**/*.feature", "**/*.md", "**/*.test.yaml", "**/*.spec.ts"],
},
});epureVitest translates feature files, Gherkin code fences in Markdown, and
structured YAML fixtures into Vitest suites in memory. It does not write
generated test files to disk.
Include .yaml files in Vitest when scenarios are structured examples rather
than prose. epureVitest compiles every .yaml file Vitest loads; the
test.include glob controls which fixtures are tests.
feature: Calculator
examples:
- scenario: adds two numbers
given: a calculator
left: 1
right: 2
result: 3Register each given in calculator.test.yaml.ts, calculator.test.steps.ts,
or a shared steps.ts beside the fixture:
import { Given } from "@epure/vitest";
import { expect } from "vitest";
Given("a calculator", ({ test }, { left, right, result }) => {
expect(Number(left) + Number(right)).toBe(result);
expect(test.task.name).toBeTypeOf("string");
});Each example becomes a source-mapped Vitest test. A scenario's given
overrides background.given; one of them is required. The remaining fields
occupy the parameter position in the same Given API used by features:
the handle first, scenario data second. The handle carries step, which
registers a scenario operation, and test, the running Vitest test.
# calculator.feature
Feature: Calculator
Scenario: Add two numbers
Given I have a calculator
When I add 1 and 2
Then the result is 3Place its steps beside it:
// calculator.feature.ts
import { Given } from "@epure/vitest";
import { expect } from "vitest";
Given("I have a calculator", ({ step }) => {
let result = 0;
step("I add {number} and {number}", (a: number, b: number) => {
result = a + b;
});
step("the result is {number}", (expected: number) => {
expect(result).toBe(expected);
});
});Each Given builder creates private scenario state. The operations it
registers through step close over that state, so scenarios can run
concurrently without a shared World. The feature file keeps its own words —
When, Then, Alors — while the code has one: step. The handle's
test is the running Vitest test, for test.onTestFinished teardown.
A scenario tagged @timeout(30000) runs with a 30 s timeout instead of
Vitest's default; the number is in milliseconds.
Open the canonical EpureVitest module:
open EpureVitest
given("I have a calculator", ({step}) => {
let result = ref(0)
step("I add {number} and {number}", (a, b) => {
result.contents = a + b
})
step("the result is {number}", expected => {
expect(result.contents).toBe(expected)
})
})The package also provides typed Vitest suites, hooks, modes, and assertions. See the ReScript guide.
| Former name | Canonical name |
|---|---|
package vitest-bdd |
package @epure/vitest |
vitestBdd() |
epureVitest() |
VitestBddOptions |
EpureVitestOptions |
ReScript module VitestBdd |
ReScript module EpureVitest |
The former names were deprecated aliases in 1.1.0 and are removed in 2.0.0.
pnpm install
pnpm build
pnpm test
pnpm --filter epure-vitest-docs check- Unreleased 2.1.0
- A scenario tag
@timeout(ms)sets that scenario's Vitest timeout, in milliseconds. - Fix: a YAML
givenis matched the same way as a feature step. It was looked up by its raw text, so aGivenregistered with a capital letter,anorthein its name threwMissing Given.
- A scenario tag
- 2026-09-24 2.0.0
- Breaking: the
Givenbuilder receives a plain handle{ step, test }in both TypeScript and ReScript.stepregisters every scenario operation — the keywords (When,Then,Alors, …) belong to the feature file, not the code — andtestis the running VitestTestContext, so teardown istest.onTestFinished. The any-name proxy and the trailingTestContextargument are gone; the exported type isHandle(previouslyContext). - Breaking: in ReScript,
givenbinds a pattern with no captures;given1andgiven2bind one and two.givenno longer types the test context into the capture slot. - Breaking: remove the deprecated
vitest-bddnames:vitestBdd,VitestBddOptionsand the ReScriptVitestBddmodule. UseepureVitest,EpureVitestOptionsandEpureVitest. - Breaking:
vitest-bddstays at 1.1.0 and is not published for 2.0.0. Install@epure/vitestto upgrade. - A ReScript
given,given1orgiven2builder can beasync. The binding typed the builder's answer asunit, so anasyncbuilder'spromise<unit>was rejected, though the runner has always awaited it.
- Breaking: the
- 2026-09-24 1.1.0
- Rename from
vitest-bddto@epure/vitest.vitest-bdd1.1.0 re-exports@epure/vitest, so existing code keeps working. - Rename plugin API to
epureVitestandEpureVitestOptions. - Rename ReScript module to
EpureVitest. - Keep deprecated TypeScript and ReScript aliases for migration.
- Move the repository to epuremethod/vitest.
- Add the epurejs.dev documentation build.
- Add plural support:
I tap 10 timesmatchesI tap {number} time(s). - Add YAML scenarios:
.yamlfiles are translated into source-mapped suites, andGivenregisters their scenario and background handlers with Vitest's test context. - Map a ReScript test whose name
rescript formatmoved to the line afterit(; such tests printedNo source mappingand reported failures against the wrong line. - Keep the package without an
exportsmap, as since 0.6.1, so ReScript projects that compile out of source still resolve its modules.
- Rename from
- 2026-04-29 1.0.1
- Fix a rare
resolveIdbug when loaded from CommonJS.
- Fix a rare
- 2026-04-05 1.0.0
- Upgrade tests to ReScript 12.
- Release the first stable version.
- 2025-12-18 0.6.2
- Add default
tsxandjsxstep-file discovery.
- Add default
- 2025-12-18 0.6.1
- Remove package exports to support ReScript projects with different suffix settings.
- 2025-09-01 0.6.0
- Add
toRecords,toNumbers, andtoStrings. - Add concurrent scenarios, enabled by default.
- Add the Vitest test context to
Givenbuilders. - Add complete ReScript bindings for Vitest assertions.
- Keep Vitest external to the bundle to preserve concurrency and test context.
- Add
- 2025-08-28 0.5.1
- Remove accidental array-step support.
- 2025-08-27 0.5.0
- Add array-step support.
- 2025-08-17 0.4.0
- Add ReScript unit tests with source maps.
- 2025-07-26 0.3.0
- Add configurable Markdown extensions and
.mdxsupport.
- Add configurable Markdown extensions and
- 2025-07-23 0.2.0
- Add Gherkin code blocks in Markdown.
- Add basic ReScript step definitions.
- 2025-07-04 0.1.0
- Add async and concurrent scenarios.
- Fix negative and scientific number parsing.
- Add the initial Vite plugin.