Current section
Files
Jump to
Current section
Files
src/dream_test@gherkin@world.erl
-module(dream_test@gherkin@world).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream_test/gherkin/world.gleam").
-export([scenario_id/1, cleanup/1, put/3, get/2, get_or/3, has/2, delete/2, new_world/1]).
-export_type([world/0, ets_table/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(
" Scenario state management for Gherkin tests.\n"
"\n"
" Each scenario gets its own World instance for storing state between steps.\n"
" The World is automatically created before a scenario runs and cleaned up after.\n"
"\n"
" ## How It Works\n"
"\n"
" The World uses ETS (Erlang Term Storage) for mutable storage with process\n"
" isolation. Each scenario gets a unique table that's cleaned up automatically.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then, when}\n"
" import dream_test/gherkin/steps.{type StepContext, get_float, step}\n"
" import dream_test/gherkin/world.{get_or, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
" import gleam/result\n"
"\n"
" // NOTE: We annotate `StepContext` because record field access needs a known type.\n"
" fn step_have_balance(context: StepContext) {\n"
" // {float} captures the numeric value (even with $ prefix)\n"
" let balance = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", balance)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_withdraw(context: StepContext) {\n"
" let current = get_or(context.world, \"balance\", 0.0)\n"
" let amount = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", current -. amount)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_balance_is(context: StepContext) {\n"
" let expected = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" get_or(context.world, \"balance\", 0.0)\n"
" |> should\n"
" |> be_equal(expected)\n"
" |> or_fail_with(\"Balance mismatch\")\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"I have a balance of ${float}\", step_have_balance)\n"
" |> step(\"I withdraw ${float}\", step_withdraw)\n"
" |> step(\"my balance should be ${float}\", step_balance_is)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"Bank Account\", steps, [\n"
" scenario(\"Withdrawal\", [\n"
" given(\"I have a balance of $100.00\"),\n"
" when(\"I withdraw $30.00\"),\n"
" then(\"my balance should be $70.00\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-opaque world() :: {world, binary(), ets_table()}.
-type ets_table() :: any().
-file("src/dream_test/gherkin/world.gleam", 558).
?DOC(
" Get the scenario ID for this World.\n"
"\n"
" Useful for debugging and logging.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to query\n"
"\n"
" ## Returns\n"
"\n"
" The scenario ID string.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/world\n"
"\n"
" pub fn main() {\n"
" let w = world.new_world(\"example_scenario\")\n"
" let id = world.scenario_id(w)\n"
" world.cleanup(w)\n"
" id\n"
" }\n"
" ```\n"
).
-spec scenario_id(world()) -> binary().
scenario_id(World) ->
erlang:element(2, World).
-file("src/dream_test/gherkin/world.gleam", 145).
?DOC(
" Clean up the World after a scenario completes.\n"
"\n"
" Called automatically by the runner after each scenario.\n"
" Deletes the ETS table and frees resources.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to clean up\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/world\n"
"\n"
" pub fn main() {\n"
" // In normal gherkin runs, the runner creates and cleans up the World for you.\n"
" let w = world.new_world(\"example_scenario\")\n"
" world.cleanup(w)\n"
" }\n"
" ```\n"
).
-spec cleanup(world()) -> nil.
cleanup(World) ->
dream_test_gherkin_world_ffi:delete_table(erlang:element(3, World)).
-file("src/dream_test/gherkin/world.gleam", 228).
?DOC(
" Store a value in the World.\n"
"\n"
" Stores any value by string key. If the key already exists,\n"
" the value is replaced.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to store in\n"
" - `key`: String key for the value\n"
" - `value`: Any value to store\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then, when}\n"
" import dream_test/gherkin/steps.{type StepContext, get_float, step}\n"
" import dream_test/gherkin/world.{get_or, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
" import gleam/result\n"
"\n"
" // NOTE: We annotate `StepContext` because record field access needs a known type.\n"
" fn step_have_balance(context: StepContext) {\n"
" // {float} captures the numeric value (even with $ prefix)\n"
" let balance = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", balance)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_withdraw(context: StepContext) {\n"
" let current = get_or(context.world, \"balance\", 0.0)\n"
" let amount = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", current -. amount)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_balance_is(context: StepContext) {\n"
" let expected = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" get_or(context.world, \"balance\", 0.0)\n"
" |> should\n"
" |> be_equal(expected)\n"
" |> or_fail_with(\"Balance mismatch\")\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"I have a balance of ${float}\", step_have_balance)\n"
" |> step(\"I withdraw ${float}\", step_withdraw)\n"
" |> step(\"my balance should be ${float}\", step_balance_is)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"Bank Account\", steps, [\n"
" scenario(\"Withdrawal\", [\n"
" given(\"I have a balance of $100.00\"),\n"
" when(\"I withdraw $30.00\"),\n"
" then(\"my balance should be $70.00\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec put(world(), binary(), any()) -> nil.
put(World, Key, Value) ->
dream_test_gherkin_world_ffi:insert(erlang:element(3, World), Key, Value).
-file("src/dream_test/gherkin/world.gleam", 301).
?DOC(
" Retrieve a value from the World.\n"
"\n"
" Returns `Ok(value)` if the key exists, `Error(message)` if not found.\n"
" The caller is responsible for ensuring type consistency.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to query\n"
" - `key`: String key to look up\n"
"\n"
" ## Returns\n"
"\n"
" - `Ok(value)`: Key exists, returns the stored value\n"
" - `Error(String)`: Key doesn't exist (human-readable message)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then}\n"
" import dream_test/gherkin/steps.{type StepContext, step}\n"
" import dream_test/gherkin/world.{get, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
"\n"
" fn step_store(context: StepContext) {\n"
" put(context.world, \"count\", 42)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_count_is_42(context: StepContext) {\n"
" case get(context.world, \"count\") {\n"
" Ok(count) ->\n"
" count\n"
" |> should\n"
" |> be_equal(42)\n"
" |> or_fail_with(\"count mismatch\")\n"
" Error(message) -> Error(message)\n"
" }\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"count is stored\", step_store)\n"
" |> step(\"count should be 42\", step_count_is_42)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"World: get\", steps, [\n"
" scenario(\"Reading a stored value\", [\n"
" given(\"count is stored\"),\n"
" then(\"count should be 42\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec get(world(), binary()) -> {ok, any()} | {error, binary()}.
get(World, Key) ->
case dream_test_gherkin_world_ffi:lookup(erlang:element(3, World), Key) of
{some, Value} ->
{ok, Value};
none ->
{error, <<"World key not found: "/utf8, Key/binary>>}
end.
-file("src/dream_test/gherkin/world.gleam", 383).
?DOC(
" Retrieve a value or return a default.\n"
"\n"
" Convenience function that returns the stored value if the key exists,\n"
" or the provided default if not found.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to query\n"
" - `key`: String key to look up\n"
" - `default`: Default value to return if key not found\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then, when}\n"
" import dream_test/gherkin/steps.{type StepContext, get_float, step}\n"
" import dream_test/gherkin/world.{get_or, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
" import gleam/result\n"
"\n"
" // NOTE: We annotate `StepContext` because record field access needs a known type.\n"
" fn step_have_balance(context: StepContext) {\n"
" // {float} captures the numeric value (even with $ prefix)\n"
" let balance = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", balance)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_withdraw(context: StepContext) {\n"
" let current = get_or(context.world, \"balance\", 0.0)\n"
" let amount = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" put(context.world, \"balance\", current -. amount)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_balance_is(context: StepContext) {\n"
" let expected = get_float(context.captures, 0) |> result.unwrap(0.0)\n"
" get_or(context.world, \"balance\", 0.0)\n"
" |> should\n"
" |> be_equal(expected)\n"
" |> or_fail_with(\"Balance mismatch\")\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"I have a balance of ${float}\", step_have_balance)\n"
" |> step(\"I withdraw ${float}\", step_withdraw)\n"
" |> step(\"my balance should be ${float}\", step_balance_is)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"Bank Account\", steps, [\n"
" scenario(\"Withdrawal\", [\n"
" given(\"I have a balance of $100.00\"),\n"
" when(\"I withdraw $30.00\"),\n"
" then(\"my balance should be $70.00\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec get_or(world(), binary(), GJI) -> GJI.
get_or(World, Key, Default) ->
case get(World, Key) of
{ok, Value} ->
Value;
{error, _} ->
Default
end.
-file("src/dream_test/gherkin/world.gleam", 456).
?DOC(
" Check if a key exists in the World.\n"
"\n"
" Returns `True` if the key exists, `False` otherwise.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to query\n"
" - `key`: String key to check\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then, when}\n"
" import dream_test/gherkin/steps.{type StepContext, step}\n"
" import dream_test/gherkin/world.{delete, has, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
"\n"
" fn step_store(context: StepContext) {\n"
" put(context.world, \"temp\", True)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_delete(context: StepContext) {\n"
" delete(context.world, \"temp\")\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_is_absent(context: StepContext) {\n"
" has(context.world, \"temp\")\n"
" |> should\n"
" |> be_equal(False)\n"
" |> or_fail_with(\"expected temp to be absent\")\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"temp is stored\", step_store)\n"
" |> step(\"temp is deleted\", step_delete)\n"
" |> step(\"temp should be absent\", step_is_absent)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"World: has + delete\", steps, [\n"
" scenario(\"Deleting a key\", [\n"
" given(\"temp is stored\"),\n"
" when(\"temp is deleted\"),\n"
" then(\"temp should be absent\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec has(world(), binary()) -> boolean().
has(World, Key) ->
case dream_test_gherkin_world_ffi:lookup(erlang:element(3, World), Key) of
{some, _} ->
true;
none ->
false
end.
-file("src/dream_test/gherkin/world.gleam", 529).
?DOC(
" Delete a key from the World.\n"
"\n"
" Removes a key-value pair. If the key doesn't exist, this is a no-op.\n"
"\n"
" ## Parameters\n"
"\n"
" - `world`: The World to modify\n"
" - `key`: String key to delete\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/feature.{feature, given, scenario, then, when}\n"
" import dream_test/gherkin/steps.{type StepContext, step}\n"
" import dream_test/gherkin/world.{delete, has, put}\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import gleam/io\n"
"\n"
" fn step_store(context: StepContext) {\n"
" put(context.world, \"temp\", True)\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_delete(context: StepContext) {\n"
" delete(context.world, \"temp\")\n"
" Ok(succeed())\n"
" }\n"
"\n"
" fn step_is_absent(context: StepContext) {\n"
" has(context.world, \"temp\")\n"
" |> should\n"
" |> be_equal(False)\n"
" |> or_fail_with(\"expected temp to be absent\")\n"
" }\n"
"\n"
" pub fn register(registry) {\n"
" registry\n"
" |> step(\"temp is stored\", step_store)\n"
" |> step(\"temp is deleted\", step_delete)\n"
" |> step(\"temp should be absent\", step_is_absent)\n"
" }\n"
"\n"
" pub fn tests() {\n"
" let steps = steps.new() |> register()\n"
"\n"
" feature(\"World: has + delete\", steps, [\n"
" scenario(\"Deleting a key\", [\n"
" given(\"temp is stored\"),\n"
" when(\"temp is deleted\"),\n"
" then(\"temp should be absent\"),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec delete(world(), binary()) -> nil.
delete(World, Key) ->
dream_test_gherkin_world_ffi:delete_key(erlang:element(3, World), Key).
-file("src/dream_test/gherkin/world.gleam", 118).
?DOC(
" Create a new World for a scenario.\n"
"\n"
" Called automatically by the runner before each scenario.\n"
" Each World gets a unique ETS table for isolated storage.\n"
"\n"
" ## Parameters\n"
"\n"
" - `scenario_id`: Unique identifier for the scenario\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/gherkin/world\n"
"\n"
" pub fn main() {\n"
" // In normal gherkin runs, the runner creates and cleans up the World for you.\n"
" let w = world.new_world(\"example_scenario\")\n"
" world.cleanup(w)\n"
" }\n"
" ```\n"
).
-spec new_world(binary()) -> world().
new_world(Scenario_id) ->
Table_name = <<<<<<"gherkin_world_"/utf8, Scenario_id/binary>>/binary,
"_"/utf8>>/binary,
(dream_test_gherkin_world_ffi:unique_id())/binary>>,
Table = dream_test_gherkin_world_ffi:create_table(Table_name),
{world, Scenario_id, Table}.