Skip to content

Commit e4a449a

Browse files
Introduce Rule Support
1 parent d17f714 commit e4a449a

File tree

6 files changed

+131
-4
lines changed

6 files changed

+131
-4
lines changed

apps/ex_cucumber/lib/ex_cucumber/gherkin/traverser/background_traverser.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,24 @@ defmodule ExCucumber.Gherkin.Traverser.Background do
1010
}
1111

1212
def run(%Background{} = b, acc, parse_tree) do
13-
acc = Ctx.extra(acc, background_meta(b))
13+
acc = Ctx.extra(acc, background_meta(b, acc))
1414

1515
b.steps
1616
|> Enum.reduce(acc, fn
1717
%Step{} = step, a -> MainTraverser.run(step, a, parse_tree)
1818
end)
1919
end
2020

21-
defp background_meta(background) do
21+
defp background_meta(background, acc) do
22+
background_key =
23+
if Map.has_key?(acc.extra, :rule) do
24+
:background_rule
25+
else
26+
:background
27+
end
28+
2229
%{
23-
background: %{
30+
background_key => %{
2431
title: background.name,
2532
location: Map.from_struct(background.location),
2633
keyword: background.keyword
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
defmodule ExCucumber.Gherkin.Traverser.Rule do
2+
@moduledoc false
3+
4+
alias ExCucumber.Gherkin.Traverser.Ctx
5+
alias ExCucumber.Gherkin.Traverser, as: MainTraverser
6+
7+
alias ExGherkin.AstNdjson.Background
8+
9+
def run(%ExGherkin.AstNdjson.Rule{} = r, acc, parse_tree) do
10+
{background, children} = background(r.children)
11+
12+
acc = Ctx.extra(acc, rule_meta(r))
13+
14+
children
15+
|> Enum.each(fn child ->
16+
acc = MainTraverser.run(background, acc, parse_tree)
17+
18+
child
19+
|> case do
20+
%{scenario: scenario} -> MainTraverser.run(scenario, acc, parse_tree)
21+
end
22+
end)
23+
end
24+
25+
defp background([]), do: {nil, []}
26+
defp background([%{background: b = %Background{}}]), do: {b, []}
27+
defp background([%{background: b = %Background{}} | tl]), do: {b, tl}
28+
defp background(ls), do: {nil, ls}
29+
30+
defp rule_meta(rule) do
31+
%{
32+
rule: %{
33+
title: rule.name,
34+
location: Map.from_struct(rule.location),
35+
keyword: rule.keyword
36+
}
37+
}
38+
end
39+
end

apps/ex_cucumber/lib/ex_cucumber/gherkin/traverser/traverser.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule ExCucumber.Gherkin.Traverser do
33
alias __MODULE__.Ctx
44
alias __MODULE__.Feature, as: FeatureTraverser
55
alias __MODULE__.Background, as: BackgroundTraverser
6+
alias __MODULE__.Rule, as: RuleTraverser
67
alias __MODULE__.Scenario, as: ScenarioTraverser
78
alias __MODULE__.Step, as: StepTraverser
89

@@ -20,6 +21,9 @@ defmodule ExCucumber.Gherkin.Traverser do
2021
def run(%ExGherkin.AstNdjson.Background{} = f, acc, parse_tree),
2122
do: BackgroundTraverser.run(f, acc, parse_tree)
2223

24+
def run(%ExGherkin.AstNdjson.Rule{} = f, acc, parse_tree),
25+
do: RuleTraverser.run(f, acc, parse_tree)
26+
2327
def run(%ExGherkin.AstNdjson.Scenario{token: _} = f, acc, parse_tree),
2428
do: ScenarioTraverser.run(f, acc, parse_tree)
2529

apps/ex_cucumber/test/ex_cucumber_test.exs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ defmodule ExCucumberTest do
3030
Params.Canonical => "#{@support_module_dir}/params/canonical.ex",
3131
Params.Custom => "#{@support_module_dir}/params/custom.ex",
3232
BookStoreFeature.DemonstrateBackgroundUsage =>
33-
"#{@support_module_dir}/book_store_feature/demonstrate_background_usage.ex"
33+
"#{@support_module_dir}/book_store_feature/demonstrate_background_usage.ex",
34+
RuleFeature.DemonstrateRuleUsage =>
35+
"#{@support_module_dir}/rule_feature/demonstrate_rule_usage.ex"
3436
}
3537

3638
setup_all do
@@ -120,6 +122,13 @@ defmodule ExCucumberTest do
120122
recompile(ctx)
121123
end)
122124
end
125+
126+
@tag test_module: RuleFeature.DemonstrateRuleUsage
127+
test "Handles Rule", ctx do
128+
refute_raise(fn ->
129+
recompile(ctx)
130+
end)
131+
end
123132
end
124133

125134
def reset_env_switch(ctx) do
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Feature: Gherkin 6 syntax
2+
3+
Background:
4+
Given there is a monster with 2 hitpoints
5+
6+
Scenario: Battle
7+
When I attack it
8+
Then the monster should be alive
9+
When I attack it
10+
Then it should die
11+
12+
Rule: Battle with preemptive attack
13+
Background:
14+
Given I attack the monster and do 1 points damage
15+
16+
Example: battle
17+
When I attack it
18+
Then it should die
19+
20+
Rule: Battle with preemptive critical attack
21+
Background:
22+
Given I attack the monster and do 2 points damage
23+
24+
Example: battle
25+
Then it should die
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
defmodule Support.RuleFeature.DemonstrateRuleUsage do
2+
use ExCucumber
3+
@feature "rule.feature"
4+
5+
defmodule Monster do
6+
defstruct hitpoints: 0,
7+
alive?: false
8+
9+
def new(hitpoints), do: __MODULE__ |> struct(%{hitpoints: hitpoints}) |> check_alive
10+
11+
def take_hit(%__MODULE__{} = m, damage \\ 1),
12+
do: %{m | hitpoints: m.hitpoints - damage} |> check_alive
13+
14+
defp check_alive(%__MODULE__{} = m), do: %{m | alive?: m.hitpoints > 0}
15+
end
16+
17+
# Main Background
18+
Given._ "there is a monster with {int} hitpoints", args do
19+
{
20+
:ok,
21+
%{
22+
monster: Monster.new(Keyword.fetch!(args.params, :int))
23+
}
24+
}
25+
end
26+
27+
# Scenario: Battle
28+
When._ "I attack it", args do
29+
{:ok, %{monster: Monster.take_hit(args.state.monster)}}
30+
end
31+
32+
Then._ "the monster should be alive", args do
33+
assert args.state.monster.alive?
34+
end
35+
36+
Then._ "it should die", args do
37+
refute args.state.monster.alive?
38+
end
39+
40+
Given._ "I attack the monster and do {int} points damage", args do
41+
{:ok, %{monster: Monster.take_hit(args.state.monster, Keyword.fetch!(args.params, :int))}}
42+
end
43+
end

0 commit comments

Comments
 (0)