Current section
Files
Jump to
Current section
Files
src/dream_test@gherkin@steps.erl
-module(dream_test@gherkin@steps).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream_test/gherkin/steps.gleam").
-export([new_registry/0, given/3, when_/3, then_/3, step/3, find_step/3, capture_count/1, get_int/2, get_float/2, get_string/2, get_word/2]).
-export_type([step_context/0, step_registry/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Step definition registry for Gherkin scenarios.\n"
"\n"
" This module provides the user-facing API for defining step definitions\n"
" that match Gherkin steps (Given/When/Then) to Gleam handler functions.\n"
"\n"
" ## Quick Start\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/steps.{\n"
" type StepContext, given, new_registry, then_, when_, get_int, get_float,\n"
" }\n"
"\n"
" pub fn cart_steps() -> StepRegistry {\n"
" new_registry()\n"
" |> given(\"I have {int} items in my cart\", have_items)\n"
" |> when_(\"I add {int} items of {word}\", add_items)\n"
" |> then_(\"the total should be ${float}\", check_total)\n"
" }\n"
"\n"
" fn have_items(context: StepContext) -> AssertionResult {\n"
" case get_int(context.captures, 0) {\n"
" Ok(count) -> {\n"
" world.put(context.world, \"count\", count)\n"
" AssertionOk\n"
" }\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" }\n"
" ```\n"
"\n"
" ## Pattern Syntax\n"
"\n"
" Step patterns use Cucumber Expression syntax with typed placeholders:\n"
"\n"
" | Placeholder | Matches | Example |\n"
" |-------------|-----------------------|----------------|\n"
" | `{int}` | Integers | `42`, `-5` |\n"
" | `{float}` | Decimals | `3.14`, `-0.5` |\n"
" | `{string}` | Quoted strings | `\"hello\"` |\n"
" | `{word}` | Single unquoted word | `apple` |\n"
" | `{}` | Any single token | (anonymous) |\n"
"\n"
" ## Prefix and Suffix Support\n"
"\n"
" Placeholders can have literal prefixes and suffixes attached:\n"
"\n"
" | Pattern | Matches | Captures |\n"
" |---------|---------|----------|\n"
" | `${float}` | `$19.99` | `19.99` as Float |\n"
" | `{int}%` | `50%` | `50` as Int |\n"
" | `${float}USD` | `$99.99USD` | `99.99` as Float |\n"
"\n"
" This is useful for currency, percentages, and other formatted values.\n"
"\n"
" ## Capture Extraction\n"
"\n"
" Use the typed extraction helpers to get captured values:\n"
"\n"
" ```gleam\n"
" fn check_total(context: StepContext) -> AssertionResult {\n"
" // Pattern was \"the total should be ${float}\"\n"
" // Step text was \"the total should be $19.99\"\n"
" case get_float(context.captures, 0) {\n"
" Ok(amount) -> {\n"
" // amount is 19.99 (the $ prefix was matched but not captured)\n"
" AssertionOk\n"
" }\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" }\n"
" ```\n"
).
-type step_context() :: {step_context,
list(dream_test@gherkin@step_trie:captured_value()),
gleam@option:option(list(list(binary()))),
gleam@option:option(binary()),
dream_test@gherkin@world:world()}.
-opaque step_registry() :: {step_registry,
dream_test@gherkin@step_trie:step_trie(fun((step_context()) -> dream_test@types:assertion_result()))}.
-file("src/dream_test/gherkin/steps.gleam", 141).
?DOC(
" Create an empty step registry.\n"
"\n"
" Start with this and chain `given`, `when_`, `then_` calls to add steps.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let steps = new_registry()\n"
" |> given(\"I have {int} items\", have_items)\n"
" |> when_(\"I add {int} more\", add_items)\n"
" |> then_(\"I should have {int} total\", check_total)\n"
" ```\n"
).
-spec new_registry() -> step_registry().
new_registry() ->
{step_registry, dream_test@gherkin@step_trie:new()}.
-file("src/dream_test/gherkin/steps.gleam", 163).
?DOC(
" Register a Given step definition.\n"
"\n"
" Given steps describe initial context or preconditions.\n"
"\n"
" ## Parameters\n"
"\n"
" - `registry`: The registry to add to\n"
" - `pattern`: Step pattern with placeholders\n"
" - `handler`: Handler function to execute\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" new_registry()\n"
" |> given(\"I have {int} items in my cart\", have_items)\n"
" |> given(\"I am logged in as {string}\", logged_in_as)\n"
" ```\n"
).
-spec given(
step_registry(),
binary(),
fun((step_context()) -> dream_test@types:assertion_result())
) -> step_registry().
given(Registry, Pattern, Handler) ->
Updated = dream_test@gherkin@step_trie:insert(
erlang:element(2, Registry),
<<"Given"/utf8>>,
Pattern,
Handler
),
{step_registry, Updated}.
-file("src/dream_test/gherkin/steps.gleam", 192).
?DOC(
" Register a When step definition.\n"
"\n"
" When steps describe an action or event.\n"
"\n"
" Note: Named `when_` with trailing underscore because `when` is reserved.\n"
"\n"
" ## Parameters\n"
"\n"
" - `registry`: The registry to add to\n"
" - `pattern`: Step pattern with placeholders\n"
" - `handler`: Handler function to execute\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" new_registry()\n"
" |> when_(\"I add {int} items of {string}\", add_items)\n"
" |> when_(\"I click the {string} button\", click_button)\n"
" ```\n"
).
-spec when_(
step_registry(),
binary(),
fun((step_context()) -> dream_test@types:assertion_result())
) -> step_registry().
when_(Registry, Pattern, Handler) ->
Updated = dream_test@gherkin@step_trie:insert(
erlang:element(2, Registry),
<<"When"/utf8>>,
Pattern,
Handler
),
{step_registry, Updated}.
-file("src/dream_test/gherkin/steps.gleam", 221).
?DOC(
" Register a Then step definition.\n"
"\n"
" Then steps describe expected outcomes or assertions.\n"
"\n"
" Note: Named `then_` with trailing underscore because `then` is reserved.\n"
"\n"
" ## Parameters\n"
"\n"
" - `registry`: The registry to add to\n"
" - `pattern`: Step pattern with placeholders\n"
" - `handler`: Handler function to execute\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" new_registry()\n"
" |> then_(\"my cart should have {int} items\", check_count)\n"
" |> then_(\"I should see {string}\", check_text)\n"
" ```\n"
).
-spec then_(
step_registry(),
binary(),
fun((step_context()) -> dream_test@types:assertion_result())
) -> step_registry().
then_(Registry, Pattern, Handler) ->
Updated = dream_test@gherkin@step_trie:insert(
erlang:element(2, Registry),
<<"Then"/utf8>>,
Pattern,
Handler
),
{step_registry, Updated}.
-file("src/dream_test/gherkin/steps.gleam", 247).
?DOC(
" Register a step that matches any keyword.\n"
"\n"
" Use this for steps that work regardless of Given/When/Then context.\n"
"\n"
" ## Parameters\n"
"\n"
" - `registry`: The registry to add to\n"
" - `pattern`: Step pattern with placeholders\n"
" - `handler`: Handler function to execute\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" new_registry()\n"
" |> step(\"I wait {int} seconds\", wait_seconds)\n"
" ```\n"
).
-spec step(
step_registry(),
binary(),
fun((step_context()) -> dream_test@types:assertion_result())
) -> step_registry().
step(Registry, Pattern, Handler) ->
Updated = dream_test@gherkin@step_trie:insert(
erlang:element(2, Registry),
<<"*"/utf8>>,
Pattern,
Handler
),
{step_registry, Updated}.
-file("src/dream_test/gherkin/steps.gleam", 290).
-spec keyword_to_string(dream_test@gherkin@types:step_keyword()) -> binary().
keyword_to_string(Keyword) ->
case Keyword of
given ->
<<"Given"/utf8>>;
'when' ->
<<"When"/utf8>>;
then ->
<<"Then"/utf8>>;
'and' ->
<<"And"/utf8>>;
but ->
<<"But"/utf8>>
end.
-file("src/dream_test/gherkin/steps.gleam", 278).
?DOC(
" Find a matching step definition.\n"
"\n"
" Searches the registry for a handler matching the given keyword and text.\n"
" Returns the handler and captured values on success, or an error message.\n"
"\n"
" This is O(words in step text), not O(number of step definitions).\n"
"\n"
" ## Parameters\n"
"\n"
" - `registry`: The registry to search\n"
" - `keyword`: Step keyword (Given, When, Then, And, But)\n"
" - `text`: Step text to match\n"
"\n"
" ## Returns\n"
"\n"
" - `Ok(StepMatch)`: Contains matched handler and captured values\n"
" - `Error(String)`: Error message if no match found\n"
).
-spec find_step(
step_registry(),
dream_test@gherkin@types:step_keyword(),
binary()
) -> {ok,
dream_test@gherkin@step_trie:step_match(fun((step_context()) -> dream_test@types:assertion_result()))} |
{error, binary()}.
find_step(Registry, Keyword, Text) ->
Keyword_str = keyword_to_string(Keyword),
case dream_test@gherkin@step_trie:lookup(
erlang:element(2, Registry),
Keyword_str,
Text
) of
{some, Match} ->
{ok, Match};
none ->
{error,
<<<<<<"Undefined step: "/utf8, Keyword_str/binary>>/binary,
" "/utf8>>/binary,
Text/binary>>}
end.
-file("src/dream_test/gherkin/steps.gleam", 446).
?DOC(
" Get the number of captures.\n"
"\n"
" Returns the total number of values captured from placeholders.\n"
"\n"
" ## Parameters\n"
"\n"
" - `captures`: List of captured values from StepContext\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let count = capture_count(context.captures)\n"
" // For \"I add {int} items of {string}\", count would be 2\n"
" ```\n"
).
-spec capture_count(list(dream_test@gherkin@step_trie:captured_value())) -> integer().
capture_count(Captures) ->
erlang:length(Captures).
-file("src/dream_test/gherkin/steps.gleam", 458).
-spec list_at_loop(list(HBM), integer(), integer()) -> gleam@option:option(HBM).
list_at_loop(Items, Target, Current) ->
case Items of
[] ->
none;
[Head | Tail] ->
case Current =:= Target of
true ->
{some, Head};
false ->
list_at_loop(Tail, Target, Current + 1)
end
end.
-file("src/dream_test/gherkin/steps.gleam", 454).
-spec list_at(list(HBJ), integer()) -> gleam@option:option(HBJ).
list_at(Items, Index) ->
list_at_loop(Items, Index, 0).
-file("src/dream_test/gherkin/steps.gleam", 325).
?DOC(
" Get an Int capture by index (0-based).\n"
"\n"
" Returns the captured integer value at the given index, or an error\n"
" if the index is out of bounds or the value isn't an integer.\n"
"\n"
" ## Parameters\n"
"\n"
" - `captures`: List of captured values from StepContext\n"
" - `index`: 0-based index of the capture to retrieve\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // Pattern: \"I have {int} items\"\n"
" // Step: \"I have 42 items\"\n"
" case get_int(context.captures, 0) {\n"
" Ok(count) -> // count = 42\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" ```\n"
).
-spec get_int(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok,
integer()} |
{error, binary()}.
get_int(Captures, Index) ->
case list_at(Captures, Index) of
{some, {captured_int, Value}} ->
{ok, Value};
{some, _} ->
{error, <<"Capture at index is not an integer"/utf8>>};
none ->
{error, <<"No capture at index"/utf8>>}
end.
-file("src/dream_test/gherkin/steps.gleam", 354).
?DOC(
" Get a Float capture by index (0-based).\n"
"\n"
" Returns the captured float value at the given index, or an error\n"
" if the index is out of bounds or the value isn't a float.\n"
"\n"
" ## Parameters\n"
"\n"
" - `captures`: List of captured values from StepContext\n"
" - `index`: 0-based index of the capture to retrieve\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // Pattern: \"the price is {float} dollars\"\n"
" // Step: \"the price is 19.99 dollars\"\n"
" case get_float(context.captures, 0) {\n"
" Ok(price) -> // price = 19.99\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" ```\n"
).
-spec get_float(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok,
float()} |
{error, binary()}.
get_float(Captures, Index) ->
case list_at(Captures, Index) of
{some, {captured_float, Value}} ->
{ok, Value};
{some, _} ->
{error, <<"Capture at index is not a float"/utf8>>};
none ->
{error, <<"No capture at index"/utf8>>}
end.
-file("src/dream_test/gherkin/steps.gleam", 386).
?DOC(
" Get a String capture by index (0-based).\n"
"\n"
" Returns the captured string value at the given index. Works for\n"
" both {string} (quoted) and {word} captures.\n"
"\n"
" ## Parameters\n"
"\n"
" - `captures`: List of captured values from StepContext\n"
" - `index`: 0-based index of the capture to retrieve\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // Pattern: \"I add items of {string}\"\n"
" // Step: \"I add items of \\\"Red Widget\\\"\"\n"
" case get_string(context.captures, 0) {\n"
" Ok(product) -> // product = \"Red Widget\"\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" ```\n"
).
-spec get_string(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok,
binary()} |
{error, binary()}.
get_string(Captures, Index) ->
case list_at(Captures, Index) of
{some, {captured_string, Value}} ->
{ok, Value};
{some, {captured_word, Value@1}} ->
{ok, Value@1};
{some, _} ->
{error, <<"Capture at index is not a string"/utf8>>};
none ->
{error, <<"No capture at index"/utf8>>}
end.
-file("src/dream_test/gherkin/steps.gleam", 419).
?DOC(
" Get a word capture by index (0-based).\n"
"\n"
" Returns the captured word value at the given index. Works for\n"
" both {word} and {} (anonymous) captures.\n"
"\n"
" ## Parameters\n"
"\n"
" - `captures`: List of captured values from StepContext\n"
" - `index`: 0-based index of the capture to retrieve\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // Pattern: \"the user {word} exists\"\n"
" // Step: \"the user alice exists\"\n"
" case get_word(context.captures, 0) {\n"
" Ok(username) -> // username = \"alice\"\n"
" Error(msg) -> fail_with(msg)\n"
" }\n"
" ```\n"
).
-spec get_word(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok,
binary()} |
{error, binary()}.
get_word(Captures, Index) ->
case list_at(Captures, Index) of
{some, {captured_word, Value}} ->
{ok, Value};
{some, {captured_string, Value@1}} ->
{ok, Value@1};
{some, _} ->
{error, <<"Capture at index is not a word"/utf8>>};
none ->
{error, <<"No capture at index"/utf8>>}
end.