Skip to content

Commit ac83f3b

Browse files
Introduce Various New Features
* Wrapper macros for Background, Scenario and Rule to facilitate introduction of structure * `StepError` for when a `GWT` raises an `AssertionError` * Fix various pesky bugs where syntax errors would be rendered by `Messages` * Colorize dotted output like ExUnit does
1 parent 8bd228f commit ac83f3b

File tree

17 files changed

+249
-73
lines changed

17 files changed

+249
-73
lines changed

apps/ex_cucumber/lib/ex_cucumber.ex

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ defmodule ExCucumber do
1616
require ExCucumber.Gherkin.Keywords.And
1717
require ExCucumber.Gherkin.Keywords.But
1818
require ExCucumber.Gherkin.Keywords.Then
19+
require ExCucumber.Gherkin.Keywords.Background
20+
require ExCucumber.Gherkin.Keywords.Scenario
21+
require ExCucumber.Gherkin.Keywords.Rule
1922

2023
alias ExCucumber.Gherkin.Keywords, as: GherkinKeywords
2124

@@ -24,7 +27,10 @@ defmodule ExCucumber do
2427
When,
2528
And,
2629
But,
27-
Then
30+
Then,
31+
Background,
32+
Scenario,
33+
Rule
2834
}
2935

3036
import unquote(__MODULE__)
@@ -75,7 +81,20 @@ defmodule ExCucumber do
7581
:gherkin_token_mismatch
7682
)
7783
else
78-
{apply(__MODULE__, ctx.extra.fun, arg), def_meta}
84+
try do
85+
result = apply(__MODULE__, ctx.extra.fun, arg)
86+
IO.write("#{IO.ANSI.green()}.#{IO.ANSI.reset()}")
87+
{result, def_meta}
88+
rescue
89+
e in [ExUnit.AssertionError] ->
90+
ExCucumber.Exceptions.StepError.raise(
91+
Ctx.extra(ctx, %{
92+
def_meta: def_meta,
93+
raised_error: e
94+
}),
95+
:error_raised
96+
)
97+
end
7998
end
8099
end
81100
end
@@ -96,7 +115,30 @@ defmodule ExCucumber do
96115
end
97116
end
98117

99-
:macro_names
118+
:context_macros
119+
|> ExCucumber.Gherkin.Keywords.mappings()
120+
|> Map.fetch!(:regular)
121+
|> Enum.each(fn macro_name ->
122+
defmacro unquote(macro_name)(do: block) do
123+
ExCucumber.define_context_macro(
124+
__CALLER__,
125+
:no_title,
126+
nil,
127+
block
128+
)
129+
end
130+
131+
defmacro unquote(macro_name)(title, do: block) do
132+
ExCucumber.define_context_macro(
133+
__CALLER__,
134+
title,
135+
nil,
136+
block
137+
)
138+
end
139+
end)
140+
141+
:def_based_gwt_macros
100142
|> ExCucumber.Gherkin.Keywords.mappings()
101143
|> Enum.each(fn {gherkin_keyword, macro_name} ->
102144
if ExCucumber.Gherkin.Keywords.macro_style?(:def) do
@@ -138,6 +180,27 @@ defmodule ExCucumber do
138180
end
139181
end)
140182

183+
def define_context_macro(
184+
caller = %Macro.Env{},
185+
_title,
186+
arg,
187+
block
188+
) do
189+
_line = caller.line
190+
has_arg? = arg != nil
191+
192+
ast =
193+
if has_arg? do
194+
raise "Not implemented yet."
195+
else
196+
quote do
197+
unquote(block)
198+
end
199+
end
200+
201+
ast
202+
end
203+
141204
def define_gherkin_keyword_macro(
142205
caller = %Macro.Env{},
143206
gherkin_keyword,
@@ -167,6 +230,7 @@ defmodule ExCucumber do
167230
] do
168231
@cucumber_expressions cucumber_expression
169232
@meta Map.put(@meta, func, meta)
233+
@meta Map.put(@meta, cucumber_expression.formulation, func)
170234
end
171235

172236
def_ast =

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/common.ex

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,23 @@ defmodule ExCucumber.Exceptions.Messages.Common do
2222
do: render(:module_file, ctx.module_file, ctx.location.line, ctx.location.column)
2323

