ferridriver-bdd
Cucumber / Gherkin BDD framework for ferridriver. Translates .feature
files into the same TestPlan the #[ferritest] runner executes — same
parallel workers, retries, fixtures, reporters. Step bodies are written in
Rust or in JavaScript / TypeScript; both share one registry.
Rust step bodies
use *;
async
async
async
Wire a binary entry point:
// tests/bdd.rs
bdd_main!;
[[]]
= "bdd"
= "tests/bdd.rs"
= false
[]
= "0.3"
= "0.3"
# or
JavaScript / TypeScript step bodies
// steps/login.ts
Given('I navigate to {string}', async function (url: string) {
await this.page.goto(url);
});
When('I click {string}', async function (selector: string) {
await this.page.locator(selector).click();
});
Then('the URL contains {string}', async function (fragment: string) {
if (!this.page.url().includes(fragment)) throw new Error('mismatch');
});
Given / When / Then / defineStep (with And / But aliases) /
Before / After / BeforeAll / AfterAll / BeforeStep / AfterStep /
defineParameterType / setWorldConstructor / setDefaultTimeout /
setDefinitionFunctionWrapper are globals. this is the World — page,
context, browser, request, parameters, attach, log, skip.
Files are bundled with rolldown (TypeScript + node_modules + tree-shake),
compiled to QuickJS bytecode once at startup, and Module::loaded per
worker. The bytecode cache is content-hashed and in-memory. No Node, no
Bun in the run path.
Macros
#[given(EXPR)] #[when(EXPR)] #[then(EXPR)]
#[step(EXPR)] #[given(regex = PATTERN)] // …same for when/then
#[before(scenario)] #[after(scenario)]
#[before(scenario, tags = "@auth", order = 10)]
#[before(all)] #[after(all)]
#[before(feature)] #[before(step)] // and matching afters
#[param_type(name = "color", regex = "red|green|blue")]
Parameter extraction is type-directed:
String→{string}i64→{int}f64→{float}- Custom
{name}→ registered regex (extract asString)
table: &DataTable / data_table: &DataTable and docstring: &str /
doc_string: &str are recognised by name and pulled in after positional
parameters.
Hooks
Eight hook points, both Rust and JS:
BeforeAll, AfterAll, BeforeFeature, AfterFeature, BeforeScenario,
AfterScenario, BeforeStep, AfterStep. Tag filters and explicit order
are supported on every point. After* hooks run even on failure (cleanup
guarantee).
Gherkin coverage
Full Gherkin 6+: Features, Rules, Backgrounds, Scenarios, Scenario
Outlines (with named Examples blocks), tags (boolean expressions: and,
or, not, parens), data tables (with .hashes(), .rows_hash(),
.transpose(), .as_type::<T>()), doc strings (with media-type hints
like """json), the asterisk (*) keyword, and i18n keywords via
--language or # language: xx.
Scenario Outline placeholders (<key>) substitute into step text, table
cells, and doc strings recursively. At runtime, $key interpolation
reaches into world.vars() / world.set_var(name, value).
Built-in steps (145)
Source: crates/ferridriver-bdd/src/steps/. Counts reflect actual
#[given] / #[when] / #[then] / #[step] registrations.
| Module | Count | Coverage |
|---|---|---|
assertion |
34 | Text, visibility, value, attribute, class, state, count, role, ARIA |
interaction |
20 | Click / double-click / right-click, fill, clear, type, hover, focus, blur, drag, scroll, select, check, uncheck |
network |
14 | Route, fulfill, continue, abort, request / response waits, HAR |
api |
11 | API request context: GET/POST/PUT/DELETE/PATCH, headers, body, status / JSON assertions |
storage |
8 | localStorage / sessionStorage get / set / clear / remove |
wait |
7 | Wait for selector / text / navigation / seconds / load state |
navigation |
6 | Navigate, back, forward, reload, URL assertions |
frame |
6 | Switch frames by name / index, main frame, frame queries |
dialog |
5 | Accept / dismiss, prompt text, assert message |
emulation |
6 | Viewport, user agent, geolocation, color scheme, timezone, locale |
mouse |
5 | Move to coordinates, scroll by delta, wheel, button holds |
window |
5 | Window size, maximize / minimize, tab / window switching |
keyboard |
4 | Press key, press on selector, repeat N times, type slowly |
javascript |
3 | Execute, evaluate, inject script |
cookie |
3 | Add, delete, clear all |
screenshot |
3 | Full page, named file, element-scoped |
variable |
3 | Set, store text / attribute / property / count of selector |
file |
2 | Upload to input, assert download |
Call StepRegistry::reference() from a bdd_main!() binary for the live
expression list with parameter types.
Reporters
Same reporter family as ferridriver-test plus BDD-specific renderers:
terminal (Feature → Scenario → Step hierarchy with colours), json,
junit, html, cucumber-json, messages / ndjson (Cucumber Messages
NDJSON), usage, rerun, progress, dot.
Public API (programmatic use)
Bypass the CLI / macros and drive the executor directly when embedding:
use ;
use Arc;
use Duration;
let registry = build;
let executor = new;
let result = executor
.run_scenario_observed
.await;
StepRegistry::register() / register_regex() accept handler closures —
useful when registering steps from a host other than the macros (an MCP
plugin, an external test driver).
License
MIT OR Apache-2.0