-module(dream_test@unit). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream_test/unit.gleam"). -export([it/2, describe/2, before_all/1, before_each/1, after_each/1, after_all/1, to_test_suite/2, to_test_cases/2]). -export_type([unit_test/0, hook_context/0, suite_hooks/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( " Unit test DSL for dream_test.\n" "\n" " This module provides a BDD-style syntax for defining tests: `describe`,\n" " `it`, and lifecycle hooks (`before_all`, `before_each`, `after_each`,\n" " `after_all`). Tests are organized hierarchically and converted to\n" " runnable test cases or suites.\n" "\n" " ## Quick Start\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, to_test_cases}\n" " import dream_test/assertions/should.{should, equal, or_fail_with}\n" " import dream_test/runner.{run_all}\n" " import dream_test/reporter/bdd.{report}\n" " import gleam/io\n" "\n" " pub fn main() {\n" " tests()\n" " |> to_test_cases(\"my_module_test\")\n" " |> run_all()\n" " |> report(io.print)\n" " }\n" "\n" " pub fn tests() {\n" " describe(\"Calculator\", [\n" " describe(\"add\", [\n" " it(\"adds positive numbers\", fn() {\n" " add(2, 3)\n" " |> should()\n" " |> equal(5)\n" " |> or_fail_with(\"2 + 3 should equal 5\")\n" " }),\n" " it(\"handles zero\", fn() {\n" " add(0, 5)\n" " |> should()\n" " |> equal(5)\n" " |> or_fail_with(\"0 + 5 should equal 5\")\n" " }),\n" " ]),\n" " ])\n" " }\n" " ```\n" "\n" " ## Output\n" "\n" " ```text\n" " Calculator\n" " add\n" " ✓ adds positive numbers\n" " ✓ handles zero\n" "\n" " Summary: 2 run, 0 failed, 2 passed\n" " ```\n" "\n" " ## Lifecycle Hooks\n" "\n" " Setup and teardown logic for tests:\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_each, after_each, to_test_cases}\n" " import dream_test/types.{AssertionOk}\n" "\n" " describe(\"Database\", [\n" " before_each(fn() {\n" " reset_database()\n" " AssertionOk\n" " }),\n" "\n" " it(\"creates users\", fn() { ... }),\n" " it(\"queries users\", fn() { ... }),\n" "\n" " after_each(fn() {\n" " rollback()\n" " AssertionOk\n" " }),\n" " ])\n" " ```\n" "\n" " | Hook | Runs | Requires |\n" " |---------------|-----------------------------------|-------------------|\n" " | `before_all` | Once before all tests in group | `to_test_suite` |\n" " | `before_each` | Before each test | Either mode |\n" " | `after_each` | After each test (always) | Either mode |\n" " | `after_all` | Once after all tests in group | `to_test_suite` |\n" "\n" " ## Two Execution Modes\n" "\n" " **Flat mode** — faster, simpler, no `before_all`/`after_all`:\n" "\n" " ```gleam\n" " tests() |> to_test_cases(\"my_test\") |> run_all()\n" " ```\n" "\n" " **Suite mode** — supports all hooks, preserves group structure:\n" "\n" " ```gleam\n" " tests() |> to_test_suite(\"my_test\") |> run_suite()\n" " ```\n" "\n" " ## Nesting\n" "\n" " You can nest `describe` blocks as deeply as needed. Each level adds to\n" " the test's `full_name`, which the reporter uses for grouping output.\n" " Lifecycle hooks are inherited by nested groups.\n" "\n" " ```gleam\n" " describe(\"User\", [\n" " before_each(fn() { create_user(); AssertionOk }),\n" "\n" " describe(\"authentication\", [\n" " describe(\"with valid credentials\", [\n" " it(\"returns the user\", fn() { ... }),\n" " it(\"sets the session\", fn() { ... }),\n" " ]),\n" " describe(\"with invalid credentials\", [\n" " it(\"returns an error\", fn() { ... }),\n" " ]),\n" " ]),\n" " ])\n" " ```\n" ). -type unit_test() :: {it_test, binary(), fun(() -> dream_test@types:assertion_result())} | {describe_group, binary(), list(unit_test())} | {before_all, fun(() -> dream_test@types:assertion_result())} | {before_each, fun(() -> dream_test@types:assertion_result())} | {after_each, fun(() -> dream_test@types:assertion_result())} | {after_all, fun(() -> dream_test@types:assertion_result())}. -type hook_context() :: {hook_context, list(fun(() -> dream_test@types:assertion_result())), list(fun(() -> dream_test@types:assertion_result()))}. -type suite_hooks() :: {suite_hooks, list(fun(() -> dream_test@types:assertion_result())), list(fun(() -> dream_test@types:assertion_result()))}. -file("src/dream_test/unit.gleam", 177). ?DOC( " Define a single test case.\n" "\n" " The test body is a function that returns an `AssertionResult`. Use the\n" " `should` API to build assertions that produce this result.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " it(\"calculates the sum correctly\", fn() {\n" " add(2, 3)\n" " |> should()\n" " |> equal(5)\n" " |> or_fail_with(\"Expected 2 + 3 to equal 5\")\n" " })\n" " ```\n" "\n" " ## Naming Conventions\n" "\n" " Good test names describe the expected behavior:\n" " - ✓ \"returns the user when credentials are valid\"\n" " - ✓ \"rejects empty passwords\"\n" " - ✗ \"test1\"\n" " - ✗ \"works\"\n" ). -spec it(binary(), fun(() -> dream_test@types:assertion_result())) -> unit_test(). it(Name, Run) -> {it_test, Name, Run}. -file("src/dream_test/unit.gleam", 211). ?DOC( " Group related tests under a common description.\n" "\n" " Groups can be nested to any depth. The group names form a hierarchy that\n" " appears in test output and failure messages.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " describe(\"String utilities\", [\n" " describe(\"trim\", [\n" " it(\"removes leading spaces\", fn() { ... }),\n" " it(\"removes trailing spaces\", fn() { ... }),\n" " ]),\n" " describe(\"split\", [\n" " it(\"splits on delimiter\", fn() { ... }),\n" " ]),\n" " ])\n" " ```\n" "\n" " ## Output\n" "\n" " ```text\n" " String utilities\n" " trim\n" " ✓ removes leading spaces\n" " ✓ removes trailing spaces\n" " split\n" " ✓ splits on delimiter\n" " ```\n" ). -spec describe(binary(), list(unit_test())) -> unit_test(). describe(Name, Children) -> {describe_group, Name, Children}. -file("src/dream_test/unit.gleam", 270). ?DOC( " Run setup once before all tests in the current `describe` block.\n" "\n" " Use `before_all` when you have expensive setup that should happen once\n" " for the entire group rather than before each individual test.\n" "\n" " ## When to Use\n" "\n" " - Starting a database server\n" " - Creating temporary files or directories\n" " - Launching external services\n" " - Any setup that's slow or has side effects you want to share\n" "\n" " ## Execution Behavior\n" "\n" " - Runs exactly once, before the first test in the group\n" " - If it returns `AssertionFailed`, all tests in the group are skipped\n" " and marked as `SetupFailed`\n" " - Nested `describe` blocks each run their own `before_all` hooks\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_all, after_all, to_test_suite}\n" " import dream_test/runner.{run_suite}\n" " import dream_test/types.{AssertionOk}\n" "\n" " describe(\"Database integration\", [\n" " before_all(fn() {\n" " // This runs once before any test\n" " start_test_database()\n" " run_migrations()\n" " AssertionOk\n" " }),\n" "\n" " it(\"creates users\", fn() { ... }),\n" " it(\"queries users\", fn() { ... }),\n" " it(\"updates users\", fn() { ... }),\n" "\n" " after_all(fn() {\n" " stop_test_database()\n" " AssertionOk\n" " }),\n" " ])\n" " |> to_test_suite(\"db_test\")\n" " |> run_suite()\n" " ```\n" "\n" " ## Important: Requires Suite Mode\n" "\n" " `before_all` hooks only work with `to_test_suite` + `run_suite`.\n" " When using `to_test_cases` + `run_all`, they are silently ignored.\n" "\n" " This is because flat mode loses the group structure needed to know\n" " where \"all tests in a group\" begins and ends.\n" ). -spec before_all(fun(() -> dream_test@types:assertion_result())) -> unit_test(). before_all(Setup) -> {before_all, Setup}. -file("src/dream_test/unit.gleam", 346). ?DOC( " Run setup before each test in the current `describe` block.\n" "\n" " Use `before_each` when tests need a fresh, isolated state. This is the\n" " most commonly used lifecycle hook.\n" "\n" " ## When to Use\n" "\n" " - Resetting database state between tests\n" " - Creating fresh test fixtures\n" " - Beginning a transaction to rollback later\n" " - Clearing caches or in-memory state\n" "\n" " ## Execution Behavior\n" "\n" " - Runs before every test in the group and all nested groups\n" " - If it returns `AssertionFailed`, the test is skipped and marked `SetupFailed`\n" " - Multiple `before_each` hooks in the same group run in declaration order\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_each, after_each, to_test_cases}\n" " import dream_test/runner.{run_all}\n" " import dream_test/types.{AssertionOk}\n" "\n" " describe(\"Shopping cart\", [\n" " before_each(fn() {\n" " // Fresh cart for each test\n" " clear_cart()\n" " AssertionOk\n" " }),\n" "\n" " it(\"starts empty\", fn() {\n" " get_cart_items()\n" " |> should()\n" " |> equal([])\n" " |> or_fail_with(\"New cart should be empty\")\n" " }),\n" "\n" " it(\"adds items\", fn() {\n" " add_to_cart(\"apple\")\n" " get_cart_items()\n" " |> should()\n" " |> contain(\"apple\")\n" " |> or_fail_with(\"Cart should contain apple\")\n" " }),\n" " ])\n" " |> to_test_cases(\"cart_test\")\n" " |> run_all()\n" " ```\n" "\n" " ## Hook Inheritance\n" "\n" " Nested `describe` blocks inherit parent `before_each` hooks. Parent hooks\n" " run first (outer-to-inner order):\n" "\n" " ```gleam\n" " describe(\"Outer\", [\n" " before_each(fn() { setup_outer(); AssertionOk }), // Runs 1st\n" "\n" " describe(\"Inner\", [\n" " before_each(fn() { setup_inner(); AssertionOk }), // Runs 2nd\n" " it(\"test\", fn() { ... }),\n" " ]),\n" " ])\n" " ```\n" "\n" " ## Works in Both Modes\n" "\n" " Unlike `before_all`, `before_each` works with both `to_test_cases`\n" " and `to_test_suite`. Use whichever fits your needs.\n" ). -spec before_each(fun(() -> dream_test@types:assertion_result())) -> unit_test(). before_each(Setup) -> {before_each, Setup}. -file("src/dream_test/unit.gleam", 415). ?DOC( " Run teardown after each test in the current `describe` block.\n" "\n" " Use `after_each` to clean up resources created during a test. This hook\n" " runs even if the test fails, ensuring reliable cleanup.\n" "\n" " ## When to Use\n" "\n" " - Rolling back database transactions\n" " - Deleting temporary files created by the test\n" " - Resetting global state or mocks\n" " - Closing connections or releasing resources\n" "\n" " ## Execution Behavior\n" "\n" " - Runs after every test in the group and all nested groups\n" " - **Always runs**, even if the test or `before_each` hooks fail\n" " - Multiple `after_each` hooks in the same group run in reverse declaration order\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_each, after_each, to_test_cases}\n" " import dream_test/runner.{run_all}\n" " import dream_test/types.{AssertionOk}\n" "\n" " describe(\"File operations\", [\n" " before_each(fn() {\n" " create_temp_directory()\n" " AssertionOk\n" " }),\n" "\n" " after_each(fn() {\n" " // Always clean up, even if test crashes\n" " delete_temp_directory()\n" " AssertionOk\n" " }),\n" "\n" " it(\"writes files\", fn() { ... }),\n" " it(\"reads files\", fn() { ... }),\n" " ])\n" " |> to_test_cases(\"file_test\")\n" " |> run_all()\n" " ```\n" "\n" " ## Hook Inheritance\n" "\n" " Nested `describe` blocks inherit parent `after_each` hooks. Child hooks\n" " run first (inner-to-outer order, reverse of `before_each`):\n" "\n" " ```gleam\n" " describe(\"Outer\", [\n" " after_each(fn() { teardown_outer(); AssertionOk }), // Runs 2nd\n" "\n" " describe(\"Inner\", [\n" " after_each(fn() { teardown_inner(); AssertionOk }), // Runs 1st\n" " it(\"test\", fn() { ... }),\n" " ]),\n" " ])\n" " ```\n" "\n" " ## Works in Both Modes\n" "\n" " Like `before_each`, `after_each` works with both `to_test_cases`\n" " and `to_test_suite`.\n" ). -spec after_each(fun(() -> dream_test@types:assertion_result())) -> unit_test(). after_each(Teardown) -> {after_each, Teardown}. -file("src/dream_test/unit.gleam", 493). ?DOC( " Run teardown once after all tests in the current `describe` block.\n" "\n" " Use `after_all` to clean up expensive resources that were set up by\n" " `before_all`. This hook runs once after all tests complete, regardless\n" " of whether tests passed or failed.\n" "\n" " ## When to Use\n" "\n" " - Stopping a database server started by `before_all`\n" " - Removing temporary directories\n" " - Shutting down external services\n" " - Any cleanup that corresponds to `before_all` setup\n" "\n" " ## Execution Behavior\n" "\n" " - Runs exactly once, after the last test in the group completes\n" " - **Always runs**, even if tests fail or `before_all` fails\n" " - Nested `describe` blocks each run their own `after_all` hooks\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_all, after_all, to_test_suite}\n" " import dream_test/runner.{run_suite}\n" " import dream_test/types.{AssertionOk}\n" "\n" " describe(\"External API integration\", [\n" " before_all(fn() {\n" " start_mock_server(port: 8080)\n" " AssertionOk\n" " }),\n" "\n" " it(\"fetches users\", fn() { ... }),\n" " it(\"creates users\", fn() { ... }),\n" " it(\"handles errors\", fn() { ... }),\n" "\n" " after_all(fn() {\n" " // Clean up even if tests failed\n" " stop_mock_server()\n" " AssertionOk\n" " }),\n" " ])\n" " |> to_test_suite(\"api_test\")\n" " |> run_suite()\n" " ```\n" "\n" " ## Complete Lifecycle Example\n" "\n" " Here's a complete example showing all four hooks working together:\n" "\n" " ```gleam\n" " describe(\"Database tests\", [\n" " // Once at start: start the database\n" " before_all(fn() { start_db(); AssertionOk }),\n" "\n" " // Before each test: begin a transaction\n" " before_each(fn() { begin_transaction(); AssertionOk }),\n" "\n" " it(\"creates records\", fn() { ... }),\n" " it(\"queries records\", fn() { ... }),\n" "\n" " // After each test: rollback the transaction\n" " after_each(fn() { rollback_transaction(); AssertionOk }),\n" "\n" " // Once at end: stop the database\n" " after_all(fn() { stop_db(); AssertionOk }),\n" " ])\n" " ```\n" "\n" " ## Important: Requires Suite Mode\n" "\n" " `after_all` hooks only work with `to_test_suite` + `run_suite`.\n" " When using `to_test_cases` + `run_all`, they are silently ignored.\n" ). -spec after_all(fun(() -> dream_test@types:assertion_result())) -> unit_test(). after_all(Teardown) -> {after_all, Teardown}. -file("src/dream_test/unit.gleam", 511). -spec empty_hook_context() -> hook_context(). empty_hook_context() -> {hook_context, [], []}. -file("src/dream_test/unit.gleam", 609). -spec collect_hook_from_node(unit_test(), hook_context()) -> hook_context(). collect_hook_from_node(Node, Context) -> case Node of {before_each, Setup} -> Hooks = lists:append(erlang:element(2, Context), [Setup]), {hook_context, Hooks, erlang:element(3, Context)}; {after_each, Teardown} -> {hook_context, erlang:element(2, Context), [Teardown | erlang:element(3, Context)]}; _ -> Context end. -file("src/dream_test/unit.gleam", 596). -spec collect_hooks_from_list(list(unit_test()), hook_context()) -> hook_context(). collect_hooks_from_list(Remaining, Context) -> case Remaining of [] -> Context; [Head | Tail] -> Updated = collect_hook_from_node(Head, Context), collect_hooks_from_list(Tail, Updated) end. -file("src/dream_test/unit.gleam", 589). ?DOC(" Collect hooks from a list of children and merge with inherited hooks.\n"). -spec collect_hooks_from_children(list(unit_test()), hook_context()) -> hook_context(). collect_hooks_from_children(Children, Inherited) -> collect_hooks_from_list(Children, Inherited). -file("src/dream_test/unit.gleam", 628). -spec build_it_test_case( list(binary()), binary(), fun(() -> dream_test@types:assertion_result()), hook_context(), list(dream_test@types:test_case()) ) -> list(dream_test@types:test_case()). build_it_test_case(Name_prefix, Name, Run, Hook_context, Accumulated) -> Full_name = lists:append(Name_prefix, [Name]), Config = {single_test_config, Name, Full_name, [], unit, Run, none, erlang:element(2, Hook_context), erlang:element(3, Hook_context)}, Test_case = {test_case, Config}, [Test_case | Accumulated]. -file("src/dream_test/unit.gleam", 871). -spec collect_suite_hook(unit_test(), suite_hooks()) -> suite_hooks(). collect_suite_hook(Node, Hooks) -> case Node of {before_all, Setup} -> {suite_hooks, [Setup | erlang:element(2, Hooks)], erlang:element(3, Hooks)}; {after_all, Teardown} -> {suite_hooks, erlang:element(2, Hooks), [Teardown | erlang:element(3, Hooks)]}; _ -> Hooks end. -file("src/dream_test/unit.gleam", 854). -spec collect_suite_hooks_from_list(list(unit_test()), suite_hooks()) -> suite_hooks(). collect_suite_hooks_from_list(Remaining, Hooks) -> case Remaining of [] -> {suite_hooks, lists:reverse(erlang:element(2, Hooks)), lists:reverse(erlang:element(3, Hooks))}; [Head | Tail] -> Updated = collect_suite_hook(Head, Hooks), collect_suite_hooks_from_list(Tail, Updated) end. -file("src/dream_test/unit.gleam", 847). -spec collect_suite_hooks(list(unit_test())) -> suite_hooks(). collect_suite_hooks(Children) -> collect_suite_hooks_from_list(Children, {suite_hooks, [], []}). -file("src/dream_test/unit.gleam", 929). -spec build_single_test_case( list(binary()), binary(), fun(() -> dream_test@types:assertion_result()), hook_context() ) -> dream_test@types:test_case(). build_single_test_case(Full_name, Name, Run, Hook_context) -> Config = {single_test_config, Name, Full_name, [], unit, Run, none, erlang:element(2, Hook_context), erlang:element(3, Hook_context)}, {test_case, Config}. -file("src/dream_test/unit.gleam", 949). -spec extract_last_name(list(binary())) -> binary(). extract_last_name(Full_name) -> case lists:reverse(Full_name) of [Last | _] -> Last; [] -> <<""/utf8>> end. -file("src/dream_test/unit.gleam", 899). -spec build_suite_item(binary(), list(binary()), hook_context(), unit_test()) -> list(dream_test@types:test_suite_item()). build_suite_item(Module_name, Name_prefix, Hook_context, Node) -> case Node of {it_test, Name, Run} -> Full_name = lists:append(Name_prefix, [Name]), Test_case = build_single_test_case( Full_name, Name, Run, Hook_context ), [{suite_test, Test_case}]; {describe_group, Name@1, Children} -> Full_name@1 = lists:append(Name_prefix, [Name@1]), Nested_suite = build_suite_from_describe( Module_name, Full_name@1, Hook_context, Children ), [{suite_group, Nested_suite}]; {before_all, _} -> []; {before_each, _} -> []; {after_each, _} -> []; {after_all, _} -> [] end. -file("src/dream_test/unit.gleam", 813). -spec build_suite_from_describe( binary(), list(binary()), hook_context(), list(unit_test()) ) -> dream_test@types:test_suite(). build_suite_from_describe(Module_name, Full_name, Inherited_hooks, Children) -> Suite_hooks = collect_suite_hooks(Children), Group_hooks = collect_hooks_from_children(Children, Inherited_hooks), Items = build_suite_items(Module_name, Full_name, Group_hooks, Children, []), Name = extract_last_name(Full_name), {test_suite, Name, Full_name, erlang:element(2, Suite_hooks), erlang:element(3, Suite_hooks), Items}. -file("src/dream_test/unit.gleam", 881). -spec build_suite_items( binary(), list(binary()), hook_context(), list(unit_test()), list(dream_test@types:test_suite_item()) ) -> list(dream_test@types:test_suite_item()). build_suite_items( Module_name, Name_prefix, Hook_context, Remaining, Accumulated ) -> case Remaining of [] -> lists:reverse(Accumulated); [Head | Tail] -> New_items = build_suite_item( Module_name, Name_prefix, Hook_context, Head ), Updated = lists:append(lists:reverse(New_items), Accumulated), build_suite_items( Module_name, Name_prefix, Hook_context, Tail, Updated ) end. -file("src/dream_test/unit.gleam", 770). -spec to_suite_from_unit_test( binary(), list(binary()), hook_context(), unit_test() ) -> dream_test@types:test_suite(). to_suite_from_unit_test(Module_name, Name_prefix, Inherited_hooks, Node) -> case Node of {describe_group, Name, Children} -> Full_name = lists:append(Name_prefix, [Name]), build_suite_from_describe( Module_name, Full_name, Inherited_hooks, Children ); {it_test, Name@1, Run} -> Full_name@1 = lists:append(Name_prefix, [Name@1]), Test_case = build_single_test_case( Full_name@1, Name@1, Run, Inherited_hooks ), {test_suite, Module_name, [Module_name], [], [], [{suite_test, Test_case}]}; {before_all, _} -> {test_suite, Module_name, [Module_name], [], [], []}; {before_each, _} -> {test_suite, Module_name, [Module_name], [], [], []}; {after_each, _} -> {test_suite, Module_name, [Module_name], [], [], []}; {after_all, _} -> {test_suite, Module_name, [Module_name], [], [], []} end. -file("src/dream_test/unit.gleam", 765). ?DOC( " Convert a test tree into a structured test suite.\n" "\n" " Use `to_test_suite` when you need `before_all` or `after_all` hooks.\n" " Unlike `to_test_cases`, this preserves the group hierarchy required\n" " for once-per-group semantics.\n" "\n" " ## When to Use Each Mode\n" "\n" " | Scenario | Function | Runner |\n" " |---------------------------------------------|------------------|--------------|\n" " | Simple tests, no hooks | `to_test_cases` | `run_all` |\n" " | Only `before_each`/`after_each` | `to_test_cases` | `run_all` |\n" " | Need `before_all` or `after_all` | `to_test_suite` | `run_suite` |\n" " | Expensive setup shared across tests | `to_test_suite` | `run_suite` |\n" "\n" " ## How It Works\n" "\n" " ```text\n" " describe(\"A\", [ TestSuite(\"A\")\n" " before_all(setup), → before_all: [setup]\n" " it(\"test1\", ...), items: [\n" " describe(\"B\", [ SuiteTest(test1),\n" " it(\"test2\", ...), SuiteGroup(TestSuite(\"B\", ...))\n" " ]), ]\n" " ])\n" " ```\n" "\n" " The tree structure is preserved, allowing the runner to execute\n" " `before_all` before entering a group and `after_all` after leaving.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/unit.{describe, it, before_all, after_all, to_test_suite}\n" " import dream_test/runner.{run_suite}\n" " import dream_test/reporter/bdd.{report}\n" " import dream_test/types.{AssertionOk}\n" " import gleam/io\n" "\n" " pub fn main() {\n" " tests()\n" " |> to_test_suite(\"integration_test\")\n" " |> run_suite()\n" " |> report(io.print)\n" " }\n" "\n" " pub fn tests() {\n" " describe(\"Payment processing\", [\n" " before_all(fn() {\n" " start_payment_gateway_mock()\n" " AssertionOk\n" " }),\n" "\n" " describe(\"successful payments\", [\n" " it(\"processes credit cards\", fn() { ... }),\n" " it(\"processes debit cards\", fn() { ... }),\n" " ]),\n" "\n" " describe(\"failed payments\", [\n" " it(\"handles declined cards\", fn() { ... }),\n" " it(\"handles network errors\", fn() { ... }),\n" " ]),\n" "\n" " after_all(fn() {\n" " stop_payment_gateway_mock()\n" " AssertionOk\n" " }),\n" " ])\n" " }\n" " ```\n" "\n" " ## Parameters\n" "\n" " - `module_name` - Name of the test module (appears in output)\n" " - `root` - The root `UnitTest` node (typically from `describe`)\n" "\n" " ## Returns\n" "\n" " A `TestSuite` that can be executed with `run_suite` or `run_suite_with_config`.\n" ). -spec to_test_suite(binary(), unit_test()) -> dream_test@types:test_suite(). to_test_suite(Module_name, Root) -> Context = empty_hook_context(), to_suite_from_unit_test(Module_name, [], Context, Root). -file("src/dream_test/unit.gleam", 651). -spec to_test_cases_from_list( binary(), list(binary()), hook_context(), list(unit_test()), list(dream_test@types:test_case()) ) -> list(dream_test@types:test_case()). to_test_cases_from_list( Module_name, Name_prefix, Hook_context, Remaining, Accumulated ) -> case Remaining of [] -> lists:reverse(Accumulated); [Head | Tail] -> Updated = to_test_cases_from_unit_test( Module_name, Name_prefix, Hook_context, Head, Accumulated ), to_test_cases_from_list( Module_name, Name_prefix, Hook_context, Tail, Updated ) end. -file("src/dream_test/unit.gleam", 555). -spec to_test_cases_from_unit_test( binary(), list(binary()), hook_context(), unit_test(), list(dream_test@types:test_case()) ) -> list(dream_test@types:test_case()). to_test_cases_from_unit_test( Module_name, Name_prefix, Hook_context, Node, Accumulated ) -> case Node of {it_test, Name, Run} -> build_it_test_case( Name_prefix, Name, Run, Hook_context, Accumulated ); {describe_group, Name@1, Children} -> New_prefix = lists:append(Name_prefix, [Name@1]), Group_hooks = collect_hooks_from_children(Children, Hook_context), to_test_cases_from_list( Module_name, New_prefix, Group_hooks, Children, Accumulated ); {before_all, _} -> Accumulated; {after_all, _} -> Accumulated; {before_each, _} -> Accumulated; {after_each, _} -> Accumulated end. -file("src/dream_test/unit.gleam", 550). ?DOC( " Convert a test tree into a flat list of runnable test cases.\n" "\n" " This function walks the `UnitTest` tree and produces `TestCase` values\n" " that the runner can execute. Each test case includes:\n" "\n" " - `name` - The test's own name (from `it`)\n" " - `full_name` - The complete path including all `describe` ancestors\n" " - `tags` - Currently empty (tag support coming soon)\n" " - `kind` - Set to `Unit` for all tests from this DSL\n" " - `before_each_hooks` - Inherited hooks to run before the test\n" " - `after_each_hooks` - Inherited hooks to run after the test\n" "\n" " ## Hook Handling\n" "\n" " - `before_each`/`after_each` hooks are collected and attached to each test\n" " - `before_all`/`after_all` hooks are ignored (use `to_test_suite` instead)\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let test_cases =\n" " describe(\"Math\", [\n" " it(\"adds\", fn() { ... }),\n" " it(\"subtracts\", fn() { ... }),\n" " ])\n" " |> to_test_cases(\"math_test\")\n" "\n" " // test_cases is now a List(TestCase) ready for run_all()\n" " ```\n" "\n" " ## Parameters\n" "\n" " - `module_name` - The name of the test module (used for identification)\n" " - `root` - The root `UnitTest` node (typically from `describe`)\n" ). -spec to_test_cases(binary(), unit_test()) -> list(dream_test@types:test_case()). to_test_cases(Module_name, Root) -> Context = empty_hook_context(), to_test_cases_from_unit_test(Module_name, [], Context, Root, []).