-module(dream_test@reporters@bdd). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream_test/reporters/bdd.gleam"). -export([new/0, color/1, summary_only/1, failures_only/1, format_summary_only/1, format_incremental_parts_with_test_indent/3, format_incremental/2, format_incremental_with_test_indent/3, render/2, format/1, report/2]). -export_type([format_incremental_result/0, format_incremental_parts_result/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( " BDD-style test report formatting.\n" "\n" " This module formats `TestResult` values in a hierarchical, spec-like layout\n" " that mirrors your `describe` / `it` structure. It’s primarily used by\n" " the end-of-run BDD results reporter (`runner.results_reporters([bdd.new()])`),\n" " but it’s also useful directly when you want formatted output as a `String`\n" " (e.g. snapshot tests) or when you’re formatting results incrementally in a\n" " custom runner.\n" ). -type format_incremental_result() :: {format_incremental_result, binary(), list(binary())}. -type format_incremental_parts_result() :: {format_incremental_parts_result, binary(), binary(), list(binary())}. -file("src/dream_test/reporters/bdd.gleam", 60). ?DOC( " Create a BDD results reporter.\n" "\n" " This reporter prints at the end of the run, using the traversal-ordered results\n" " provided by the runner.\n" ). -spec new() -> dream_test@reporters@types:results_reporter(). new() -> {bdd, {bdd_reporter_config, false, bdd_full}}. -file("src/dream_test/reporters/bdd.gleam", 68). ?DOC(" Enable ANSI color output for the BDD report.\n"). -spec color(dream_test@reporters@types:results_reporter()) -> dream_test@reporters@types:results_reporter(). color(Reporter) -> case Reporter of {bdd, {bdd_reporter_config, _, Mode}} -> {bdd, {bdd_reporter_config, true, Mode}}; Other -> Other end. -file("src/dream_test/reporters/bdd.gleam", 82). ?DOC(" Print only the summary line at the end of the run.\n"). -spec summary_only(dream_test@reporters@types:results_reporter()) -> dream_test@reporters@types:results_reporter(). summary_only(Reporter) -> case Reporter of {bdd, {bdd_reporter_config, Color, _}} -> {bdd, {bdd_reporter_config, Color, bdd_summary_only}}; Other -> Other end. -file("src/dream_test/reporters/bdd.gleam", 96). ?DOC(" Print only repeated failures and the summary line at the end of the run.\n"). -spec failures_only(dream_test@reporters@types:results_reporter()) -> dream_test@reporters@types:results_reporter(). failures_only(Reporter) -> case Reporter of {bdd, {bdd_reporter_config, Color, _}} -> {bdd, {bdd_reporter_config, Color, bdd_failures_only}}; Other -> Other end. -file("src/dream_test/reporters/bdd.gleam", 163). -spec is_failure_status(dream_test@types:status()) -> boolean(). is_failure_status(Status) -> case Status of failed -> true; setup_failed -> true; timed_out -> true; _ -> false end. -file("src/dream_test/reporters/bdd.gleam", 148). -spec filter_failures( list(dream_test@types:test_result()), list(dream_test@types:test_result()) ) -> list(dream_test@types:test_result()). filter_failures(Results, Acc_rev) -> case Results of [] -> lists:reverse(Acc_rev); [Result | Rest] -> case is_failure_status(erlang:element(4, Result)) of true -> filter_failures(Rest, [Result | Acc_rev]); false -> filter_failures(Rest, Acc_rev) end end. -file("src/dream_test/reporters/bdd.gleam", 172). -spec ansi_wrap(binary(), binary()) -> binary(). ansi_wrap(Code, Text) -> erlang:list_to_binary( [<<"\x{1b}["/utf8>>, Code, <<"m"/utf8>>, Text, <<"\x{1b}[0m"/utf8>>] ). -file("src/dream_test/reporters/bdd.gleam", 176). -spec ansi_dim(binary()) -> binary(). ansi_dim(Text) -> ansi_wrap(<<"2"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 180). -spec ansi_red(binary()) -> binary(). ansi_red(Text) -> ansi_wrap(<<"31"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 184). -spec ansi_green(binary()) -> binary(). ansi_green(Text) -> ansi_wrap(<<"32"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 188). -spec ansi_yellow(binary()) -> binary(). ansi_yellow(Text) -> ansi_wrap(<<"33"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 192). -spec ansi_cyan(binary()) -> binary(). ansi_cyan(Text) -> ansi_wrap(<<"36"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 196). -spec ansi_magenta(binary()) -> binary(). ansi_magenta(Text) -> ansi_wrap(<<"35"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 200). -spec ansi_bold(binary()) -> binary(). ansi_bold(Text) -> ansi_wrap(<<"1"/utf8>>, Text). -file("src/dream_test/reporters/bdd.gleam", 204). -spec 'maybe'(boolean(), fun((binary()) -> binary()), binary()) -> binary(). 'maybe'(Color, Apply, Text) -> case Color of true -> Apply(Text); false -> Text end. -file("src/dream_test/reporters/bdd.gleam", 211). -spec format_failures_header(boolean()) -> binary(). format_failures_header(Color) -> 'maybe'(Color, fun(T) -> ansi_bold(ansi_red(T)) end, <<"Failures:"/utf8>>). -file("src/dream_test/reporters/bdd.gleam", 259). -spec partition_by_kind(list(dream_test@types:test_result())) -> {list(dream_test@types:test_result()), list(dream_test@types:test_result())}. partition_by_kind(Results) -> gleam@list:partition( Results, fun dream_test@reporters@gherkin:is_gherkin_result/1 ). -file("src/dream_test/reporters/bdd.gleam", 281). -spec compare_string_lists(list(binary()), list(binary())) -> gleam@order:order(). compare_string_lists(A, B) -> case {A, B} of {[], []} -> eq; {[], _} -> lt; {_, []} -> gt; {[Head_a | Rest_a], [Head_b | Rest_b]} -> case gleam@string:compare(Head_a, Head_b) of eq -> compare_string_lists(Rest_a, Rest_b); Other -> Other end end. -file("src/dream_test/reporters/bdd.gleam", 277). -spec compare_by_full_name( dream_test@types:test_result(), dream_test@types:test_result() ) -> gleam@order:order(). compare_by_full_name(A, B) -> compare_string_lists(erlang:element(3, A), erlang:element(3, B)). -file("src/dream_test/reporters/bdd.gleam", 312). -spec remove_gherkin_summary(list(binary()), list(binary())) -> list(binary()). remove_gherkin_summary(Lines, Accumulated) -> case Lines of [] -> lists:reverse(Accumulated); [Line] -> case gleam_stdlib:string_starts_with(Line, <<"Summary:"/utf8>>) orelse (Line =:= <<""/utf8>>) of true -> lists:reverse(Accumulated); false -> lists:reverse([Line | Accumulated]) end; [Line@1 | Rest] -> case gleam_stdlib:string_starts_with(Line@1, <<"Summary:"/utf8>>) of true -> remove_gherkin_summary(Rest, Accumulated); false -> remove_gherkin_summary(Rest, [Line@1 | Accumulated]) end end. -file("src/dream_test/reporters/bdd.gleam", 305). -spec remove_summary_line(binary()) -> binary(). remove_summary_line(Text) -> Lines = gleam@string:split(Text, <<"\n"/utf8>>), Without_summary = remove_gherkin_summary(Lines, []), gleam@string:join(Without_summary, <<"\n"/utf8>>). -file("src/dream_test/reporters/bdd.gleam", 294). -spec format_gherkin_results(list(dream_test@types:test_result())) -> binary(). format_gherkin_results(Results) -> case Results of [] -> <<""/utf8>>; _ -> Formatted = dream_test@reporters@gherkin:format(Results), remove_summary_line(Formatted) end. -file("src/dream_test/reporters/bdd.gleam", 664). -spec extract_describe_segments(list(binary())) -> list(binary()). extract_describe_segments(Full_name) -> case lists:reverse(Full_name) of [] -> []; [_] -> []; [_ | Rest] -> lists:reverse(Rest) end. -file("src/dream_test/reporters/bdd.gleam", 700). -spec format_duration(integer()) -> binary(). format_duration(Duration_ms) -> case Duration_ms of Ms when Ms =< 0 -> <<""/utf8>>; Ms@1 -> <<<<" ("/utf8, (dream_test@timing:format_duration_ms(Ms@1))/binary>>/binary, ")"/utf8>> end. -file("src/dream_test/reporters/bdd.gleam", 708). -spec calculate_test_depth(list(binary())) -> integer(). calculate_test_depth(Full_name) -> case Full_name of [] -> 0; [_] -> 0; _ -> erlang:length(Full_name) - 1 end. -file("src/dream_test/reporters/bdd.gleam", 720). -spec build_indent_recursive(integer(), binary()) -> binary(). build_indent_recursive(Level, Accumulated) -> case Level of 0 -> Accumulated; N -> build_indent_recursive( N - 1, erlang:list_to_binary([Accumulated, <<" "/utf8>>]) ) end. -file("src/dream_test/reporters/bdd.gleam", 716). -spec build_indent(integer()) -> binary(). build_indent(Level) -> build_indent_recursive(Level, <<""/utf8>>). -file("src/dream_test/reporters/bdd.gleam", 604). -spec format_header_segments_for_kind( boolean(), list(binary()), integer(), boolean(), binary() ) -> binary(). format_header_segments_for_kind(Color, Segments, Depth, Is_gherkin, Accumulated) -> case Segments of [] -> Accumulated; [Segment | Rest] -> Indent = build_indent(Depth), Label = case Is_gherkin andalso (Depth =:= 0) of true -> <<"Feature: "/utf8, Segment/binary>>; false -> Segment end, Styled = 'maybe'( Color, fun(T) -> ansi_bold(ansi_cyan(T)) end, Label ), Header = erlang:list_to_binary([Indent, Styled, <<"\n"/utf8>>]), Updated = erlang:list_to_binary([Accumulated, Header]), format_header_segments_for_kind( Color, Rest, Depth + 1, Is_gherkin, Updated ) end. -file("src/dream_test/reporters/bdd.gleam", 727). -spec extract_test_name(list(binary())) -> binary(). extract_test_name(Full_name) -> case lists:reverse(Full_name) of [Last | _] -> Last; [] -> <<""/utf8>> end. -file("src/dream_test/reporters/bdd.gleam", 734). -spec status_marker(boolean(), dream_test@types:status()) -> binary(). status_marker(Color, Status) -> case Status of passed -> 'maybe'(Color, fun ansi_green/1, <<"✓"/utf8>>); failed -> 'maybe'(Color, fun ansi_red/1, <<"✗"/utf8>>); skipped -> 'maybe'(Color, fun ansi_yellow/1, <<"-"/utf8>>); pending -> 'maybe'(Color, fun ansi_yellow/1, <<"~"/utf8>>); timed_out -> 'maybe'(Color, fun ansi_red/1, <<"!"/utf8>>); setup_failed -> 'maybe'(Color, fun ansi_red/1, <<"⚠"/utf8>>) end. -file("src/dream_test/reporters/bdd.gleam", 790). -spec format_failure_message(boolean(), binary(), binary()) -> binary(). format_failure_message(Color, Message, Base_indent) -> case Message of <<""/utf8>> -> <<""/utf8>>; _ -> Label = 'maybe'(Color, fun ansi_dim/1, <<" Message: "/utf8>>), Msg = 'maybe'(Color, fun ansi_red/1, Message), erlang:list_to_binary([Base_indent, Label, Msg, <<"\n"/utf8>>]) end. -file("src/dream_test/reporters/bdd.gleam", 898). -spec format_expected_actual(boolean(), binary(), binary(), binary()) -> binary(). format_expected_actual(Color, Base_indent, Expected, Actual) -> Expected_label = 'maybe'(Color, fun ansi_dim/1, <<" Expected: "/utf8>>), Actual_label = 'maybe'(Color, fun ansi_dim/1, <<" Actual: "/utf8>>), erlang:list_to_binary( [Base_indent, Expected_label, 'maybe'(Color, fun ansi_green/1, Expected), <<"\n"/utf8>>, Base_indent, Actual_label, 'maybe'(Color, fun ansi_red/1, Actual), <<"\n"/utf8>>] ). -file("src/dream_test/reporters/bdd.gleam", 918). -spec format_snapshot_failure( boolean(), binary(), binary(), binary(), boolean(), binary() ) -> binary(). format_snapshot_failure( Color, Actual, Expected, Snapshot_path, Is_missing, Base_indent ) -> case Is_missing of true -> erlang:list_to_binary( [Base_indent, 'maybe'( Color, fun ansi_dim/1, <<" Snapshot missing: "/utf8>> ), 'maybe'(Color, fun ansi_yellow/1, Snapshot_path), <<"\n"/utf8>>] ); false -> erlang:list_to_binary( [Base_indent, 'maybe'(Color, fun ansi_dim/1, <<" Snapshot: "/utf8>>), 'maybe'(Color, fun ansi_cyan/1, Snapshot_path), <<"\n"/utf8>>, format_expected_actual(Color, Base_indent, Expected, Actual)] ) end. -file("src/dream_test/reporters/bdd.gleam", 805). -spec format_failure_payload( boolean(), gleam@option:option(dream_test@types:failure_payload()), binary() ) -> binary(). format_failure_payload(Color, Payload, Base_indent) -> case Payload of {some, {boolean_failure, Actual, Expected}} -> Expected_text = case Expected of true -> <<"True"/utf8>>; false -> <<"False"/utf8>> end, Actual_text = case Actual of true -> <<"True"/utf8>>; false -> <<"False"/utf8>> end, format_expected_actual( Color, Base_indent, Expected_text, Actual_text ); {some, {equality_failure, Actual@1, Expected@1}} -> format_expected_actual(Color, Base_indent, Expected@1, Actual@1); {some, {option_failure, Actual@2, Expected_some}} -> Expected_text@1 = case Expected_some of true -> <<"Some(_)"/utf8>>; false -> <<"None"/utf8>> end, format_expected_actual( Color, Base_indent, Expected_text@1, Actual@2 ); {some, {result_failure, Actual@3, Expected_ok}} -> Expected_text@2 = case Expected_ok of true -> <<"Ok(_)"/utf8>>; false -> <<"Error(_)"/utf8>> end, format_expected_actual( Color, Base_indent, Expected_text@2, Actual@3 ); {some, {collection_failure, Actual@4, Expected@2, Operation}} -> Op_line = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_cyan/1, Operation))/binary>>/binary, "\n"/utf8>>, <>; {some, {comparison_failure, Actual@5, Expected@3, Operator}} -> Op_line@1 = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_cyan/1, Operator))/binary>>/binary, "\n"/utf8>>, <>; {some, {string_match_failure, Actual@6, Pattern, Operation@1}} -> Op_line@2 = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_cyan/1, Operation@1))/binary>>/binary, "\n"/utf8>>, Pat_line = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_green/1, Pattern))/binary>>/binary, "\n"/utf8>>, Actual_line = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_red/1, Actual@6))/binary>>/binary, "\n"/utf8>>, <<<>/binary, Actual_line/binary>>; {some, {snapshot_failure, Actual@7, Expected@4, Snapshot_path, Is_missing}} -> format_snapshot_failure( Color, Actual@7, Expected@4, Snapshot_path, Is_missing, Base_indent ); {some, {custom_matcher_failure, Actual@8, Description}} -> Desc_line = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_cyan/1, Description))/binary>>/binary, "\n"/utf8>>, Actual_line@1 = <<<<<> ))/binary>>/binary, ('maybe'(Color, fun ansi_red/1, Actual@8))/binary>>/binary, "\n"/utf8>>, <>; _ -> <<""/utf8>> end. -file("src/dream_test/reporters/bdd.gleam", 774). -spec format_one_failure( boolean(), dream_test@types:assertion_failure(), integer() ) -> binary(). format_one_failure(Color, Failure, Indent_level) -> Base_indent = build_indent(Indent_level), Operator_text = 'maybe'( Color, fun(T) -> ansi_bold(ansi_magenta(T)) end, erlang:element(2, Failure) ), Header = erlang:list_to_binary( [Base_indent, <<" "/utf8>>, Operator_text, <<"\n"/utf8>>] ), Message_text = format_failure_message( Color, erlang:element(3, Failure), Base_indent ), Payload_text = format_failure_payload( Color, erlang:element(4, Failure), Base_indent ), erlang:list_to_binary([Header, Message_text, Payload_text]). -file("src/dream_test/reporters/bdd.gleam", 758). -spec format_all_failures( boolean(), list(dream_test@types:assertion_failure()), integer(), binary() ) -> binary(). format_all_failures(Color, Failures, Indent_level, Accumulated) -> case Failures of [] -> Accumulated; [Failure | Rest] -> Formatted = format_one_failure(Color, Failure, Indent_level), Updated = erlang:list_to_binary([Accumulated, Formatted]), format_all_failures(Color, Rest, Indent_level, Updated) end. -file("src/dream_test/reporters/bdd.gleam", 745). -spec format_failure_details( boolean(), dream_test@types:test_result(), integer() ) -> binary(). format_failure_details(Color, Result, Indent_level) -> case erlang:element(4, Result) of failed -> format_all_failures( Color, erlang:element(7, Result), Indent_level, <<""/utf8>> ); setup_failed -> format_all_failures( Color, erlang:element(7, Result), Indent_level, <<""/utf8>> ); timed_out -> format_all_failures( Color, erlang:element(7, Result), Indent_level, <<""/utf8>> ); _ -> <<""/utf8>> end. -file("src/dream_test/reporters/bdd.gleam", 672). -spec format_test_line_with_indent_for_kind( boolean(), dream_test@types:test_result(), integer(), boolean() ) -> binary(). format_test_line_with_indent_for_kind(Color, Result, Extra_indent, Is_gherkin) -> Depth = calculate_test_depth(erlang:element(3, Result)), Indent = build_indent(Depth + Extra_indent), Marker = status_marker(Color, erlang:element(4, Result)), Name = case Is_gherkin of true -> <<"Scenario: "/utf8, (extract_test_name(erlang:element(3, Result)))/binary>>; false -> extract_test_name(erlang:element(3, Result)) end, Duration = format_duration(erlang:element(5, Result)), Colored_name = case erlang:element(4, Result) of passed -> Name; failed -> 'maybe'(Color, fun ansi_red/1, Name); setup_failed -> 'maybe'(Color, fun ansi_red/1, Name); timed_out -> 'maybe'(Color, fun ansi_red/1, Name); skipped -> 'maybe'(Color, fun ansi_yellow/1, Name); pending -> 'maybe'(Color, fun ansi_yellow/1, Name) end, Test_line = erlang:list_to_binary( [Indent, Marker, <<" "/utf8>>, Colored_name, Duration, <<"\n"/utf8>>] ), Failure_text = format_failure_details(Color, Result, Depth + Extra_indent), erlang:list_to_binary([Test_line, Failure_text]). -file("src/dream_test/reporters/bdd.gleam", 970). -spec sum_durations(list(dream_test@types:test_result()), integer()) -> integer(). sum_durations(Results, Total) -> case Results of [] -> Total; [Result | Rest] -> sum_durations(Rest, Total + erlang:element(5, Result)) end. -file("src/dream_test/reporters/bdd.gleam", 995). -spec increment_if_matches( dream_test@types:status(), dream_test@types:status(), integer() ) -> integer(). increment_if_matches(Status, Wanted, Count) -> case Status =:= Wanted of true -> Count + 1; false -> Count end. -file("src/dream_test/reporters/bdd.gleam", 981). -spec count_matching_status( list(dream_test@types:test_result()), dream_test@types:status(), integer() ) -> integer(). count_matching_status(Results, Wanted, Count) -> case Results of [] -> Count; [Result | Rest] -> Next_count = increment_if_matches( erlang:element(4, Result), Wanted, Count ), count_matching_status(Rest, Wanted, Next_count) end. -file("src/dream_test/reporters/bdd.gleam", 977). -spec count_by_status( list(dream_test@types:test_result()), dream_test@types:status() ) -> integer(). count_by_status(Results, Wanted) -> count_matching_status(Results, Wanted, 0). -file("src/dream_test/reporters/bdd.gleam", 1024). -spec format_summary_parts(list(binary())) -> binary(). format_summary_parts(Parts) -> case Parts of [] -> <<""/utf8>>; _ -> erlang:list_to_binary( [<<", "/utf8>>, gleam@string:join(Parts, <<", "/utf8>>)] ) end. -file("src/dream_test/reporters/bdd.gleam", 1031). -spec add_summary_part_if_nonzero( list(binary()), boolean(), integer(), binary(), fun((binary()) -> binary()) ) -> list(binary()). add_summary_part_if_nonzero(Parts, Color, Count, Label, Paint) -> case Count of 0 -> Parts; _ -> [erlang:list_to_binary( [Paint(erlang:integer_to_binary(Count)), 'maybe'(Color, fun ansi_dim/1, Label)] ) | Parts] end. -file("src/dream_test/reporters/bdd.gleam", 1002). -spec build_summary_suffix( boolean(), integer(), integer(), integer(), integer() ) -> binary(). build_summary_suffix(Color, Skipped, Pending, Timed_out, Setup_failed) -> Parts = begin _pipe = [], _pipe@1 = add_summary_part_if_nonzero( _pipe, Color, Skipped, <<" skipped"/utf8>>, fun ansi_yellow/1 ), _pipe@2 = add_summary_part_if_nonzero( _pipe@1, Color, Pending, <<" pending"/utf8>>, fun ansi_yellow/1 ), _pipe@3 = add_summary_part_if_nonzero( _pipe@2, Color, Timed_out, <<" timed out"/utf8>>, fun ansi_red/1 ), add_summary_part_if_nonzero( _pipe@3, Color, Setup_failed, <<" setup failed"/utf8>>, fun ansi_red/1 ) end, format_summary_parts(Parts). -file("src/dream_test/reporters/bdd.gleam", 945). -spec format_summary(boolean(), list(dream_test@types:test_result())) -> binary(). format_summary(Color, Results) -> Total = erlang:length(Results), Failed = count_by_status(Results, failed), Skipped = count_by_status(Results, skipped), Pending = count_by_status(Results, pending), Timed_out = count_by_status(Results, timed_out), Setup_failed = count_by_status(Results, setup_failed), Passed = ((((Total - Failed) - Skipped) - Pending) - Timed_out) - Setup_failed, Total_duration = sum_durations(Results, 0), erlang:list_to_binary( ['maybe'( Color, fun(T) -> ansi_bold(ansi_cyan(T)) end, <<"Summary: "/utf8>> ), 'maybe'(Color, fun ansi_cyan/1, erlang:integer_to_binary(Total)), <<" run, "/utf8>>, 'maybe'(Color, fun ansi_red/1, erlang:integer_to_binary(Failed)), <<" failed, "/utf8>>, 'maybe'(Color, fun ansi_green/1, erlang:integer_to_binary(Passed)), <<" passed"/utf8>>, build_summary_suffix( Color, Skipped, Pending, Timed_out, Setup_failed ), <<" in "/utf8>>, 'maybe'( Color, fun ansi_cyan/1, dream_test@timing:format_duration_ms(Total_duration) ), <<"\n"/utf8>>] ). -file("src/dream_test/reporters/bdd.gleam", 521). ?DOC( " ## Example\n" "\n" " ```gleam\n" " let result = passing_result()\n" " let text =\n" " bdd.format_incremental_parts_with_test_indent(result, [], 0)\n" " |> incremental_parts_text\n" "\n" " text\n" " |> should\n" " |> match_snapshot(\n" " \"./test/snapshots/bdd_format_incremental_parts_with_test_indent.snap\",\n" " )\n" " |> or_fail_with(\"expected incremental parts snapshot match\")\n" " ```\n" " Format only the trailing summary line (no per-test output).\n" "\n" " ## Parameters\n" "\n" " - `results`: The test results to summarize\n" "\n" " ## Returns\n" "\n" " A single summary line as a `String` (including a trailing newline).\n" ). -spec format_summary_only(list(dream_test@types:test_result())) -> binary(). format_summary_only(Results) -> format_summary(false, Results). -file("src/dream_test/reporters/bdd.gleam", 651). -spec count_common_prefix_check( binary(), binary(), list(binary()), list(binary()), integer() ) -> integer(). count_common_prefix_check(Prev_head, Curr_head, Prev_rest, Curr_rest, Depth) -> case Prev_head =:= Curr_head of true -> count_common_prefix(Prev_rest, Curr_rest, Depth + 1); false -> Depth end. -file("src/dream_test/reporters/bdd.gleam", 633). -spec count_common_prefix(list(binary()), list(binary()), integer()) -> integer(). count_common_prefix(Previous, Current, Depth) -> case {Previous, Current} of {[Prev_head | Prev_rest], [Curr_head | Curr_rest]} -> count_common_prefix_check( Prev_head, Curr_head, Prev_rest, Curr_rest, Depth ); {_, _} -> Depth end. -file("src/dream_test/reporters/bdd.gleam", 576). -spec format_one_result_parts_with_test_indent( boolean(), dream_test@types:test_result(), list(binary()), integer() ) -> {binary(), binary()}. format_one_result_parts_with_test_indent( Color, Result, Previous_path, Extra_test_indent ) -> Current_path = extract_describe_segments(erlang:element(3, Result)), Common_depth = count_common_prefix(Previous_path, Current_path, 0), New_segments = gleam@list:drop(Current_path, Common_depth), Is_gherkin = dream_test@reporters@gherkin:is_gherkin_result(Result), Headers = format_header_segments_for_kind( Color, New_segments, Common_depth, Is_gherkin, <<""/utf8>> ), Test_line = format_test_line_with_indent_for_kind( Color, Result, Extra_test_indent, Is_gherkin ), {Headers, Test_line}. -file("src/dream_test/reporters/bdd.gleam", 477). ?DOC( " ## Example\n" "\n" " ```gleam\n" " let result = passing_result()\n" " let bdd.FormatIncrementalResult(text: text, new_path: _new_path) =\n" " bdd.format_incremental_with_test_indent(result, [], 0)\n" "\n" " text\n" " |> should\n" " |> match_snapshot(\n" " \"./test/snapshots/bdd_format_incremental_with_test_indent.snap\",\n" " )\n" " |> or_fail_with(\"expected incremental output snapshot match\")\n" " ```\n" " Format a single incremental result as two parts:\n" " - `headers`: any new describe/group lines required for this result\n" " - `test_line`: the test line (and any failure details)\n" "\n" " This allows callers to insert additional lines (like lifecycle hooks)\n" " between headers and the test line while maintaining correct ordering.\n" "\n" " ## Parameters\n" "\n" " - `result`: The test result to format\n" " - `previous_path`: The previous describe/group path (from the prior call)\n" " - `extra_test_indent`: Additional indentation to apply to the test line\n" "\n" " ## Returns\n" "\n" " A `FormatIncrementalPartsResult` containing:\n" " - `headers`: any new describe/group lines required for this result\n" " - `test_line`: the test line (and any failure details)\n" " - `new_path`: the updated describe/group path to pass as `previous_path` for the next call\n" ). -spec format_incremental_parts_with_test_indent( dream_test@types:test_result(), list(binary()), integer() ) -> format_incremental_parts_result(). format_incremental_parts_with_test_indent( Result, Previous_path, Extra_test_indent ) -> {Headers, Test_line} = format_one_result_parts_with_test_indent( false, Result, Previous_path, Extra_test_indent ), New_path = extract_describe_segments(erlang:element(3, Result)), {format_incremental_parts_result, Headers, Test_line, New_path}. -file("src/dream_test/reporters/bdd.gleam", 552). -spec format_one_result_with_test_indent( boolean(), dream_test@types:test_result(), list(binary()), integer() ) -> binary(). format_one_result_with_test_indent( Color, Result, Previous_path, Extra_test_indent ) -> {Headers, Test_line} = format_one_result_parts_with_test_indent( Color, Result, Previous_path, Extra_test_indent ), erlang:list_to_binary([Headers, Test_line]). -file("src/dream_test/reporters/bdd.gleam", 393). ?DOC( " Format a single test result as BDD output, suitable for streaming.\n" "\n" " This is designed to be used with `ReporterEvent.TestFinished` so that BDD\n" " output can be printed as tests complete.\n" "\n" " - `previous_path` should be the describe-path (all but the leaf test name)\n" " of the previously printed result.\n" " - Returns the formatted output for this result, and the updated\n" " describe-path to use as `previous_path` for the next call.\n" "\n" " ## Parameters\n" "\n" " - `result`: The test result to format\n" " - `previous_path`: The previous describe/group path (from the prior call)\n" "\n" " ## Returns\n" "\n" " A `FormatIncrementalResult` containing the text to print and the new\n" " describe/group path to pass as `previous_path` for the next call.\n" ). -spec format_incremental(dream_test@types:test_result(), list(binary())) -> format_incremental_result(). format_incremental(Result, Previous_path) -> Text = format_one_result_with_test_indent(false, Result, Previous_path, 0), New_path = extract_describe_segments(erlang:element(3, Result)), {format_incremental_result, Text, New_path}. -file("src/dream_test/reporters/bdd.gleam", 428). ?DOC( " ## Example\n" "\n" " ```gleam\n" " use first <- result.try(first_result([passing_result()]))\n" "\n" " let bdd.FormatIncrementalResult(text: _text, new_path: new_path) =\n" " bdd.format_incremental(first, [])\n" "\n" " new_path\n" " |> should\n" " |> be_equal([\"Example Suite\"])\n" " |> or_fail_with(\"expected new_path to be the describe path\")\n" " ```\n" " Format a single test result as BDD output, allowing an extra indent level\n" " for the test line.\n" "\n" " ## Parameters\n" "\n" " - `result`: The test result to format\n" " - `previous_path`: The previous describe/group path (from the prior call)\n" " - `extra_test_indent`: Additional indentation to apply to the test line\n" "\n" " ## Returns\n" "\n" " A `FormatIncrementalResult` containing the text to print and the new\n" " describe/group path to pass as `previous_path` for the next call.\n" ). -spec format_incremental_with_test_indent( dream_test@types:test_result(), list(binary()), integer() ) -> format_incremental_result(). format_incremental_with_test_indent(Result, Previous_path, Extra_test_indent) -> Text = format_one_result_with_test_indent( false, Result, Previous_path, Extra_test_indent ), New_path = extract_describe_segments(erlang:element(3, Result)), {format_incremental_result, Text, New_path}. -file("src/dream_test/reporters/bdd.gleam", 568). -spec format_one_result( boolean(), dream_test@types:test_result(), list(binary()) ) -> binary(). format_one_result(Color, Result, Previous_path) -> format_one_result_with_test_indent(Color, Result, Previous_path, 0). -file("src/dream_test/reporters/bdd.gleam", 535). ?DOC( " ## Example\n" "\n" " ```gleam\n" " let summary = bdd.format_summary_only([passing_result()])\n" "\n" " summary\n" " |> should\n" " |> match_snapshot(\"./test/snapshots/bdd_format_summary_only.snap\")\n" " |> or_fail_with(\"expected summary snapshot match\")\n" " ```\n" ). -spec format_all_results( boolean(), list(dream_test@types:test_result()), list(binary()), binary() ) -> binary(). format_all_results(Color, Results, Previous_path, Accumulated) -> case Results of [] -> Accumulated; [Result | Rest] -> Formatted = format_one_result(Color, Result, Previous_path), Updated = erlang:list_to_binary([Accumulated, Formatted]), New_path = extract_describe_segments(erlang:element(3, Result)), format_all_results(Color, Rest, New_path, Updated) end. -file("src/dream_test/reporters/bdd.gleam", 118). -spec render_bdd_sections( boolean(), dream_test@reporters@types:bdd_output_mode(), list(dream_test@types:test_result()) ) -> binary(). render_bdd_sections(Color, Mode, Results) -> Summary = format_summary(Color, Results), case Mode of bdd_summary_only -> Summary; bdd_failures_only -> Failures = filter_failures(Results, []), Failures_text = format_all_results(Color, Failures, [], <<""/utf8>>), erlang:list_to_binary([Failures_text, <<"\n"/utf8>>, Summary]); bdd_full -> Results_text = format_all_results(Color, Results, [], <<""/utf8>>), Failures@1 = filter_failures(Results, []), Failures_text@1 = case Failures@1 of [] -> <<""/utf8>>; _ -> <<<<<<"\n"/utf8, (format_failures_header(Color))/binary>>/binary, "\n"/utf8>>/binary, (format_all_results(Color, Failures@1, [], <<""/utf8>>))/binary>> end, erlang:list_to_binary( [Results_text, Failures_text@1, <<"\n"/utf8>>, Summary] ) end. -file("src/dream_test/reporters/bdd.gleam", 110). ?DOC(" Render the end-of-run BDD output for the given results.\n"). -spec render( dream_test@reporters@types:bdd_reporter_config(), list(dream_test@types:test_result()) ) -> binary(). render(Config, Results) -> {bdd_reporter_config, Color, Mode} = Config, render_bdd_sections(Color, Mode, Results). -file("src/dream_test/reporters/bdd.gleam", 265). -spec format_unit_results(boolean(), list(dream_test@types:test_result())) -> binary(). format_unit_results(Color, Results) -> case Results of [] -> <<""/utf8>>; _ -> Sorted = gleam@list:sort(Results, fun compare_by_full_name/2), format_all_results(Color, Sorted, [], <<""/utf8>>) end. -file("src/dream_test/reporters/bdd.gleam", 246). ?DOC( " Format test results as a BDD-style report string.\n" "\n" " Returns the complete report including:\n" " - Hierarchical test results with status markers\n" " - Failure details with messages and diffs\n" " - Summary line with counts\n" "\n" " Gherkin tests are automatically formatted using the Gherkin reporter style.\n" "\n" " Use this when you need the report as a string (e.g., for testing the\n" " reporter itself or writing to a file).\n" "\n" " ## Parameters\n" "\n" " - `results`: The test results to format\n" "\n" " ## Returns\n" "\n" " A single `String` containing the formatted report and trailing summary line.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let report = bdd.format(sample_results())\n" "\n" " report\n" " |> should\n" " |> match_snapshot(\"./test/snapshots/bdd_format_report.snap\")\n" " |> or_fail_with(\"expected formatted report snapshot match\")\n" " ```\n" ). -spec format(list(dream_test@types:test_result())) -> binary(). format(Results) -> {Gherkin_results, Unit_results} = partition_by_kind(Results), Unit_text = format_unit_results(false, Unit_results), Gherkin_text = format_gherkin_results(Gherkin_results), Summary_text = format_summary(false, Results), erlang:list_to_binary( [Unit_text, Gherkin_text, <<"\n"/utf8>>, Summary_text] ). -file("src/dream_test/reporters/bdd.gleam", 366). ?DOC( " Print test results using a provided writer function.\n" "\n" " This is the main entry point for most test runs. The writer function\n" " receives the formatted report string and can print it, log it, or\n" " handle it however needed.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " bdd.report([passing_result()], write_bdd_report_to_file)\n" "\n" " use text <- result.try(\n" " file.read(\"test/tmp/bdd_report.txt\")\n" " |> result.map_error(file.error_to_string),\n" " )\n" "\n" " text\n" " |> should\n" " |> match_snapshot(\"./test/snapshots/bdd_report_file_output.snap\")\n" " |> or_fail_with(\"expected report output snapshot match\")\n" " ```\n" "\n" " ## Parameters\n" "\n" " - `results`: List of test results from the runner\n" " - `write`: Function that handles the formatted output string\n" "\n" " ## Returns\n" "\n" " Returns the input results unchanged, enabling pipeline composition.\n" ). -spec report(list(dream_test@types:test_result()), fun((binary()) -> nil)) -> list(dream_test@types:test_result()). report(Results, Write) -> Write(format(Results)), Results.