type-safe wrapper for playwright-bdd
npm install typed-playwright-bddTyped, schema-driven step definitions for BDD frameworks with playwright-bdd signatures.
typed-playwright-bdd provides a typed API for defining BDD steps using tagged template literals and Standard Schema-compliant schemas. It generates regex patterns from JSON Schema and validates arguments at runtime.
``bash`
npm install typed-playwright-bdd
You'll also need a schema library that implements Standard Schema v1, such as Zod:
`bash`
npm install zod
`gherkin
Feature: User Registration
Scenario: Fill in form
When I fill in my email "john@example.com"
And I fill "age" with 25
Then I should see 1 items
`
`typescript
import { expect } from "@playwright/test";
import { createBdd } from "playwright-bdd";
import { createTypedBdd } from "typed-playwright-bdd";
import { z } from "zod";
const { Given, When, Then } = createTypedBdd(createBdd());
WhenI fill in my email ${z.email()}(async ({ page }, email) => {
await page.getByLabel("Email").fill(email);
});
WhenI fill ${z.string()} with ${z.union([z.number(), z.string()])}(
async ({ page }, field, value) => {
await page.getByLabel(new RegExp(field, "i")).fill(value);
},
);
ThenI should see ${z.number()} items(async ({ page }, count) => {`
await expect(page.locator(".item").count()).toBe(count);
});
Use literals for exact matches:
`typescriptI click the ${z.literal("submit")} button
// "When I click the submit button"
When(button[type="${buttonType}"]
async ({ page }, buttonType) => {
await page.click();`
},
);
Create alternatives with unions:
`typescript
const direction = z.union([
z.literal("left"),
z.literal("right"),
z.literal("up"),
z.literal("down"),
]);
// "When I swipe left"
WhenI swipe ${direction}(async ({ page }, dir) => {`
await page.swipe(dir);
});
You can use transformations to transform the value before it is passed to the handler.
`typescript
const booleanSchema = z
.union([z.literal("true"), z.literal("false")])
.transform((val) => val === "true");
// "When I set value to true"
WhenI set value to ${booleanSchema}((_, value) => {`
if (value === true) {
// do something
} else {
// do something else
}
});
The original string-based API remains available:
`typescript
// Traditional approach still works
When("I fill in {string}", (async { page }, value) => {
await page.fill("input", value);
});
// Mix and match as needed
WhenI type ${z.string()}(async({ page }, text) => {`
await page.keyboard.type(text);
});
typed-playwright-bdd converts schemas to regex patterns based on JSON Schema:
| Schema | Matches | Regex Pattern |
| ------------------------------------------- | -------------------- | ---------------------- |
| z.string() | "hello", 'world' | "'["'] |z.number()
| | 42, -3.14 | ([-+]?\d+(?:\.\d+)?) |z.literal("test")
| | test | (test) |z.union([z.literal("a"), z.literal("b")])
| | a, b | (a\|b) |
Schemas must implement StandardSchemaV1 and StandardJSONSchemaV1 from @standard-schema/spec. This means they must provide:
- schema["~standard"].jsonSchema.input() — Returns JSON Schema for the input typeschema["~standard"].validate(value)
- — Validates a value and returns a result
Libraries that support Standard Schema v1:
The underlying BDD framework must support:
`typescript`
(pattern: string | RegExp, handler: Function) => void
With Playwright-style handlers:
`typescript`
({ page, ...context }, ...args) => Promise
Compatible frameworks:
- playwright-bdd
- Any BDD framework with a similar API
Complex types like objects and arrays are not supported in this version:
`typescriptI submit ${z.object({ name: z.string() })}
// ❌ Not supported
When(...)I select ${z.array(z.string())}
When(...)`
Reason: Regex-based parsing cannot reliably handle nested JSON structures. This may be added in a future version with explicit opt-in.
Whitespace in templates is matched literally:
`typescriptI fill in ${z.string()}
// These are different patterns:
When; // Two spacesI fill in ${z.string()}
When; // One space``
MIT
Issues and pull requests are welcome on GitHub.