2424
# ARITY 3
25+
def render(:macro_usage_heading, %Ctx{} = ctx, cucumber_expression) do
26+
if macro_style = ctx.extra[:macro_style] do
27+
"""
28+
#{GherkinKeywords.macro_name(ctx, macro_style)} "#{cucumber_expression}", arg do
29+
"""
30+
else
31+
"""
32+
#{GherkinKeywords.macro_name(ctx)} "#{cucumber_expression}", arg do
33+
"""
34+
end
35+
end
36+
2537
def render(:macro_usage, %Ctx{} = ctx, cucumber_expression) do
26-
r =
27-
if macro_style = ctx.extra[:macro_style] do
28-
"""
29-
#{GherkinKeywords.macro_name(ctx, macro_style)} "#{cucumber_expression}", arg do
30-
end
31-
"""
32-
else
33-
"""
34-
#{GherkinKeywords.macro_name(ctx)} "#{cucumber_expression}", arg do
35-
end
36-
"""
37-
end
38+
r = """
39+
#{render(:macro_usage_heading, ctx, cucumber_expression)}
40+
end
41+
"""
3842

3943
render(:code_block, r)
4044
end

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/configuration_error_messages/incorrect_error_level_detail.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule ExCucumber.Exceptions.Messages.IncorrectErrorLevelDetail do
33
alias ExCucumber.{
44
Config,
55
Exceptions.ConfigurationError,
6-
Utils,
6+
Utils
77
}
88

99
def render(%ConfigurationError{error_code: :incorrect_error_level_detail}, :brief) do

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/configuration_error_messages/incorrect_macro_style.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule ExCucumber.Exceptions.Messages.IncorrectMacroStyle do
33
alias ExCucumber.{
44
Config,
55
Exceptions.ConfigurationError,
6-
Utils,
6+
Utils
77
}
88

99
def render(%ConfigurationError{error_code: :incorrect_macro_style}, :brief) do

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/configuration_error_messages/macro_style_mismatch.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule ExCucumber.Exceptions.Messages.MacroStyleMismatch do
66
Config,
77
Exceptions.ConfigurationError,
88
Gherkin.Traverser.Ctx,
9-
Utils,
9+
Utils
1010
}
1111

1212
alias ExCucumber.Gherkin.Keywords, as: GherkinKeywords
@@ -59,7 +59,7 @@ defmodule ExCucumber.Exceptions.Messages.MacroStyleMismatch do
5959
`ExCucumber` allows you to configure how you would prefer to express yourself.
6060
In the case that you like to employ the following macro styles:
6161
62-
#{example_phrases(:macro_names)}
62+
#{example_phrases(:def_based_gwt_macros)}
6363
6464
Then you need to set the following config option:
6565
#{
@@ -72,7 +72,7 @@ defmodule ExCucumber.Exceptions.Messages.MacroStyleMismatch do
7272
7373
In the case that you like to employ the following macro styles:
7474
75-
#{example_phrases(:modularized_macro_names)}
75+
#{example_phrases(:module_based_gwt_macros)}
7676
7777
Then you need to set the following config option:
7878
#{

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/match_failure_messages/unable_to_auto_match_param.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ defmodule ExCucumber.Exceptions.Messages.UnableToAutoMatchParam do
22
@moduledoc false
33
alias ExCucumber.{
44
Exceptions.MatchFailure,
5-
Utils,
5+
Utils
66
}
7+
78
alias ExCucumber.Exceptions.Messages.Common, as: CommonMessages
89

910
def render(%MatchFailure{error_code: :unable_to_auto_match_param} = f, :brief) do

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/match_failure_messages/unable_to_match.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ defmodule ExCucumber.Exceptions.Messages.UnableToMatch do
22
@moduledoc false
33
alias ExCucumber.{
44
Exceptions.MatchFailure,
5-
Utils,
5+
Utils
66
}
7+
78
alias ExCucumber.Exceptions.Messages.Common, as: CommonMessages
89

910
def render(%MatchFailure{error_code: :unable_to_match} = f, :brief) do

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/match_failure_messages/unable_to_match_param.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ defmodule ExCucumber.Exceptions.Messages.UnableToMatchParam do
44

55
alias ExCucumber.{
66
Exceptions.MatchFailure,
7-
Utils,
7+
Utils
88
}
9+
910
alias ExCucumber.Exceptions.Messages.Common, as: CommonMessages
1011

