dream_test/gherkin/steps
Step definition registry for Gherkin scenarios.
This module provides the user-facing API for defining step definitions that match Gherkin steps (Given/When/Then) to Gleam handler functions.
Quick Start
import dream_test/gherkin/steps.{
type StepContext, given, new_registry, then_, when_, get_int, get_float,
}
pub fn cart_steps() -> StepRegistry {
new_registry()
|> given("I have {int} items in my cart", have_items)
|> when_("I add {int} items of {word}", add_items)
|> then_("the total should be ${float}", check_total)
}
fn have_items(context: StepContext) -> AssertionResult {
case get_int(context.captures, 0) {
Ok(count) -> {
world.put(context.world, "count", count)
AssertionOk
}
Error(msg) -> fail_with(msg)
}
}
Pattern Syntax
Step patterns use Cucumber Expression syntax with typed placeholders:
| Placeholder | Matches | Example |
|---|---|---|
{int} | Integers | 42, -5 |
{float} | Decimals | 3.14, -0.5 |
{string} | Quoted strings | "hello" |
{word} | Single unquoted word | apple |
{} | Any single token | (anonymous) |
Prefix and Suffix Support
Placeholders can have literal prefixes and suffixes attached:
| Pattern | Matches | Captures |
|---|---|---|
${float} | $19.99 | 19.99 as Float |
{int}% | 50% | 50 as Int |
${float}USD | $99.99USD | 99.99 as Float |
This is useful for currency, percentages, and other formatted values.
Capture Extraction
Use the typed extraction helpers to get captured values:
fn check_total(context: StepContext) -> AssertionResult {
// Pattern was "the total should be ${float}"
// Step text was "the total should be $19.99"
case get_float(context.captures, 0) {
Ok(amount) -> {
// amount is 19.99 (the $ prefix was matched but not captured)
AssertionOk
}
Error(msg) -> fail_with(msg)
}
}
Types
Context passed to every step handler.
Contains everything a step needs to execute:
captures- Values captured from pattern placeholderstable- Optional DataTable argument from the stepdoc_string- Optional DocString argument from the stepworld- Mutable scenario state
pub type StepContext {
StepContext(
captures: List(step_trie.CapturedValue),
table: option.Option(List(List(String))),
doc_string: option.Option(String),
world: world.World,
)
}
Constructors
-
StepContext( captures: List(step_trie.CapturedValue), table: option.Option(List(List(String))), doc_string: option.Option(String), world: world.World, )Arguments
- captures
-
Values captured from pattern placeholders, in order
- table
-
Optional DataTable attached to the step
- doc_string
-
Optional DocString attached to the step
- world
-
Mutable scenario state for sharing data between steps
Type alias for step handler functions.
All step handlers have the same signature: they receive a StepContext and return an AssertionResult.
pub type StepHandler =
fn(StepContext) -> types.AssertionResult
Step registry backed by radix trie.
Stores step definitions and provides O(words) lookup.
pub opaque type StepRegistry
Values
pub fn capture_count(
captures: List(step_trie.CapturedValue),
) -> Int
Get the number of captures.
Returns the total number of values captured from placeholders.
Parameters
captures: List of captured values from StepContext
Example
let count = capture_count(context.captures)
// For "I add {int} items of {string}", count would be 2
pub fn find_step(
registry: StepRegistry,
keyword: types.StepKeyword,
text: String,
) -> Result(
step_trie.StepMatch(fn(StepContext) -> types.AssertionResult),
String,
)
Find a matching step definition.
Searches the registry for a handler matching the given keyword and text. Returns the handler and captured values on success, or an error message.
This is O(words in step text), not O(number of step definitions).
Parameters
registry: The registry to searchkeyword: Step keyword (Given, When, Then, And, But)text: Step text to match
Returns
Ok(StepMatch): Contains matched handler and captured valuesError(String): Error message if no match found
pub fn get_float(
captures: List(step_trie.CapturedValue),
index: Int,
) -> Result(Float, String)
Get a Float capture by index (0-based).
Returns the captured float value at the given index, or an error if the index is out of bounds or the value isn’t a float.
Parameters
captures: List of captured values from StepContextindex: 0-based index of the capture to retrieve
Example
// Pattern: "the price is {float} dollars"
// Step: "the price is 19.99 dollars"
case get_float(context.captures, 0) {
Ok(price) -> // price = 19.99
Error(msg) -> fail_with(msg)
}
pub fn get_int(
captures: List(step_trie.CapturedValue),
index: Int,
) -> Result(Int, String)
Get an Int capture by index (0-based).
Returns the captured integer value at the given index, or an error if the index is out of bounds or the value isn’t an integer.
Parameters
captures: List of captured values from StepContextindex: 0-based index of the capture to retrieve
Example
// Pattern: "I have {int} items"
// Step: "I have 42 items"
case get_int(context.captures, 0) {
Ok(count) -> // count = 42
Error(msg) -> fail_with(msg)
}
pub fn get_string(
captures: List(step_trie.CapturedValue),
index: Int,
) -> Result(String, String)
Get a String capture by index (0-based).
Returns the captured string value at the given index. Works for both {string} (quoted) and {word} captures.
Parameters
captures: List of captured values from StepContextindex: 0-based index of the capture to retrieve
Example
// Pattern: "I add items of {string}"
// Step: "I add items of \"Red Widget\""
case get_string(context.captures, 0) {
Ok(product) -> // product = "Red Widget"
Error(msg) -> fail_with(msg)
}
pub fn get_word(
captures: List(step_trie.CapturedValue),
index: Int,
) -> Result(String, String)
Get a word capture by index (0-based).
Returns the captured word value at the given index. Works for both {word} and {} (anonymous) captures.
Parameters
captures: List of captured values from StepContextindex: 0-based index of the capture to retrieve
Example
// Pattern: "the user {word} exists"
// Step: "the user alice exists"
case get_word(context.captures, 0) {
Ok(username) -> // username = "alice"
Error(msg) -> fail_with(msg)
}
pub fn given(
registry: StepRegistry,
pattern: String,
handler: fn(StepContext) -> types.AssertionResult,
) -> StepRegistry
Register a Given step definition.
Given steps describe initial context or preconditions.
Parameters
registry: The registry to add topattern: Step pattern with placeholdershandler: Handler function to execute
Example
new_registry()
|> given("I have {int} items in my cart", have_items)
|> given("I am logged in as {string}", logged_in_as)
pub fn new_registry() -> StepRegistry
Create an empty step registry.
Start with this and chain given, when_, then_ calls to add steps.
Example
let steps = new_registry()
|> given("I have {int} items", have_items)
|> when_("I add {int} more", add_items)
|> then_("I should have {int} total", check_total)
pub fn step(
registry: StepRegistry,
pattern: String,
handler: fn(StepContext) -> types.AssertionResult,
) -> StepRegistry
Register a step that matches any keyword.
Use this for steps that work regardless of Given/When/Then context.
Parameters
registry: The registry to add topattern: Step pattern with placeholdershandler: Handler function to execute
Example
new_registry()
|> step("I wait {int} seconds", wait_seconds)
pub fn then_(
registry: StepRegistry,
pattern: String,
handler: fn(StepContext) -> types.AssertionResult,
) -> StepRegistry
Register a Then step definition.
Then steps describe expected outcomes or assertions.
Note: Named then_ with trailing underscore because then is reserved.
Parameters
registry: The registry to add topattern: Step pattern with placeholdershandler: Handler function to execute
Example
new_registry()
|> then_("my cart should have {int} items", check_count)
|> then_("I should see {string}", check_text)
pub fn when_(
registry: StepRegistry,
pattern: String,
handler: fn(StepContext) -> types.AssertionResult,
) -> StepRegistry
Register a When step definition.
When steps describe an action or event.
Note: Named when_ with trailing underscore because when is reserved.
Parameters
registry: The registry to add topattern: Step pattern with placeholdershandler: Handler function to execute
Example
new_registry()
|> when_("I add {int} items of {string}", add_items)
|> when_("I click the {string} button", click_button)