spinach 0.1.5.4 → 0.2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/Gemfile +2 -0
  2. data/README.markdown +18 -12
  3. data/features/background.feature +13 -0
  4. data/features/reporting/show_step_source_location.feature +11 -1
  5. data/features/steps/automatic_feature_generation.rb +7 -7
  6. data/features/steps/background.rb +30 -0
  7. data/features/steps/exit_status.rb +12 -13
  8. data/features/steps/feature_name_guessing.rb +7 -7
  9. data/features/steps/reporting/display_run_summary.rb +22 -22
  10. data/features/steps/reporting/error_reporting.rb +7 -7
  11. data/features/steps/reporting/show_step_source_location.rb +63 -14
  12. data/features/steps/reporting/undefined_feature_reporting.rb +7 -7
  13. data/features/steps/rspec_compatibility.rb +14 -14
  14. data/features/support/error_reporting.rb +5 -5
  15. data/features/support/filesystem.rb +71 -0
  16. data/features/support/spinach_runner.rb +5 -6
  17. data/lib/spinach.rb +11 -6
  18. data/lib/spinach/background.rb +11 -0
  19. data/lib/spinach/capybara.rb +7 -1
  20. data/lib/spinach/cli.rb +36 -13
  21. data/lib/spinach/config.rb +40 -6
  22. data/lib/spinach/dsl.rb +14 -11
  23. data/lib/spinach/exceptions.rb +1 -1
  24. data/lib/spinach/feature.rb +16 -0
  25. data/lib/spinach/frameworks.rb +2 -0
  26. data/lib/spinach/{suites → frameworks}/minitest.rb +0 -0
  27. data/lib/spinach/{suites → frameworks}/rspec.rb +0 -0
  28. data/lib/spinach/generators/feature_generator.rb +12 -23
  29. data/lib/spinach/generators/step_generator.rb +5 -5
  30. data/lib/spinach/hookable.rb +6 -4
  31. data/lib/spinach/hooks.rb +12 -4
  32. data/lib/spinach/parser.rb +6 -8
  33. data/lib/spinach/parser/visitor.rb +109 -0
  34. data/lib/spinach/reporter.rb +10 -6
  35. data/lib/spinach/reporter/stdout.rb +41 -16
  36. data/lib/spinach/reporter/stdout/error_reporting.rb +2 -2
  37. data/lib/spinach/runner.rb +9 -6
  38. data/lib/spinach/runner/feature_runner.rb +40 -34
  39. data/lib/spinach/runner/scenario_runner.rb +63 -36
  40. data/lib/spinach/scenario.rb +12 -0
  41. data/lib/spinach/step.rb +10 -0
  42. data/lib/spinach/version.rb +1 -1
  43. data/spinach.gemspec +3 -3
  44. data/test/spinach/background_test.rb +6 -0
  45. data/test/spinach/capybara_test.rb +30 -13
  46. data/test/spinach/cli_test.rb +46 -1
  47. data/test/spinach/config_test.rb +39 -0
  48. data/test/spinach/dsl_test.rb +12 -10
  49. data/test/spinach/feature_steps_test.rb +3 -3
  50. data/test/spinach/feature_test.rb +6 -0
  51. data/test/spinach/{suites → frameworks}/minitest_test.rb +2 -2
  52. data/test/spinach/generators/feature_generator_test.rb +18 -58
  53. data/test/spinach/generators/step_generator_test.rb +3 -3
  54. data/test/spinach/generators_test.rb +12 -10
  55. data/test/spinach/hookable_test.rb +8 -0
  56. data/test/spinach/hooks_test.rb +6 -7
  57. data/test/spinach/parser/visitor_test.rb +173 -0
  58. data/test/spinach/parser_test.rb +14 -27
  59. data/test/spinach/reporter/stdout/error_reporting_test.rb +9 -9
  60. data/test/spinach/reporter/stdout_test.rb +15 -19
  61. data/test/spinach/reporter_test.rb +15 -0
  62. data/test/spinach/runner/feature_runner_test.rb +79 -69
  63. data/test/spinach/runner/scenario_runner_test.rb +118 -92
  64. data/test/spinach/runner_test.rb +10 -6
  65. data/test/spinach/scenario_test.rb +6 -0
  66. data/test/spinach/step_test.rb +6 -0
  67. data/test/spinach_test.rb +7 -7
  68. metadata +60 -39
  69. data/lib/spinach/suites.rb +0 -2
