spinach 0.1.5.4 → 0.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -36,7 +36,7 @@ module Spinach
36
36
  if undefined_features.any?
37
37
  error.puts " Undefined features (#{undefined_features.length})".light_yellow
38
38
  undefined_features.each do |feature|
39
- error.puts " #{feature['name']}".yellow
39
+ error.puts " #{feature.name}".yellow
40
40
  end
41
41
  end
42
42
  end
@@ -93,7 +93,7 @@ module Spinach
93
93
  #
94
94
  def summarized_error(error)
95
95
  feature, scenario, step, exception = error
96
- summary = " #{feature['name']} :: #{scenario['name']} :: #{full_step step}"
96
+ summary = " #{feature.name} :: #{scenario.name} :: #{full_step step}"
97
97
  if exception.kind_of?(Spinach::StepNotDefinedException)
98
98
  summary.yellow
99
99
  else
@@ -54,14 +54,17 @@ module Spinach
54
54
  # @api public
55
55
  def run
56
56
  require_dependencies
57
- require_suites
57
+ require_frameworks
58
58
 
59
59
  Spinach.hooks.run_before_run
60
60
 
61
61
  successful = true
62
62
 
63
- filenames.each do |filename|
64
- success = FeatureRunner.new(filename).run
63
+ filenames.map do |filename|
64
+ filename.split(':')
65
+ end.each do |filename, line|
66
+ feature = Parser.open_file(filename).parse
67
+ success = FeatureRunner.new(feature, line).run
65
68
  successful = false unless success
66
69
  end
67
70
 
@@ -80,10 +83,10 @@ module Spinach
80
83
  end
81
84
  end
82
85
 
83
- # Requires the test suite support
86
+ # Requires the test framework support
84
87
  #
85
- def require_suites
86
- require_relative 'suites'
88
+ def require_frameworks
89
+ require_relative 'frameworks'
87
90
  end
88
91
 
89
92
  # @return [Array<String>] files
@@ -3,30 +3,19 @@ module Spinach
3
3
  # A feature runner handles a particular feature run.
4
4
  #
5
5
  class FeatureRunner
6
+ attr_reader :feature
6
7
 
7
- # The file that describes the feature.
8
- attr_reader :filename
9
-
10
- # @param [String] filename
11
- # path to the feature file. Scenario line could be passed to run just
12
- # that scenario.
13
- # @example feature/a_cool_feature.feature:12
14
- #
15
- # @api public
16
- def initialize(filename)
17
- @filename, @scenario_line = filename.split(':')
18
- end
19
-
20
- # The file taht describes the feature.
8
+ # @param [Gherkin::AST::Feature] feature
9
+ # The feature to run.
21
10
  #
22
- attr_reader :filename
23
-
24
- # @return [Hash]
25
- # The parsed data for this feature.
11
+ # @param [#to_i] line
12
+ # If a scenario line is passed, then only the scenario defined on that
13
+ # line will be run.
26
14
  #
27
15
  # @api public
28
- def data
29
- @data ||= Spinach::Parser.open_file(filename).parse
16
+ def initialize(feature, line=nil)
17
+ @feature = feature
18
+ @line = line.to_i if line
30
19
  end
31
20
 
32
21
  # @return [String]
@@ -34,15 +23,15 @@ module Spinach
34
23
  #
35
24
  # @api public
36
25
  def feature_name
37
- @feature_name ||= data['name']
26
+ @feature.name
38
27
  end
39
28
 
40
- # @return [Hash]
29
+ # @return [Array<Gherkin::AST::Scenario>]
41
30
  # The parsed scenarios for this runner's feature.
42
31
  #
43
32
  # @api public
44
33
  def scenarios
45
- @scenarios ||= (data['elements'] || [])
34
+ @feature.scenarios
46
35
  end
47
36
 
48
37
  # Runs this feature.
@@ -52,21 +41,38 @@ module Spinach
52
41
  #
53
42
  # @api public
54
43
  def run
