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.
- data/Gemfile +2 -0
- data/README.markdown +18 -12
- data/features/background.feature +13 -0
- data/features/reporting/show_step_source_location.feature +11 -1
- data/features/steps/automatic_feature_generation.rb +7 -7
- data/features/steps/background.rb +30 -0
- data/features/steps/exit_status.rb +12 -13
- data/features/steps/feature_name_guessing.rb +7 -7
- data/features/steps/reporting/display_run_summary.rb +22 -22
- data/features/steps/reporting/error_reporting.rb +7 -7
- data/features/steps/reporting/show_step_source_location.rb +63 -14
- data/features/steps/reporting/undefined_feature_reporting.rb +7 -7
- data/features/steps/rspec_compatibility.rb +14 -14
- data/features/support/error_reporting.rb +5 -5
- data/features/support/filesystem.rb +71 -0
- data/features/support/spinach_runner.rb +5 -6
- data/lib/spinach.rb +11 -6
- data/lib/spinach/background.rb +11 -0
- data/lib/spinach/capybara.rb +7 -1
- data/lib/spinach/cli.rb +36 -13
- data/lib/spinach/config.rb +40 -6
- data/lib/spinach/dsl.rb +14 -11
- data/lib/spinach/exceptions.rb +1 -1
- data/lib/spinach/feature.rb +16 -0
- data/lib/spinach/frameworks.rb +2 -0
- data/lib/spinach/{suites → frameworks}/minitest.rb +0 -0
- data/lib/spinach/{suites → frameworks}/rspec.rb +0 -0
- data/lib/spinach/generators/feature_generator.rb +12 -23
- data/lib/spinach/generators/step_generator.rb +5 -5
- data/lib/spinach/hookable.rb +6 -4
- data/lib/spinach/hooks.rb +12 -4
- data/lib/spinach/parser.rb +6 -8
- data/lib/spinach/parser/visitor.rb +109 -0
- data/lib/spinach/reporter.rb +10 -6
- data/lib/spinach/reporter/stdout.rb +41 -16
- data/lib/spinach/reporter/stdout/error_reporting.rb +2 -2
- data/lib/spinach/runner.rb +9 -6
- data/lib/spinach/runner/feature_runner.rb +40 -34
- data/lib/spinach/runner/scenario_runner.rb +63 -36
- data/lib/spinach/scenario.rb +12 -0
- data/lib/spinach/step.rb +10 -0
- data/lib/spinach/version.rb +1 -1
- data/spinach.gemspec +3 -3
- data/test/spinach/background_test.rb +6 -0
- data/test/spinach/capybara_test.rb +30 -13
- data/test/spinach/cli_test.rb +46 -1
- data/test/spinach/config_test.rb +39 -0
- data/test/spinach/dsl_test.rb +12 -10
- data/test/spinach/feature_steps_test.rb +3 -3
- data/test/spinach/feature_test.rb +6 -0
- data/test/spinach/{suites → frameworks}/minitest_test.rb +2 -2
- data/test/spinach/generators/feature_generator_test.rb +18 -58
- data/test/spinach/generators/step_generator_test.rb +3 -3
- data/test/spinach/generators_test.rb +12 -10
- data/test/spinach/hookable_test.rb +8 -0
- data/test/spinach/hooks_test.rb +6 -7
- data/test/spinach/parser/visitor_test.rb +173 -0
- data/test/spinach/parser_test.rb +14 -27
- data/test/spinach/reporter/stdout/error_reporting_test.rb +9 -9
- data/test/spinach/reporter/stdout_test.rb +15 -19
- data/test/spinach/reporter_test.rb +15 -0
- data/test/spinach/runner/feature_runner_test.rb +79 -69
- data/test/spinach/runner/scenario_runner_test.rb +118 -92
- data/test/spinach/runner_test.rb +10 -6
- data/test/spinach/scenario_test.rb +6 -0
- data/test/spinach/step_test.rb +6 -0
- data/test/spinach_test.rb +7 -7
- metadata +60 -39
- 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
|
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
|
96
|
+
summary = " #{feature.name} :: #{scenario.name} :: #{full_step step}"
|
97
97
|
if exception.kind_of?(Spinach::StepNotDefinedException)
|
98
98
|
summary.yellow
|
99
99
|
else
|
data/lib/spinach/runner.rb
CHANGED
@@ -54,14 +54,17 @@ module Spinach
|
|
54
54
|
# @api public
|
55
55
|
def run
|
56
56
|
require_dependencies
|
57
|
-
|
57
|
+
require_frameworks
|
58
58
|
|
59
59
|
Spinach.hooks.run_before_run
|
60
60
|
|
61
61
|
successful = true
|
62
62
|
|
63
|
-
filenames.
|
64
|
-
|
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
|
86
|
+
# Requires the test framework support
|
84
87
|
#
|
85
|
-
def
|
86
|
-
require_relative '
|
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
|
-
#
|
8
|
-
|
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
|
-
|
23
|
-
|
24
|
-
#
|
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
|
29
|
-
@
|
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
|
-
@
|
26
|
+
@feature.name
|
38
27
|
end
|
39
28
|
|
40
|
-
# @return [
|
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
|
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
|
56
|
-
if Spinach.
|
57
|
-
|
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
|
-
|
65
|
-
@failed = true
|
48
|
+
undefined_steps!
|
66
49
|
end
|
67
|
-
Spinach.hooks.run_after_feature
|
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
|
-
|
7
|
-
|
8
|
-
# @param [String] feature_name
|
9
|
-
# The feature name
|
6
|
+
# @param [Gherkin::AST::Scenario] scenario
|
7
|
+
# The scenario.
|
10
8
|
#
|
11
|
-
# @
|
12
|
-
|
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
|
16
|
-
@
|
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
|
-
|
27
|
+
feature.background_steps + @scenario.steps
|
22
28
|
end
|
23
29
|
|
24
30
|
# @return [FeatureSteps]
|
25
|
-
# The
|
31
|
+
# The step definitions for the current feature.
|
26
32
|
#
|
27
33
|
# @api public
|
28
|
-
def
|
29
|
-
@
|
34
|
+
def step_definitions
|
35
|
+
@step_definitions ||= Spinach.find_step_definitions(feature.name).new
|
30
36
|
end
|
31
37
|
|
32
|
-
# Runs
|
33
|
-
#
|
34
|
-
#
|
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
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
54
|
-
Spinach.hooks.
|
58
|
+
|
59
|
+
Spinach.hooks.run_after_step step
|
55
60
|
end
|
56
|
-
Spinach.hooks.run_after_step step
|
57
61
|
end
|
58
|
-
|
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
|
data/lib/spinach/step.rb
ADDED
data/lib/spinach/version.rb
CHANGED
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', '
|
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 '
|
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) }
|
@@ -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
|
-
|
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
|
-
|
57
|
+
@feature_runner.run
|
58
|
+
end
|
54
59
|
|
55
|
-
|
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
|
data/test/spinach/cli_test.rb
CHANGED
@@ -18,7 +18,19 @@ describe Spinach::Cli do
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
describe '
|
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
|