@@ -5,6 +5,17 @@ describe Spinach::Config do
5
5
  Spinach::Config.new
6
6
  end
7
7
 
8
+ describe '#features_path' do
9
+ it 'returns a default' do
10
+ subject[:features_path].must_be_kind_of String
11
+ end
12
+
13
+ it 'can be overwritten' do
14
+ subject[:features_path] = 'test'
15
+ subject[:features_path].must_equal 'test'
16
+ end
17
+ end
18
+
8
19
  describe '#step_definitions_path' do
9
20
  it 'returns a default' do
10
21
  subject[:step_definitions_path].must_be_kind_of String
@@ -42,4 +53,32 @@ describe Spinach::Config do
42
53
  subject[:failure_exceptions].must_include RuntimeError
43
54
  end
44
55
  end
56
+
57
+ describe '#config_path' do
58
+ it 'returns a default' do
59
+ subject[:config_path].must_equal 'config/spinach.yml'
60
+ end
61
+
62
+ it 'can be overwritten' do
63
+ subject[:config_path] = 'my_config_file.yml'
64
+ subject[:config_path].must_equal 'my_config_file.yml'
65
+ end
66
+ end
67
+
68
+ describe '#parse_from_file' do
69
+ before do
70
+ subject[:config_path] = 'a_path'
71
+ YAML.stubs(:load_file).returns({support_path: 'my_path', config_path: 'another_path'})
72
+ subject.parse_from_file
73
+ end
74
+
75
+ it 'sets the options' do
76
+ subject[:support_path].must_equal 'my_path'
77
+ end
78
+
79
+ it "doesn't set the config_path option" do
80
+ subject[:config_path].must_equal 'a_path'
81
+ end
82
+ end
83
+
45
84
  end
@@ -15,18 +15,9 @@ describe Spinach::DSL do
15
15
  step_executed = true
16
16
  end
17
17
 
18
- @feature.new.execute_step('I say goodbye')
18
+ @feature.new.execute(stub(name: 'I say goodbye'))
19
19
  step_executed.must_equal true
20
20
  end
21
-
22
- it 'returns step source location' do
23
- @feature.When('I say goodbye') do
24
- 'You say hello'
25
- end
26
-
27
- @feature.new.execute_step('I say goodbye').first.must_include '/dsl_test.rb'
28
- @feature.new.execute_step('I say goodbye').last.must_be_kind_of Fixnum
29
- end
30
21
  end
31
22
 
32
23
  describe '#When, #Then, #And, #But' do
@@ -50,5 +41,16 @@ describe Spinach::DSL do
50
41
  @feature.new.name.must_equal "A cool feature"
51
42
  end
52
43
  end
44
+
45
+ describe '#step_location_for' do
46
+ it 'returns step source location' do
47
+ @feature.When('I say goodbye') do
48
+ 'You say hello'
49
+ end
50
+
51
+ @feature.new.step_location_for('I say goodbye').first.must_include '/dsl_test.rb'
52
+ @feature.new.step_location_for('I say goodbye').last.must_be_kind_of Fixnum
53
+ end
54
+ end
53
55
  end
54
56
  end
@@ -31,16 +31,16 @@ describe Spinach::FeatureSteps do
31
31
  end.new
32
32
  end
33
33
 
34
- describe 'execute_step' do
34
+ describe 'execute' do
35
35
  it 'runs defined step correctly' do
36
- feature.execute_step('I go to the toilet')
36
+ feature.execute(stub(name: 'I go to the toilet'))
37
37
 
38
38
  feature.pee.must_equal true
39
39
  end
40
40
 
41
41
  it 'raises an exception if step is not defined' do
42
42
  proc {
43
- feature.execute_step 'I am lost'
43
+ feature.execute(stub(name: 'I am lost'))
44
44
  }.must_raise Spinach::StepNotDefinedException
45
45
  end
46
46
  end
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ module Spinach
4
+ describe Feature do
5
+ end
6
+ end
@@ -1,8 +1,8 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- describe "minitest suite" do
3
+ describe "minitest framework" do
4
4
  before do
5
- require_relative '../../../lib/spinach/suites/minitest'
5
+ require_relative '../../../lib/spinach/frameworks/minitest'
6
6
  end
7
7
 
8
8
  it "adds MiniTest::Assertion into the failure exceptions" do