55
- Spinach.hooks.run_before_feature data
56
- if Spinach.find_feature_steps(feature_name)
57
- scenarios.each do |scenario|
58
- if !@scenario_line || scenario['line'].to_s == @scenario_line
59
- success = ScenarioRunner.new(feature_name, scenario).run
60
- @failed = true unless success
61
- end
62
- end
44
+ Spinach.hooks.run_before_feature @feature
45
+ if Spinach.find_step_definitions(feature_name)
46
+ run_scenarios!
63
47
  else
64
- Spinach.hooks.run_on_undefined_feature data
65
- @failed = true
48
+ undefined_steps!
66
49
  end
67
- Spinach.hooks.run_after_feature data
50
+ Spinach.hooks.run_after_feature @feature
68
51
  !@failed
69
52
  end
53
+
54
+ private
55
+
56
+ def run_scenarios!
57
+ scenarios.each_with_index do |scenario, current_scenario_index|
58
+ if match(current_scenario_index)
59
+ success = ScenarioRunner.new(scenario).run
60
+ @failed = true unless success
61
+ end
62
+ end
63
+ end
64
+
65
+ def match(current_scenario_index)
66
+ return true unless @line
67
+ return false if @line<scenarios[current_scenario_index].line
68
+ next_scenario = scenarios[current_scenario_index+1]
69
+ !next_scenario || @line<next_scenario.line
70
+ end
71
+
72
+ def undefined_steps!
73
+ Spinach.hooks.run_on_undefined_feature @feature
74
+ @failed = true
75
+ end
70
76
  end
71
77
  end
72
78
  end
@@ -3,61 +3,88 @@ module Spinach
3
3
  # A Scenario Runner handles a particular scenario run.
4
4
  #
5
5
  class ScenarioRunner
6
- attr_reader :feature_name, :data
7
-
8
- # @param [String] feature_name
9
- # The feature name
6
+ # @param [Gherkin::AST::Scenario] scenario
7
+ # The scenario.
10
8
  #
11
- # @param [Hash] data
12
- # The parsed feature data.
9
+ # @api public
10
+ def initialize(scenario)
11
+ @scenario = scenario
12
+ end
13
+
14
+ # @return [Gherkin::AST::Feature>]
15
+ # The feature containing the scenario.
13
16
  #
14
17
  # @api public
15
- def initialize(feature_name, data)
16
- @feature_name = feature_name
17
- @data = data
18
+ def feature
19
+ @scenario.feature
18
20
  end
19
21
 
22
+ # @return [Array<Gherkin::AST::Step>]
23
+ # An array of steps.
24
+ #
25
+ # @api public
20
26
  def steps
21
- @steps ||= data['steps']
27
+ feature.background_steps + @scenario.steps
22
28
  end
23
29
 
24
30
  # @return [FeatureSteps]
25
- # The feature object used to run this scenario.
31
+ # The step definitions for the current feature.
26
32
  #
27
33
  # @api public
28
- def feature_steps
29
- @feature_steps ||= Spinach.find_feature_steps(feature_name).new
34
+ def step_definitions
35
+ @step_definitions ||= Spinach.find_step_definitions(feature.name).new
30
36
  end
31
37
 
32
- # Runs this scenario
33
- # @return [True, False]
34
- # true if this scenario succeeded, false if not
38
+ # Runs the scenario, capturing any exception, and running the
39
+ # corresponding hooks.
40
+ #
41
+ # @return [true, false]
42
+ # Whether the scenario succeeded or not.
43
+ #
44
+ # @api public
35
45
  def run
36
- Spinach.hooks.run_before_scenario data
37
- steps.each do |step|
38
- Spinach.hooks.run_before_step step
39
- unless @exception
40
- begin
41
- step_location = feature_steps.execute_step(step['name'])
42
- Spinach.hooks.run_on_successful_step step, step_location
43
- rescue *Spinach.config[:failure_exceptions] => e
44
- @exception = e
45
- Spinach.hooks.run_on_failed_step step, @exception, step_location
46
- rescue Spinach::StepNotDefinedException => e
47
- @exception = e
48
- Spinach.hooks.run_on_undefined_step step, @exception
49
- rescue Exception => e
50
- @exception = e
51
- Spinach.hooks.run_on_error_step step, @exception, step_location
46
+ Spinach.hooks.run_before_scenario @scenario
47
+ scenario_run = false
48
+ Spinach.hooks.run_around_scenario @scenario do
49
+ scenario_run = true
50
+ steps.each do |step|
51
+ Spinach.hooks.run_before_step step
52
+
53
+ if @exception
54
+ Spinach.hooks.run_on_skipped_step step
55
+ else
56
+ run_step(step)
52
57
  end
