A lightweight, fast, and modern BDD (Behavior-Driven Development) framework built directly on top of Vitest. Write Gherkin-style scenarios inline with your tests and run them at blazing speed.
Traditional BDD tools in JavaScript come with overhead - separate feature files, complex configuration, slow test runners, and constant context switching. Vitest Story changes that:
- β¨ Write scenarios inline with your TypeScript/JavaScript tests
- β‘ Blazing fast execution powered by Vite and Vitest
- π Type-safe step definitions with full TypeScript support
- π¨ VS Code integration with Test Explorer and step navigation
- π Zero boilerplate - minimal configuration required
This monorepo contains two packages:
π§ vitest-story - The Core Library
The testing library that brings BDD to Vitest. Write Gherkin scenarios using template literals and define type-safe steps.
import { story, Given, When, Then } from "vitest-story";
import { expect } from "vitest";
Given("my account balance is {int}", (ctx, params) => {
ctx.balance = params.int;
});
When("I withdraw {int}", (ctx, params) => {
ctx.balance -= params.int;
});
Then("my account balance should be {int}", (ctx, params) => {
expect(ctx.balance).toBe(params.int);
});
story`
Feature: Bank Account
Scenario: Successful Withdrawal
Given my account balance is 100
When I withdraw 20
Then my account balance should be 80
`();π Read the full library documentation β
π¨ vitest-story-extension - VS Code Extension
A VS Code extension that supercharges your BDD workflow with:
- Test Explorer integration - View and run scenarios directly from VS Code
- Go to Definition - Cmd/Ctrl+Click on steps to navigate to their implementation
- Real-time discovery - Automatically detects new scenarios as you write them
- Debug support - Set breakpoints and debug your scenarios
π Read the extension documentation β
npm install -D vitest vitest-story
# or
pnpm add -D vitest vitest-storySearch for "Vitest Story" in the VS Code Extensions marketplace, or install from the releases page.
Create a test file (example.test.ts):
import { story, Given, When, Then } from "vitest-story";
import { expect } from "vitest";
const cart = {
items: [] as string[],
add(item: string) {
this.items.push(item);
},
size() {
return this.items.length;
},
};
Given("the shopping cart is empty", () => {
cart.items = [];
});
When("I add {string} to the cart", (ctx, params) => {
cart.add(params.string);
});
Then("the cart should contain {int} item(s)", (ctx, params) => {
expect(cart.size()).toBe(params.int);
});
story`
Feature: Shopping Cart
Scenario: Adding items
Given the shopping cart is empty
When I add "Apple" to the cart
And I add "Banana" to the cart
Then the cart should contain 2 items
`();npx vitestNo more switching between .feature files and step definitions. Your scenarios live right next to your code.
Extract values from steps automatically:
{int}- Integers{float}- Floating point numbers{string}- Quoted strings{word}- Single words{customName}- Any custom named parameter
Full support for setup and teardown:
import {
beforeScenario,
afterScenario,
beforeFeature,
afterFeature,
} from "vitest-story";
beforeFeature((ctx) => {
// Runs once before all scenarios
});
beforeScenario((ctx) => {
// Runs before each scenario
});Organize and filter your tests using tags:
story`
@fast
@calculator
Feature: Calculator Operations
@smoke
Scenario: Addition
When I add 5
Then the result should be 5
@skip
Scenario: Work in progress
Given incomplete feature
`();Run tests with specific tags:
# Run only @smoke tests
VITEST_STORY_TAGS=smoke npx vitest
# Run @smoke or @fast tests
VITEST_STORY_TAGS=smoke,fast npx vitestYou can also configure tag filtering using Vitest projects in your vitest.config.ts:
import { defineConfig } from "vitest/config";
import { vitestStoryPlugin } from "vitest-story";
export default defineConfig({
plugins: [
vitestStoryPlugin({
stepsPaths: ["./steps"],
storyPaths: ["./stories"],
}),
],
test: {
include: ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "**/*.story"],
// Use projects to run different tag combinations
projects: [
{
extends: true,
test: {
name: "smoke-tests",
env: {
VITEST_STORY_TAGS: "smoke",
},
},
},
{
extends: true,
test: {
name: "fast-tests",
env: {
VITEST_STORY_TAGS: "smoke,fast",
},
},
},
],
},
});This allows you to run specific test suites:
# Run smoke tests only
npx vitest --project=smoke-tests
# Run fast tests
npx vitest --project=fast-tests
# Run all projects
npx vitestInclude structured data in your scenarios:
story`
Scenario: Bulk operations
Given I have these users:
---
- name: Alice
age: 30
- name: Bob
age: 25
---
`();| Feature | Cucumber JS | Vitest Story |
|---|---|---|
| Speed | Slow startup | Instant (Vite-powered) |
| File Format | Separate .feature files |
Inline template literals |
| TypeScript | Requires extra setup | Native support |
| Test Runner | Custom CLI | Vitest (with watch mode) |
| Step Matching | Regex in separate files | Imported functions |
| IDE Support | Limited | Full IntelliSense + Navigation |
vitest-story/
βββ packages/
β βββ plugin/ # Core vitest-story library
β β βββ src/
β β βββ README.md
β β βββ package.json
β βββ vsix/ # VS Code extension
β β βββ src/
β β βββ README.md
β β βββ package.json
β βββ examples/ # Example tests
βββ README.md # This file
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE.md for details.
- NPM Package (Coming Soon)
- VS Code Extension (Coming Soon)
- GitHub Repository
- Issue Tracker
Check out the examples directory for more complete examples including:
- Basic step definitions
- Shopping cart with database
- Auto-loaded step definitions
- Complex scenarios with hooks
Happy Testing! π