1112
def render(%MatchFailure{error_code: :unable_to_match_param} = f, :brief) do

apps/ex_cucumber/lib/ex_cucumber/exceptions/messages/messages.ex

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ defmodule ExCucumber.Exceptions.Messages do
77
## Details
88
"""
99

10+
use ExDebugger.Manual
11+
1012
alias __MODULE__.{
1113
BestPracticesIncomplete,
14+
ErrorRaised,
1215
GherkinTokenMismatch,
1316
IncorrectErrorLevelDetail,
1417
IncorrectMacroStyle,
@@ -22,7 +25,19 @@ defmodule ExCucumber.Exceptions.Messages do
2225
FeatureFileNotFound
2326
}
2427

25-
use ExDebugger.Manual
28+
alias ExCucumber.Exceptions.{
29+
ConfigurationError,
30+
MatchFailure,
31+
StepError,
32+
UsageError
33+
}
34+
35+
@cucumber_related_error_types [
36+
ConfigurationError,
37+
MatchFailure,
38+
StepError,
39+
UsageError
40+
]
2641

2742
@default_options [
2843
enabled: true,
@@ -40,8 +55,10 @@ defmodule ExCucumber.Exceptions.Messages do
4055

4156
@matcher_failure_heading "** (CucumberExpressions.Matcher.Failure)"
4257
@configuration_error_heading "** (ExCucumber.Exceptions.ConfigurationError)"
58+
@step_error_heading "** (ExCucumber.Exceptions.StepError)"
4359

4460
@delegation_mappings %{
61+
error_raised: {ErrorRaised, @step_error_heading},
4562
best_practices_incomplete: {BestPracticesIncomplete, @configuration_error_heading},
4663
unable_to_match: {UnableToMatch, @matcher_failure_heading},
4764
unable_to_match_param: {UnableToMatchParam, @matcher_failure_heading},
@@ -57,6 +74,7 @@ defmodule ExCucumber.Exceptions.Messages do
5774
}
5875

5976
def render(f, exit? \\ true)
77+
6078
def render(f, exit?) when is_atom(exit?) do
6179
error_detail_level = ExCucumber.Config.error_detail_level()
6280
dd(:render)
@@ -76,16 +94,17 @@ defmodule ExCucumber.Exceptions.Messages do
7694
end
7795
end
7896

79-
def render(%CompileError{} = f, detail_level: detail_level) do
80-
raise f
81-
end
82-
83-
def render(%_{error_code: error_code} = f, detail_level: detail_level) do
97+
def render(%error_type{error_code: error_code} = f, detail_level: detail_level) do
8498
{module, heading} = Map.fetch!(@delegation_mappings, error_code)
8599

86-
{
87-
heading,
88-
module.render(f, detail_level)
89-
}
100+
cond do
101+
error_type in @cucumber_related_error_types -> {heading, module.render(f, detail_level)}
102+
true -> raise f
103+
end
104+
end
105+
106+
def render(f, _) do
107+
IO.inspect(f, label: :f)
108+
raise f
90109
end
91110
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule ExCucumber.Exceptions.Messages.ErrorRaised do
2+
@moduledoc false
3+
alias ExCucumber.{
4+
Exceptions.StepError,
5+
Utils
6+
}
7+
8+
alias ExCucumber.Exceptions.Messages.Common, as: CommonMessages
9+
10+
def render(%StepError{error_code: :error_raised} = e, _) do
11+
{_, [line: line], _} = e.ctx.extra.raised_error.expr
12+
13+
"""
14+
Feature File: #{CommonMessages.render(:feature_file, e.ctx)}
15+
#{inspect(e.ctx.module) |> to_string |> String.trim_leading("Elixir.")}: #{
16+
CommonMessages.render(:module_file, e.ctx.module_file, line, 0)
17+
}
18+
Step: #{
19+
Utils.backtick(
20+
CommonMessages.render(:macro_usage_heading, e.ctx, e.ctx.extra.cucumber_expression)
21+
)
22+
}
23+
Error:
24+
#{CommonMessages.render(:code_block, Exception.format(:error, e.ctx.extra.raised_error, []))}
25+
"""
26+
end
27+
end

0 commit comments

Comments
 (0)