BDD Testing, Together
BDD (Behavior-Driven Development) testing with Gherkin syntax for Vitest and Playwright
Co-Gherkin brings the power of Gherkin (Given-When-Then) syntax to modern JavaScript testing frameworks: Vitest for unit/integration tests and Playwright for E2E tests.
Inspired by co-working philosophy, Co-Gherkin promotes collaborative testing where:
- β Product managers write scenarios in plain English
- β Developers implement step definitions
- β QA validates behavior automatically
- β Everyone speaks the same language
Co-Gherkin tests itself using Co-Gherkin! π
That's right - we use our own BDD framework to test our own code. This means:
- π― >99% test coverage achieved through BDD scenarios
- π§ͺ Real-world validation - if it works for us, it works for you
- π Examples - Check out our
examples/folder for a fully working Calculator project - π Living documentation - our test features are usage examples
- π Quality assurance - we trust our own tool enough to depend on it
Check out our test features to see Co-Gherkin in action testing itself!
- β Cucumber.js doesn't work well with Vitest
- β Playwright BDD is E2E only, not for unit/integration tests
- β No native Gherkin support in Vitest ecosystem
Feature: User Login
Scenario: Successful login
Given I am on the login page
When I enter valid credentials
Then I should see the dashboardnpm install co-gherkin --save-devfeatures/login.feature:
Feature: User Login
Scenario: Successful login
Given I am on the login page
When I enter "user@example.com" and "password123"
Then I should see the dashboardtests/login.steps.ts:
import { Given, When, Then } from "co-gherkin";
import { render, screen } from "@testing-library/react";
import { LoginPage } from "../pages/LoginPage";
Given("I am on the login page", () => {
render(<LoginPage />);
});
When("I enter {string} and {string}", (email: string, password: string) => {
// Your test logic
});
Then("I should see the dashboard", () => {
expect(screen.getByText("Dashboard")).toBeInTheDocument();
});tests/login.test.ts:
import { runFeature } from "co-gherkin";
import { resolve } from "path";
import "./login.steps";
runFeature(resolve(__dirname, "../features/login.feature"));npm run testCo-Gherkin also works with Playwright for End-to-End testing!
tests/e2e/playwright-setup.ts:
import { configureGherkin } from "co-gherkin";
import { test } from "@playwright/test";
// Configure co-gherkin to use Playwright's test functions
configureGherkin({
describe: test.describe,
it: test,
beforeAll: test.beforeAll,
afterAll: test.afterAll,
beforeEach: test.beforeEach,
afterEach: test.afterEach,
});tests/e2e/steps/login.steps.ts:
import { Given, When, Then } from "co-gherkin";
import { expect } from "@playwright/test";
import { getPage } from "./playwright-context"; // Your context helper
Given("I am on the login page", async () => {
const page = getPage();
await page.goto("/login");
});
When(
"I enter {string} and {string}",
async (email: string, password: string) => {
const page = getPage();
await page.locator('input[type="email"]').fill(email);
await page.locator('input[type="password"]').fill(password);
await page.locator('button[type="submit"]').click();
}
);
Then("I should see the dashboard", async () => {
const page = getPage();
await expect(page).toHaveURL(/.*dashboard/);
});tests/e2e/login.spec.ts:
import { runFeatureSync } from "co-gherkin";
import { resolve } from "path";
import "./playwright-setup"; // Configure co-gherkin
import "./steps/login.steps"; // Import steps
// Use runFeatureSync for Playwright (synchronous test generation)
runFeatureSync(resolve(__dirname, "./features/login.feature"));npx playwright test| Aspect | Vitest | Playwright |
|---|---|---|
| Function | runFeature() |
runFeatureSync() |
| Test Generation | Dynamic | Synchronous |
| Configuration | Auto-detect | configureGherkin() required |
| Use Case | Unit/Integration | E2E |
- β Multi-Framework Support - Works with Vitest AND Playwright
- β Vitest Native - Built specifically for Vitest unit/integration tests
- β Playwright E2E - Full support for Playwright E2E tests
- β TypeScript First - Full type safety and IntelliSense
- β Lightweight - Zero heavy dependencies
- β Fast - Powered by Vitest's and Playwright's speed
- β
Full Gherkin Support:
- β Given, When, Then, And, But, * keywords
- β Background - Shared setup steps
- β Scenario Outline - Data-driven tests with Examples
- β Data Tables - Structured test data
- β Regex Patterns - Capture groups for dynamic values
- β Hooks - BeforeScenario and AfterScenario lifecycle
- β Unit + Integration + E2E - Complete testing solution
- β Easy Migration - From Cucumber or Playwright BDD
Define test steps using Gherkin keywords:
import { Given, When, Then, And } from "co-gherkin";
Given("I am on the {string} page", (pageName: string) => {
// Setup code
});
When("I click the {string} button", (buttonName: string) => {
// Action code
});
Then("I should see {string}", (text: string) => {
// Assertion code
});
And("the page title is {string}", (title: string) => {
// Additional assertion
});Use regex for more complex matching:
Given(/^I have (\d+) items in my cart$/, (count: string) => {
const itemCount = parseInt(count);
// Your logic
});Share common setup steps across scenarios:
Feature: Shopping Cart
Background:
Given I am logged in
And I have an empty cart
Scenario: Add item to cart
When I add "Product A" to cart
Then cart should have 1 item
Scenario: Remove item from cart
When I add "Product A" to cart
And I remove "Product A" from cart
Then cart should be emptyData-driven testing with examples:
Feature: Calculator
Scenario Outline: Add two numbers
Given I have entered <a> into the calculator
And I have entered <b> into the calculator
When I press add
Then the result should be <result>
Examples:
| a | b | result |
| 1 | 2 | 3 |
| 5 | 7 | 12 |
| 10 | 15 | 25 |Pass structured data to steps:
Given the following users exist:
| name | email | role |
| Alice | alice@test.com | admin |
| Bob | bob@test.com | user |Given("the following users exist:", (dataTable: string[][]) => {
dataTable.forEach(([name, email, role]) => {
createUser({ name, email, role });
});
});Execute code before/after each scenario:
import { BeforeScenario, AfterScenario } from "co-gherkin";
BeforeScenario(() => {
// Setup: clear database, reset state, etc.
});
AfterScenario(() => {
// Cleanup: close connections, clear mocks, etc.
});Run a feature file with Vitest (dynamic):
import { runFeature } from "co-gherkin";
import { resolve } from "path";
runFeature(resolve(__dirname, "./features/my-feature.feature"));Run a feature file with Playwright (synchronous):
import { runFeatureSync } from "co-gherkin";
import { resolve } from "path";
runFeatureSync(resolve(__dirname, "./features/my-feature.feature"));Configure co-gherkin for different test frameworks:
import { configureGherkin } from "co-gherkin";
import { test } from "@playwright/test";
configureGherkin({
describe: test.describe,
it: test,
beforeAll: test.beforeAll,
afterAll: test.afterAll,
beforeEach: test.beforeEach,
afterEach: test.afterEach,
});Note: Configuration is only required for Playwright. Vitest auto-detects when globals: true is enabled.
my-project/
βββ features/
β βββ login.feature
β βββ signup.feature
β βββ checkout.feature
βββ tests/
β βββ steps/
β β βββ login.steps.ts
β β βββ signup.steps.ts
β β βββ common.steps.ts
β βββ login.test.ts
β βββ signup.test.ts
β βββ checkout.test.ts
βββ vitest.config.ts
vitest.config.ts:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
},
});| Feature | Co-Gherkin | Cucumber.js | Playwright BDD |
|---|---|---|---|
| Vitest Support | β Native | β No | β No |
| TypeScript | β First-class | β Yes | |
| Unit Tests | β Yes | β Yes | β E2E only |
| Speed | β‘ Fast | π’ Slow | β‘ Fast |
| Bundle Size | πͺΆ Light | π¦ Heavy | π¦ Heavy |
| Background | β Yes | β Yes | β Yes |
| Scenario Outline | β Yes | β Yes | β Yes |
| Data Tables | β Yes | β Yes | β Yes |
See our detailed roadmap for planned features.
v1.0.0 (Stable)
- Core engine (Parser + Runner)
- TypeScript definitions
- Step definition system
- Hooks (BeforeScenario, AfterScenario)
- Background support
- Scenario Outline with Examples
- Data Tables
- >99% Code Coverage
Future Ideas
- Tags support (@smoke, @integration)
- VS Code Extension with syntax highlighting
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT Β© David GarcΓa
David GarcΓa
Built with β€οΈ for the Vitest community
"Testing, Together" - The Co-Gherkin Philosophy