53
- else
54
- Spinach.hooks.run_on_skipped_step step
58
+
59
+ Spinach.hooks.run_after_step step
55
60
  end
56
- Spinach.hooks.run_after_step step
57
61
  end
58
- Spinach.hooks.run_after_scenario data
62
+ raise "around_scenario hooks *must* yield" if !scenario_run && !@exception
63
+ Spinach.hooks.run_after_scenario @scenario
59
64
  !@exception
60
65
  end
66
+
67
+ # Runs a particular step.
68
+ #
69
+ # @param [Gherkin::AST::Step] step
70
+ # The step to be run.
71
+ #
72
+ # @api semipublic
73
+ def run_step(step)
74
+ step_location = step_definitions.step_location_for(step.name)
75
+ step_definitions.execute(step)
76
+ Spinach.hooks.run_on_successful_step step, step_location
77
+ rescue *Spinach.config[:failure_exceptions] => e
78
+ @exception = e
79
+ Spinach.hooks.run_on_failed_step step, @exception, step_location
80
+ rescue Spinach::StepNotDefinedException => e
81
+ @exception = e
82
+ Spinach.hooks.run_on_undefined_step step, @exception
83
+ rescue Exception => e
84
+ @exception = e
85
+ Spinach.hooks.run_on_error_step step, @exception, step_location
86
+ end
87
+
61
88
  end
62
89
  end
63
90
  end
@@ -0,0 +1,12 @@
1
+ module Spinach
2
+ class Scenario
3
+ attr_accessor :line
4
+ attr_accessor :name, :steps, :tags, :feature
5
+
6
+ def initialize(feature)
7
+ @feature = feature
8
+ @steps = []
9
+ @tags = []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Spinach
2
+ class Step
3
+ attr_accessor :line
4
+ attr_accessor :name, :keyword, :scenario
5
+
6
+ def initialize(scenario)
7
+ @scenario = scenario
8
+ end
9
+ end
10
+ end
@@ -1,4 +1,4 @@
1
1
  module Spinach
2
2
  # Spinach version.
3
- VERSION = "0.1.5.4"
3
+ VERSION = "0.2.0.1"
4
4
  end
data/spinach.gemspec CHANGED
@@ -9,18 +9,18 @@ Gem::Specification.new do |gem|
9
9
  gem.summary = %q{Spinach is a BDD framework on top of gherkin}
10
10
  gem.homepage = "http://github.com/codegram/spinach"
11
11
 
12
- gem.add_runtime_dependency 'gherkin', '2.5.4'
12
+ gem.add_runtime_dependency 'gherkin-ruby', '~> 0.0.2'
13
13
  gem.add_runtime_dependency 'colorize'
14
14
  gem.add_development_dependency 'rake'
15
15
  gem.add_development_dependency 'mocha'
16
16
  gem.add_development_dependency 'sinatra'
17
17
  gem.add_development_dependency 'capybara'
18
- gem.add_development_dependency 'aruba'
18
+ gem.add_development_dependency 'open4'
19
19
  gem.add_development_dependency 'pry'
20
20
  gem.add_development_dependency 'simplecov'
21
21
  gem.add_development_dependency 'rspec'
22
22
  gem.add_development_dependency 'fakefs'
23
- gem.add_development_dependency 'minitest'
23
+ gem.add_development_dependency 'minitest', '~> 2.8.0'
24
24
  gem.add_development_dependency 'turn'
25
25
 
26
26
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ module Spinach
4
+ describe Background do
5
+ end
6
+ end
@@ -27,6 +27,19 @@ describe Spinach::FeatureSteps::Capybara do
27
27
  end.new
28
28
  end
29
29
 
30
+ let(:parsed_feature) { Spinach::Parser.new("""
31
+ Feature: A test feature
32
+ Scenario: A test scenario
33
+ Given Hello
34
+ Then Goodbye
35
+
36
+ @javascript
37
+ Scenario: Another test scenario
38
+ Given Hello
39
+ Then Goodbye
40
+ """).parse
41
+ }
42
+
30
43
  it 'includes capybara into all features' do
