-module(dream_test@unit_context). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream_test/unit_context.gleam"). -export([describe/3, group/2, it/2, before_all/1, before_each/1, after_each/1, after_all/1, with_tags/2, skip/2]). -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( " Context-aware unit DSL (`unit_context`).\n" "\n" " Use this module when you want hooks and tests to operate on a shared,\n" " strongly-typed context value (your own record/union).\n" "\n" " - You provide an initial context value to `describe`.\n" " - `before_all` / `before_each` can transform the context.\n" " - `it` receives the current context.\n" "\n" " This builds a `dream_test/types.TestSuite(context)` under the hood.\n" "\n" " ## When should I use this?\n" "\n" " - Use `dream_test/unit` for most tests (no explicit context).\n" " - Use `dream_test/unit_context` when you want an explicit context value\n" " passed into every test (database handles, fixtures, counters, etc.).\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/matchers.{be_equal, or_fail_with, should}\n" " import dream_test/reporters/bdd\n" " import dream_test/reporters/progress\n" " import dream_test/runner\n" " import dream_test/unit_context.{before_each, describe, it}\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " fn increment_counter(context: Context) {\n" " Ok(Context(counter: context.counter + 1))\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Context-aware suite\", Context(counter: 0), [\n" " before_each(increment_counter),\n" " it(\"receives the updated context\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(1)\n" " |> or_fail_with(\"expected counter to be 1 after before_each\")\n" " }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -file("src/dream_test/unit_context.gleam", 115). ?DOC( " Create a top-level context-aware suite with an explicit initial context.\n" "\n" " ## Parameters\n" "\n" " - `name`: Display name for the suite (shown in reports)\n" " - `seed`: Initial context value passed into hooks and tests\n" " - `children`: Tests, groups, and hooks that make up the suite\n" "\n" " ## Returns\n" "\n" " A `TestSuite(context)` you can run with `dream_test/runner`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/matchers.{be_equal, or_fail_with, should}\n" " import dream_test/reporters/bdd\n" " import dream_test/reporters/progress\n" " import dream_test/runner\n" " import dream_test/unit_context.{before_each, describe, it}\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " fn increment_counter(context: Context) {\n" " Ok(Context(counter: context.counter + 1))\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Context-aware suite\", Context(counter: 0), [\n" " before_each(increment_counter),\n" " it(\"receives the updated context\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(1)\n" " |> or_fail_with(\"expected counter to be 1 after before_each\")\n" " }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -spec describe(binary(), LHA, list(dream_test@types:node_(LHA))) -> dream_test@types:root(LHA). describe(Name, Seed, Children) -> {root, Seed, {group, Name, [], Children}}. -file("src/dream_test/unit_context.gleam", 183). ?DOC( " Create a nested group inside a context-aware suite.\n" "\n" " ## Parameters\n" "\n" " - `name`: Display name for the group (shown in reports)\n" " - `children`: Tests and hooks that belong to this group\n" "\n" " ## Returns\n" "\n" " A `ContextNode(context)` you can include in `describe`/`group`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/matchers.{be_equal, or_fail_with, should}\n" " import dream_test/reporters/bdd\n" " import dream_test/reporters/progress\n" " import dream_test/runner\n" " import dream_test/unit_context.{before_each, describe, group, it}\n" " import gleam/io\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " fn increment_counter(context: Context) {\n" " Ok(Context(counter: context.counter + 1))\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Context-aware grouping\", Context(counter: 0), [\n" " // This outer hook applies everywhere under this describe, including groups.\n" " before_each(increment_counter),\n" " group(\"inner group\", [\n" " // This hook only applies to tests inside this group.\n" " before_each(increment_counter),\n" " it(\"sees both outer + inner hooks\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(2)\n" " |> or_fail_with(\"expected counter to be 2 (outer + inner before_each)\")\n" " }),\n" " ]),\n" " it(\"sees only outer hook\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(1)\n" " |> or_fail_with(\"expected counter to be 1 (outer before_each only)\")\n" " }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -spec group(binary(), list(dream_test@types:node_(LHE))) -> dream_test@types:node_(LHE). group(Name, Children) -> {group, Name, [], Children}. -file("src/dream_test/unit_context.gleam", 242). ?DOC( " Define a context-aware test case.\n" "\n" " The test body receives the current context and returns:\n" " `Result(AssertionResult, String)`.\n" "\n" " ## Parameters\n" "\n" " - `name`: Display name for the test (shown in reports)\n" " - `run`: Test body function that receives the current context\n" "\n" " ## Returns\n" "\n" " A `ContextNode(context)` you can include in `describe`/`group`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/matchers.{be_equal, or_fail_with, should}\n" " import dream_test/reporters/bdd\n" " import dream_test/reporters/progress\n" " import dream_test/runner\n" " import dream_test/unit_context.{before_each, describe, it}\n" " import gleam/io\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " fn increment_counter(context: Context) {\n" " Ok(Context(counter: context.counter + 1))\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Context-aware suite\", Context(counter: 0), [\n" " before_each(increment_counter),\n" " it(\"receives the updated context\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(1)\n" " |> or_fail_with(\"expected counter to be 1 after before_each\")\n" " }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -spec it( binary(), fun((LHI) -> {ok, dream_test@types:assertion_result()} | {error, binary()}) ) -> dream_test@types:node_(LHI). it(Name, Run) -> {test, Name, [], unit, Run, none, none}. -file("src/dream_test/unit_context.gleam", 325). ?DOC(" Run once before any tests and produce/transform the context.\n"). -spec before_all(fun((LHQ) -> {ok, LHQ} | {error, binary()})) -> dream_test@types:node_(LHQ). before_all(Setup) -> {before_all, Setup}. -file("src/dream_test/unit_context.gleam", 332). ?DOC(" Run before each test and transform the context for that test.\n"). -spec before_each(fun((LHU) -> {ok, LHU} | {error, binary()})) -> dream_test@types:node_(LHU). before_each(Setup) -> {before_each, Setup}. -file("src/dream_test/unit_context.gleam", 339). ?DOC(" Run after each test for cleanup.\n"). -spec after_each(fun((LHY) -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(LHY). after_each(Teardown) -> {after_each, Teardown}. -file("src/dream_test/unit_context.gleam", 346). ?DOC(" Run once after all tests for cleanup.\n"). -spec after_all(fun((LIC) -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(LIC). after_all(Teardown) -> {after_all, Teardown}. -file("src/dream_test/unit_context.gleam", 400). ?DOC( " Attach tags to a node.\n" "\n" " Tags can be used by runners and reporters for filtering and display.\n" " Prefer piping: `node |> with_tags([\"tag\"])`.\n" "\n" " ## Parameters\n" "\n" " - `node`: A group or test node to tag\n" " - `tags`: Tags to attach (replaces any existing tags on that node)\n" "\n" " ## Returns\n" "\n" " A new node with the provided tags.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream_test/matchers.{succeed}\n" " import dream_test/reporters/bdd\n" " import dream_test/reporters/progress\n" " import dream_test/runner\n" " import dream_test/unit_context.{describe, group, it, with_tags}\n" " import gleam/io\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Tagging context-aware tests\", Context(counter: 0), [\n" " group(\"group tagged slow\", [\n" " it(\"inherits the group tag\", fn(_context: Context) { Ok(succeed()) }),\n" " ])\n" " |> with_tags([\"slow\"]),\n" " it(\"can tag an individual test\", fn(_context: Context) { Ok(succeed()) })\n" " |> with_tags([\"unit_context\", \"fast\"]),\n" " it(\"untagged tests still work\", fn(_context: Context) { Ok(succeed()) }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -spec with_tags(dream_test@types:node_(LIG), list(binary())) -> dream_test@types:node_(LIG). with_tags(Node, Tags) -> case Node of {group, Name, _, Children} -> {group, Name, Tags, Children}; {test, Name@1, _, Kind, Run, Timeout_ms, Source} -> {test, Name@1, Tags, Kind, Run, Timeout_ms, Source}; Other -> Other end. -file("src/dream_test/unit_context.gleam", 420). -spec skipped_test_run(any()) -> {ok, dream_test@types:assertion_result()} | {error, binary()}. skipped_test_run(_) -> {ok, assertion_skipped}. -file("src/dream_test/unit_context.gleam", 310). ?DOC( " Define a skipped context-aware test.\n" "\n" " `skip` has the same shape as `it` so you can easily switch a test between\n" " running and skipped without rewriting the test body.\n" "\n" " The provided test body is preserved for that purpose, but it is **not\n" " executed** while the test is skipped.\n" "\n" " ## Parameters\n" "\n" " - `name`: Display name for the skipped test (shown in reports)\n" " - `run`: Unused; kept so you can switch between `it` and `skip` easily\n" "\n" " ## Returns\n" "\n" " A `ContextNode(context)` that always results in `AssertionSkipped`.\n" "\n" " ## Example\n" "\n" " ```gleam\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 dream_test/unit_context.{describe, it, skip}\n" " import gleam/io\n" "\n" " pub type Context {\n" " Context(counter: Int)\n" " }\n" "\n" " pub fn suite() {\n" " describe(\"Skipping context-aware tests\", Context(counter: 0), [\n" " skip(\"this test is skipped\", fn(_context: Context) {\n" " // This would pass if it ran, but Dream Test will mark it skipped.\n" " Ok(succeed())\n" " }),\n" " it(\"normal tests still run\", fn(context: Context) {\n" " context.counter\n" " |> should\n" " |> be_equal(0)\n" " |> or_fail_with(\"expected counter to start at 0\")\n" " }),\n" " ])\n" " }\n" "\n" " pub fn main() {\n" " runner.new([suite()])\n" " |> runner.progress_reporter(progress.new())\n" " |> runner.results_reporters([bdd.new()])\n" " |> runner.exit_on_failure()\n" " |> runner.run()\n" " }\n" " ```\n" ). -spec skip( binary(), fun((LHM) -> {ok, dream_test@types:assertion_result()} | {error, binary()}) ) -> dream_test@types:node_(LHM). skip(Name, _) -> {test, Name, [], unit, fun skipped_test_run/1, none, none}.