npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deepracticex/vitest-cucumber

v1.4.3

Published

Runtime API for Cucumber BDD step definitions and hooks

Downloads

77

Readme

@deepracticex/vitest-cucumber

Native Cucumber BDD experience for Vitest - Write standard Gherkin features with authentic Cucumber APIs.

100% Cucumber-compliant - Standard execution order, familiar APIs, no compromises 🚀 Zero configuration - Works with Vitest out of the box 📝 Real Gherkin - Use .feature files, not wrapped test code 🎯 Type-safe - Full TypeScript support with type inference

Note: This package provides the runtime API. You need @deepracticex/vitest-cucumber-plugin for transforming .feature files.

What's This Package?

This is the runtime library that provides:

  • Given, When, Then - Step definition APIs
  • Before, After, BeforeAll, AfterAll - Lifecycle hooks
  • DataTable - Rich API for tabular data
  • setWorldConstructor - Custom test context

The generated test code (from .feature files) imports from this package.

Installation

# Install the package
pnpm add -D @deepracticex/vitest-cucumber vitest

# Or use npm
npm install -D @deepracticex/vitest-cucumber vitest

Architecture

┌─────────────────────────────────────┐
│  @deepracticex/vitest-cucumber      │
│                                     │
│  • Given/When/Then (main export)    │
│  • vitestCucumber (/plugin)         │
│  • Runtime APIs (/runtime)          │
└─────────────────────────────────────┘

One package, everything included.

Quick Start

1. Configure Plugin

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { vitestCucumber } from '@deepracticex/vitest-cucumber/plugin';

export default defineConfig({
  plugins: [
    vitestCucumber({
      // Feature files location
      features: ['features/**/*.feature'],

      // Step definitions directory
      steps: 'tests/e2e/steps',

      // Support files (optional - auto-discovered if not specified)
      // support: 'tests/e2e/support',
    }),
  ],
  test: {
    include: ['**/*.feature'],
  },
});

2. Write Feature

# features/calculator.feature
Feature: Calculator
  Scenario: Add two numbers
    Given I have entered 50 into the calculator
    And I have entered 70 into the calculator
    When I press add
    Then the result should be 120

3. Define Steps (This Package)

// tests/steps/calculator.steps.ts
import { Given, When, Then } from '@deepracticex/vitest-cucumber';
import { expect } from 'vitest';

Given('I have entered {int} into the calculator', function (num: number) {
  this.numbers = this.numbers || [];
  this.numbers.push(num);
});

When('I press add', function () {
  this.result = this.numbers.reduce((a, b) => a + b, 0);
});

Then('the result should be {int}', function (expected: number) {
  expect(this.result).toBe(expected);
});

4. Run Tests

pnpm vitest

Core APIs

Step Definitions

import { Given, When, Then, And, But } from '@deepracticex/vitest-cucumber';

// Parameter types: {int}, {float}, {string}, {word}
Given('I have {int} items', function (count: number) {
  this.count = count;
});

When('I add {int} more', function (more: number) {
  this.count += more;
});

Then('I should have {int} items', function (expected: number) {
  expect(this.count).toBe(expected);
});

Hooks

Cucumber-standard execution order (verified against official spec):

import {
  Before,
  After,
  BeforeAll,
  AfterAll,
} from '@deepracticex/vitest-cucumber';

BeforeAll(async function () {
  // Runs once before all scenarios
});

Before(async function () {
  // Runs before each scenario (and before Background steps)
  // Perfect for cleanup/reset operations
});

After(async function () {
  // Runs after each scenario
});

AfterAll(async function () {
  // Runs once after all scenarios
});

Execution order per scenario:

  1. Before hooks
  2. Background steps (if defined)
  3. Scenario steps
  4. After hooks

This follows the official Cucumber specification, ensuring your tests behave identically to other Cucumber implementations.

Custom World Context

Define a custom World class to share state across steps (standard Cucumber.js pattern):

import { setWorldConstructor } from '@deepracticex/vitest-cucumber';

// Define World as a class (recommended - standard Cucumber.js)
class CustomWorld {
  calculator: Calculator;
  result: number;

  constructor() {
    this.calculator = new Calculator();
    this.result = 0;
  }

  // Add custom helper methods
  async performCalculation() {
    // ...
  }
}

setWorldConstructor(CustomWorld);

// Use in steps with type safety
Given('...', function (this: CustomWorld) {
  this.calculator.add(5);
});

Alternative: Factory function pattern

setWorldConstructor(() => ({
  calculator: new Calculator(),
  result: 0,
}));

Both patterns work - use whichever fits your style. Class pattern provides better IDE support and this binding.

Project Structure

Recommended Structure (Cucumber.js Compatible)