31
44
  @feature.kind_of? Capybara
32
45
  end
@@ -37,22 +50,26 @@ describe Spinach::FeatureSteps::Capybara do
37
50
  end
38
51
 
39
52
  it 'resets the capybara session after each scenario' do
40
- @feature_runner = Spinach::Runner::FeatureRunner.new(
41
- 'a_feature.feature')
53
+ @feature_runner = Spinach::Runner::FeatureRunner.new(parsed_feature)
42
54
 
43
- @feature_runner.stubs(data: Spinach::Parser.new('
44
- Feature: A test feature
45
- Scenario: A test scenario
46
- Given Hello
47
- Then Goodbye
48
- Scenario: Another test scenario
49
- Given Hello
50
- Then Goodbye
51
- ').parse).at_least_once
55
+ Capybara.current_session.expects(:reset!).at_least_once
52
56
 
53
- Spinach::Runner::ScenarioRunner.any_instance.stubs(feature_steps: @feature)
57
+ @feature_runner.run
58
+ end
54
59
 
55
- Capybara.current_session.expects(:reset!).at_least_once
60
+ it 'resets the javascript driver after each scenario' do
61
+ @feature_runner = Spinach::Runner::FeatureRunner.new(parsed_feature)
62
+
63
+ Capybara.expects(:use_default_driver).at_least(2)
64
+
65
+ @feature_runner.run
66
+ end
67
+
68
+ it 'changes the javascript driver when an scenario has the @javascript tag' do
69
+ @feature_runner = Spinach::Runner::FeatureRunner.new(parsed_feature)
70
+
71
+ Capybara.expects(:javascript_driver).at_least(1)
72
+ Capybara.expects(:current_driver=).at_least(1)
56
73
 
57
74
  @feature_runner.run
58
75
  end
@@ -18,7 +18,19 @@ describe Spinach::Cli do
18
18
  end
19
19
  end
20
20
 
21
- describe 'backtrace' do
21
+ describe 'features_path' do
22
+ %w{-f --features_path}.each do |opt|
23
+ it 'sets the given features_path' do
24
+ config = Spinach::Config.new
25
+ Spinach.stubs(:config).returns(config)
26
+ cli = Spinach::Cli.new([opt,"custom_path"])
27
+ cli.options
28
+ config.features_path.must_equal 'custom_path'
29
+ end
30
+ end
31
+ end
32
+
33
+ describe 'generate' do
22
34
  %w{-g --generate}.each do |opt|
23
35
  it 'inits the generator if #{opt}' do
24
36
  Spinach::Generators.expects(:bind)
@@ -40,6 +52,39 @@ describe Spinach::Cli do
40
52
  end
41
53
  end
42
54
  end
55
+
56
+ describe 'config_path' do
57
+ %w{-c --config_path}.each do |opt|
58
+ it "sets the config path" do
59
+ config = Spinach::Config.new
60
+ Spinach.stubs(:config).returns(config)
61
+ cli = Spinach::Cli.new([opt, 'config_file'])
62
+ config.expects(:parse_from_file)
63
+ cli.options
64
+ config.config_path.must_equal 'config_file'
65
+ end
66
+
67
+ it "gets overriden by the other cli options" do
68
+ config = Spinach::Config.new
69
+ Spinach.stubs(:config).returns(config)
70
+ YAML.stubs(:load_file).returns({features_path: 'my_path'})
71
+ cli = Spinach::Cli.new(['-f', 'another_path', opt, 'config_file'])
72
+ cli.options
73
+ config.features_path.must_equal 'another_path'
74
+ end
75
+ end
76
+ end
77
+
78
+ describe 'undefined option' do
79
+ %w{-lorem --ipsum}.each do |opt|
80
+ it 'exits and outputs error message with #{opt}' do
81
+ cli = Spinach::Cli.new([opt])
82
+ cli.expects(:exit)
83
+ cli.expects(:puts).with("Invalid option: #{opt}")
84
+ options = cli.options
85
+ end
86
+ end
87
+ end
43
88
  end
44
89
 
45
90
  describe '#init_reporter' do