Current section
Files
Jump to
Current section
Files
src/dream_test@reporters@bdd.erl
-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, render/2, format/1, report/2, format_incremental/2, format_incremental_with_test_indent/3, format_incremental_parts_with_test_indent/3, format_summary_only/1]).
-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", 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", 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", 180).
-spec ansi_red(binary()) -> binary().
ansi_red(Text) ->
ansi_wrap(<<"31"/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", 176).
-spec ansi_dim(binary()) -> binary().
ansi_dim(Text) ->
ansi_wrap(<<"2"/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", 184).
-spec ansi_green(binary()) -> binary().
ansi_green(Text) ->
ansi_wrap(<<"32"/utf8>>, Text).
-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", 188).
-spec ansi_yellow(binary()) -> binary().
ansi_yellow(Text) ->
ansi_wrap(<<"33"/utf8>>, Text).
-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 = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Operation: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_cyan/1, Operation))/binary>>/binary,
"\n"/utf8>>,
<<Op_line/binary,
(format_expected_actual(
Color,
Base_indent,
Expected@2,
Actual@4
))/binary>>;
{some, {comparison_failure, Actual@5, Expected@3, Operator}} ->
Op_line@1 = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Operator: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_cyan/1, Operator))/binary>>/binary,
"\n"/utf8>>,
<<Op_line@1/binary,
(format_expected_actual(
Color,
Base_indent,
Expected@3,
Actual@5
))/binary>>;
{some, {string_match_failure, Actual@6, Pattern, Operation@1}} ->
Op_line@2 = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Operation: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_cyan/1, Operation@1))/binary>>/binary,
"\n"/utf8>>,
Pat_line = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Pattern: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_green/1, Pattern))/binary>>/binary,
"\n"/utf8>>,
Actual_line = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Actual: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_red/1, Actual@6))/binary>>/binary,
"\n"/utf8>>,
<<<<Op_line@2/binary, Pat_line/binary>>/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 = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Description: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_cyan/1, Description))/binary>>/binary,
"\n"/utf8>>,
Actual_line@1 = <<<<<<Base_indent/binary,
('maybe'(
Color,
fun ansi_dim/1,
<<" Actual: "/utf8>>
))/binary>>/binary,
('maybe'(Color, fun ansi_red/1, Actual@8))/binary>>/binary,
"\n"/utf8>>,
<<Desc_line/binary, Actual_line@1/binary>>;
_ ->
<<""/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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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.
-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", 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", 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).