project/
├── features/              # Feature files
│   ├── auth.feature
│   └── user.feature
└── tests/
    └── e2e/
        ├── support/       # Loaded FIRST (hooks, world, custom types)
        │   ├── hooks.ts
        │   └── world.ts
        └── steps/         # Loaded SECOND (step definitions)
            ├── auth.steps.ts
            └── user.steps.ts

Configuration:

vitestCucumber({
  features: ['features/**/*.feature'],
  steps: 'tests/e2e/steps',
});

Support Directory Auto-Loading

Files in support/ directories are automatically loaded before step definitions, ensuring hooks and world setup are available when steps execute.

How Auto-Discovery Works

If you don't specify support in the config, the plugin intelligently searches for support files:

  1. Smart detection - Checks sibling directory of steps:

    • steps='tests/bdd/steps' → auto-finds tests/bdd/support
    • steps='tests/e2e/steps' → auto-finds tests/e2e/support
    • steps='src/test/steps' → auto-finds src/test/support
  2. Common fallback locations:

    • tests/e2e/support/**/*.ts
    • tests/support/**/*.ts
    • tests/bdd/support/**/*.ts

Loading order is always:

  1. Support files (hooks, world, custom types)
  2. Step definitions

Explicit Configuration

You can also explicitly specify support directories:

vitestCucumber({
  steps: 'tests/steps',
  support: 'tests/support', // Single directory
});

// Or multiple directories
vitestCucumber({
  steps: 'tests/steps',
  support: ['tests/support', 'src/test/fixtures'], // Multiple
});

Example support/hooks.ts:

import { Before, After } from '@deepracticex/vitest-cucumber';

Before(function () {
  // Initialize context before each scenario
  this.services = createTestServices();
});

After(function () {
  // Clean up after each scenario
  this.services?.cleanup();
});

Example support/world.ts:

import { setWorldConstructor } from '@deepracticex/vitest-cucumber';

// Standard Cucumber.js class pattern
class CustomWorld {
  services: any;
  userData: Record<string, any>;

  constructor() {
    this.services = null;
    this.userData = {};
  }

  // Custom helper methods
  async login(username: string) {
    // Implementation...
  }

  async setupTestData() {
    // Implementation...
  }
}

setWorldConstructor(CustomWorld);

Alternative Structures

Features co-located with source:

src/
├── auth/
│   ├── auth.feature
│   └── auth.steps.ts
└── user/
    ├── user.feature
    └── user.steps.ts

Everything in tests:

tests/
├── features/
│   └── *.feature
├── support/
│   └── hooks.ts
└── steps/
    └── *.steps.ts

Configuration:

vitestCucumber({
  features: ['tests/features/**/*.feature'],
  steps: 'tests/steps',
});

Data Tables

Scenario: Multiple users
  Given the following users:
    | name  | email          | role  |
    | Alice | [email protected] | admin |
    | Bob   | [email protected]   | user  |
import { Given, DataTable } from '@deepracticex/vitest-cucumber';

Given('the following users:', function (table: DataTable) {
  // Get as array of objects
  const users = table.hashes();
  users.forEach((user) => {
    console.log(user.name, user.email, user.role);
  });

  // Or raw 2D array
  const rows = table.raw();
});

Doc Strings

Scenario: Process JSON
  Given I have the following JSON:
    """json
    {
      "name": "Test",
      "value": 42
    }
    """
Given('I have the following JSON:', function (docString: string) {
  this.data = JSON.parse(docString);
});

For Package Authors

If you're building a testing utilities wrapper:

// your-package/cucumber.ts
export * from '@deepracticex/vitest-cucumber';

// Export runtime for generated code
export * from '@deepracticex/vitest-cucumber/runtime';

Then configure the plugin to use your package:

// vitest.config.ts
vitestCucumber({
  runtimeModule: '@your-company/testing-utils',
});

Generated code will import from your package instead:

import { StepExecutor, ... } from '@your-company/testing-utils/runtime';

Why Choose This Over Alternatives?

🎯 Authentic Cucumber Experience

Unlike test wrappers that mimic Gherkin syntax, we provide:

  • Real .feature files parsed with official @cucumber/gherkin
  • Standard Cucumber APIs - Given, When, Then, Before, After
  • Spec-compliant execution order - Hooks run exactly as documented in Cucumber spec
  • Support directory convention - Automatically loads support files before steps (Cucumber.js compatible)
  • Familiar patterns - If you know Cucumber, you already know this

⚡ Vitest Native

  • Built for Vitest from the ground up
  • No extra test runners or frameworks
  • Full compatibility with Vitest ecosystem (UI, coverage, watch mode)
  • TypeScript support with proper type inference

🔒 Production Ready

  • Comprehensive test coverage
  • Actively maintained
  • Used in production applications
  • Regular updates following Vitest and Cucumber standards

Requirements

  • Node.js >= 18
  • Vitest >= 2.0 || >= 3.0

License

MIT