@@ -4,81 +4,41 @@ require_relative '../../../lib/spinach/generators'
4
4
  module Spinach::Generators
5
5
  describe FeatureGenerator do
6
6
  subject do
7
- FeatureGenerator.new(data)
7
+ FeatureGenerator.new(feature)
8
8
  end
9
9
 
10
- let(:data) do
11
- Spinach::Parser.new("
12
- Feature: Cheezburger can I has
13
- Scenario: Some Lulz
14
- Given I haz a sad
15
- When I get some lulz
16
- Then I haz a happy
17
- Scenario: Some sad
18
- Given I haz a happy
19
- When I get some lulz
20
- Then I am OMG ROFLMAO").parse
10
+ let(:feature) do
11
+ Spinach::Parser.new("""
12
+ Feature: Cheezburger can I has
13
+ Background:
14
+ Given I liek cheezburger
15
+ Scenario: Some Lulz
16
+ Given I haz a sad
17
+ When I get some lulz
18
+ Then I haz a happy
19
+ Scenario: Some sad
20
+ Given I haz a happy
21
+ When I get some lulz
22
+ Then I am OMG ROFLMAO""").parse
21
23
  end
22
24
 
23
25
  describe "#name" do
24
- it "returns the scenario name" do
26
+ it "returns the feature name" do
25
27
  subject.name.must_equal 'Cheezburger can I has'
26
28
  end
27
-
28
- it "returns nil if no name present" do
29
- data['name'] = nil
30
- subject.name.must_equal nil
31
- end
32
29
  end
33
30
 
34
31
  describe "#steps" do
35
32
  it "returns a correct number different steps for this data" do
36
- subject.steps.length.must_equal 4
37
- end
38
-
39
- describe "includes the correct steps" do
40
- it "includes 'Given I haz a sad'" do
41
- subject.steps.any?{ |step|
42
- (step['keyword'] == 'Given') && (step['name'] == 'I haz a sad')
43
- }.must_equal true
44
- end
45
-
46
- it "includes 'When I get some lulz'" do
47
- subject.steps.any?{ |step|
48
- (step['keyword'] == 'When') && (step['name'] == 'I get some lulz')
49
- }.must_equal true
50
- end
51
-
52
- it "includes 'Then I has a happy'" do
53
- subject.steps.any?{ |step|
54
- (step['keyword'] == 'Then') && (step['name'] == 'I haz a happy')
55
- }.must_equal true
56
- end
57
-
58
- it "does not include 'Given I haz a happy'" do
59
- subject.steps.any?{ |step|
60
- (step['keyword'] == 'Given') && (step['name'] == 'I haz a happy')
61
- }.must_equal false
62
- end
63
-
64
- it "includes 'Then I am OMG ROFLMAO'" do
65
- subject.steps.any?{ |step|
66
- (step['keyword'] == 'Then') && (step['name'] == 'I am OMG ROFLMAO')
67
- }.must_equal true
68
- end
33
+ subject.steps.length.must_equal 5
69
34
  end
70
35
  end
71
36
 
72
37
  describe "#generate" do
73
38
  it "generates an entire feature_steps class definition" do
74
39
  result = subject.generate
75
- klass = eval(result)
76
- feature_runner = Spinach::Runner::FeatureRunner.new(stub_everything)
77
- feature_runner.stubs(data: data)
78
- error_count = 0
79
- CheezburgerCanIHas.any_instance.
80
- expects(:raise).with("step not implemented").times(6).returns(nil)
81
- feature_runner.run.must_equal true
40
+ result.must_match /Given 'I haz a sad' do/
41
+ result.must_match /raise 'step not implemented'/
82
42
  end
83
43
  end
84
44
 
@@ -4,11 +4,11 @@ require_relative '../../../lib/spinach/generators'
4
4
  module Spinach::Generators
5
5
  describe StepGenerator do
6
6
  subject do
7
- StepGenerator.new(data)
7
+ StepGenerator.new(step)
8
8
  end
9
9
 
10
- let(:data) do
11
- {'keyword' => 'Given', 'name' => "I has a sad"}
10
+ let(:step) do
11
+ stub(keyword: 'Given', name: 'I has a sad')
12
12
  end
13
13
 
14
14
  describe "#generate" do
@@ -6,16 +6,18 @@ describe Spinach::Generators do
6
6
  end
7
7
 
8
8
  let(:data) do
9
- Spinach::Parser.new("
10
- Feature: Cheezburger can I has
11
- Scenario: Some Lulz
12
- Given I haz a sad
13
- When I get some lulz
14
- Then I haz a happy
15
- Scenario: Some sad
16
- Given I haz a happy
17
- When I get some lulz
18
- Then I am OMG ROFLMAO").parse
9
+ Spinach::Parser.new("""
10
+ Feature: Cheezburger can I has
11
+ Background:
12
+ Given I liek cheezburger
13
+ Scenario: Some Lulz
14
+ Given I haz a sad
15
+ When I get some lulz
16
+ Then I haz a happy
17
+ Scenario: Some sad
18
+ Given I haz a happy
19
+ When I get some lulz
20
+ Then I am OMG ROFLMAO""").parse
19
21
  end
20
22
 
21
23
  describe "#bind" do
@@ -48,6 +48,14 @@ describe Spinach::Hookable do
48
48
  subject.run_hook(:before_save, 1, 2)
49
49
  array.must_equal [1, 2]
50
50
  end
51
+
52
+ it "yields to hook block even if nothing is hooked" do
53
+ called = false
54
+ subject.run_hook(:before_save) do
55
+ called = true
56
+ end
57
+ called.must_equal true
58
+ end
51
59
  end
52
60
 
53
61
  describe "#reset_hooks" do
@@ -7,7 +7,7 @@ describe Spinach::Hooks do
7
7
 
8
8
  describe "hooks" do
9
9
  %w{before_run after_run before_feature after_feature on_undefined_feature
10
- before_scenario after_scenario before_step after_step on_successful_step
10
+ before_scenario around_scenario after_scenario before_step after_step on_successful_step
11
11
  on_failed_step on_error_step on_undefined_step on_skipped_step}.each do |callback|
12
12
  describe "#{callback}" do
13
13
  it "responds to #{callback}" do
@@ -26,11 +26,10 @@ describe Spinach::Hooks do
26
26
  end
27
27
  end
28
28
  end
29
+
29
30
  describe "#on_tag" do
30
- let(:data) do
31
- {
32
- 'tags' => [{'name' => '@javascript'}, {'name' => '@capture'}]
33
- }
31
+ let(:scenario) do
32
+ stub(tags: ['javascript', 'capture'])
34
33
  end
35
34
 
36
35
  it "calls the block if the scenario includes the tag" do
@@ -38,7 +37,7 @@ describe Spinach::Hooks do
38
37
  subject.on_tag('javascript') do
39
38
  assertion = true
40
39
  end
41
- subject.run_before_scenario(data)
40
+ subject.run_before_scenario(scenario)
42
41
  assertion.must_equal true
43
42
  end
44
43
 
@@ -47,7 +46,7 @@ describe Spinach::Hooks do
47
46
  subject.on_tag('screenshot') do
48
47
  assertion = true
49
48
  end
50
- subject.run_before_scenario(data)
49
+ subject.run_before_scenario(scenario)
51
50
  assertion.wont_equal true
52
51
  end
53
52
  end
@@ -0,0 +1,173 @@
1
+ require 'test_helper'
2
+
3
+ module Spinach
4
+ class Parser
5
+ describe Visitor do
6
+ let(:feature) { Feature.new }
7
+ let(:visitor) { Visitor.new }
8
+
9
+ describe '#visit' do
10
+ it 'makes ast accept self' do
11
+ ast = stub('AST')
12
+ ast.expects(:accept).with(visitor)
13
+
14
+ visitor.visit(ast)
15
+ end
16
+
17
+ it 'returns the feature' do
18
+ ast = stub_everything
19
+ visitor.instance_variable_set(:@feature, feature)
20
+ visitor.visit(ast).must_equal feature
21
+ end
22
+ end
23
+
24
+ describe '#visit_Feature' do
25
+ before do
26
+ @background = stub_everything
27
+ @scenarios = [stub_everything, stub_everything, stub_everything]
28
+ @node = stub(scenarios: @scenarios, name: 'Go shopping', background: @background)
29
+ end
30
+
31
+ it 'sets the name' do
32
+ visitor.visit_Feature(@node)
33
+ visitor.feature.name.must_equal 'Go shopping'
34
+ end
35
+
36
+ it 'iterates over its children' do
37
+ @scenarios.each do |scenario|
38
+ scenario.expects(:accept).with visitor
39
+ end
40
+
41
+ visitor.visit_Feature(@node)
42
+ end
43
+ end
44
+
45
+ describe '#visit_Scenario' do
46
+ before do
47
+ @steps = [stub_everything, stub_everything, stub_everything]
48
+ @tags = [stub_everything, stub_everything, stub_everything]
49
+ @node = stub(
50
+ tags: @tags,
51
+ steps: @steps,
52
+ name: 'Go shopping on Saturday morning',
53
+ line: 3
54
+ )
55
+ end
56
+
57
+ it 'adds the scenario to the feature' do
58
+ visitor.visit_Scenario(@node)
59
+ visitor.feature.scenarios.length.must_equal 1
60
+ end
61
+
62
+ it 'sets the name' do
63
+ visitor.visit_Scenario(@node)
64
+ visitor.feature.scenarios.first.name.must_equal 'Go shopping on Saturday morning'
65
+ end
66
+
67
+ it 'sets the line' do
68
+ visitor.visit_Scenario(@node)
69
+ visitor.feature.scenarios.first.line.must_equal 3
70
+ end
71
+
72
+ it 'sets the tags' do
73
+ @tags.each do |step|
74
+ step.expects(:accept).with visitor
75
+ end
76
+ visitor.visit_Scenario(@node)
77
+ end
78
+
79
+ it 'iterates over its children' do
80
+ @steps.each do |step|
81
+ step.expects(:accept).with visitor
82
+ end
83
+ visitor.visit_Scenario(@node)
84
+ end
85
+
86
+ end
87
+
88
+ describe '#visit_Background' do
89
+ before do
90
+ @steps = [stub_everything, stub_everything, stub_everything]
91
+ @node = stub(
92
+ steps: @steps,
93
+ line: 3
94
+ )
95
+ end
96
+
97
+ it 'adds the background to the feature' do
98
+ visitor.visit_Background(@node)
99
+ visitor.feature.background.must_be_kind_of Background
100
+ end
101
+
102
+ it 'sets the line' do
103
+ visitor.visit_Background(@node)
104
+ visitor.feature.background.line.must_equal 3
105
+ end
106
+
107
+ it 'iterates over its children' do
108
+ @steps.each do |step|
109
+ step.expects(:accept).with visitor
110
+ end
111
+ visitor.visit_Background(@node)
112
+ end
113
+
114
+ it 'visits the background' do
115
+ visitor.visit_Background(@node)
116
+ end
117
+ end
118
+
119
+ describe '#visit_Tag' do
120
+ it 'adds the tag to the current scenario' do
121
+ tags = ['tag1', 'tag2', 'tag3']
122
+ scenario = stub(tags: tags)
123
+ visitor.instance_variable_set(:@current_scenario, scenario)
124
+
125
+ visitor.visit_Tag(stub(name: 'tag4'))
126
+ scenario.tags.must_equal ['tag1', 'tag2', 'tag3', 'tag4']
127
+ end
128
+ end
129
+
130
+ describe '#visit_Step' do
131
+ before do
132
+ @node = stub(name: 'Baz', line: 3, keyword: 'Given')
133
+ @steps = [stub(name: 'Foo'), stub(name: 'Bar')]
134
+ end
135
+
136
+ it 'adds the step to the step set' do
137
+ step_set = stub(steps: @steps)
138
+ visitor.instance_variable_set(:@current_step_set, step_set)
139
+
140
+ visitor.visit_Step(@node)
141
+ step_set.steps.length.must_equal 3
142
+ end
143
+
144
+ it 'sets the name' do
145
+ step_set = stub(steps: [])
146
+ visitor.instance_variable_set(:@current_step_set, step_set)
147
+
148
+ visitor.visit_Step(@node)
149
+
150
+ step_set.steps.first.name.must_equal 'Baz'
151
+ end
152
+
153
+ it 'sets the keyword' do
154
+ step_set = stub(steps: [])
155
+ visitor.instance_variable_set(:@current_step_set, step_set)
156
+
157
+ visitor.visit_Step(@node)
158
+
159
+ step_set.steps.first.keyword.must_equal 'Given'
160
+ end
161
+
162
+ it 'sets the line' do
163
+ step_set = stub(steps: [])
164
+ visitor.instance_variable_set(:@current_step_set, step_set)
165
+
166
+ visitor.visit_Step(@node)
167
+
168
+ step_set.steps.first.line.must_equal 3
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end