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
@@ -5,13 +5,13 @@ class UndefinedFeatureReporting < Spinach::FeatureSteps
|
|
5
5
|
include Integration::SpinachRunner
|
6
6
|
|
7
7
|
Given "I've written a feature but not its steps" do
|
8
|
-
write_file('features/feature_without_steps.feature',
|
9
|
-
|
8
|
+
write_file('features/feature_without_steps.feature', """
|
9
|
+
Feature: Feature without steps
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
Scenario: A scenario without steps
|
12
|
+
Given I have no steps
|
13
|
+
Then I should do nothing
|
14
|
+
""")
|
15
15
|
end
|
16
16
|
|
17
17
|
When "I run spinach" do
|
@@ -19,6 +19,6 @@ class UndefinedFeatureReporting < Spinach::FeatureSteps
|
|
19
19
|
end
|
20
20
|
|
21
21
|
Then "I should see a message telling me that there's an undefined feature" do
|
22
|
-
|
22
|
+
@stderr.must_match /Undefined features.*1/
|
23
23
|
end
|
24
24
|
end
|
@@ -6,13 +6,13 @@ class RSpecCompatibility < Spinach::FeatureSteps
|
|
6
6
|
include Integration::ErrorReporting
|
7
7
|
|
8
8
|
Given "I have a feature with some failed expectations" do
|
9
|
-
write_file('features/feature_with_failures.feature',
|
10
|
-
|
9
|
+
write_file('features/feature_with_failures.feature', """
|
10
|
+
Feature: Feature with failures
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
Scenario: This scenario will fail
|
13
|
+
Given true is false
|
14
|
+
Then remove all the files in my hard drive
|
15
|
+
""")
|
16
16
|
|
17
17
|
write_file('features/steps/failure_feature.rb',
|
18
18
|
'class FeatureWithFailures < Spinach::FeatureSteps
|
@@ -30,7 +30,7 @@ class RSpecCompatibility < Spinach::FeatureSteps
|
|
30
30
|
|
31
31
|
When "I run \"spinach\" with rspec" do
|
32
32
|
@feature =
|
33
|
-
|
33
|
+
run_feature "features/#{@feature}.feature", framework: :rspec
|
34
34
|
end
|
35
35
|
|
36
36
|
Then "I should see the failure count along with their messages" do
|
@@ -49,13 +49,13 @@ class RSpecCompatibility < Spinach::FeatureSteps
|
|
49
49
|
Capybara.app = app
|
50
50
|
')
|
51
51
|
|
52
|
-
write_file('features/greeting.feature',
|
53
|
-
|
52
|
+
write_file('features/greeting.feature', """
|
53
|
+
Feature: Greeting
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
Scenario: Greeting
|
56
|
+
Given I am on the front page
|
57
|
+
Then I should see hello world
|
58
|
+
""")
|
59
59
|
|
60
60
|
write_file('features/steps/greeting.rb',
|
61
61
|
'require "spinach/capybara"
|
@@ -74,7 +74,7 @@ class RSpecCompatibility < Spinach::FeatureSteps
|
|
74
74
|
end
|
75
75
|
|
76
76
|
Given 'There should be no error' do
|
77
|
-
last_exit_status.must_equal 0
|
77
|
+
@last_exit_status.must_equal 0
|
78
78
|
end
|
79
79
|
|
80
80
|
end
|
@@ -1,16 +1,16 @@
|
|
1
|
-
|
1
|
+
require_relative 'spinach_runner'
|
2
2
|
|
3
3
|
module Integration
|
4
4
|
module ErrorReporting
|
5
|
-
include
|
5
|
+
include Filesystem
|
6
6
|
|
7
7
|
def check_error_messages(n = 1)
|
8
|
-
|
8
|
+
@stderr.must_match /Failures \(1\)/
|
9
9
|
end
|
10
10
|
|
11
11
|
def check_backtrace(n = 1)
|
12
|
-
|
13
|
-
|
12
|
+
@stderr.must_match /Failures \(1\)/
|
13
|
+
@stderr.must_match /gems.*(minitest|rspec).*assert_equal/
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'open4'
|
3
|
+
|
4
|
+
# The Filesystem module runs commands, captures their output and exit status
|
5
|
+
# and lets the host know about it.
|
6
|
+
#
|
7
|
+
module Filesystem
|
8
|
+
# Writes a file with some contents.
|
9
|
+
#
|
10
|
+
# @param [String] filename
|
11
|
+
# The file name to write.
|
12
|
+
#
|
13
|
+
# @param [String] contents
|
14
|
+
# The contents to include in the file.
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def write_file(filename, contents)
|
18
|
+
in_current_dir do
|
19
|
+
mkdir(File.dirname(filename))
|
20
|
+
File.open(filename, 'w') { |f| f << contents }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Executes a code block within a particular directory.
|
25
|
+
#
|
26
|
+
# @param [Proc] block
|
27
|
+
# The block to execute
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def in_current_dir(&block)
|
31
|
+
mkdir(current_dir)
|
32
|
+
Dir.chdir(current_dir, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Runs a command in the current directory.
|
36
|
+
#
|
37
|
+
# It populates the following instance variables:
|
38
|
+
#
|
39
|
+
# * @stdout - The standard output captured from the process.
|
40
|
+
# * @stderr - The standard error captured from the process.
|
41
|
+
# * @last_exit_status - The process exit status.
|
42
|
+
#
|
43
|
+
# @param [String] command
|
44
|
+
# The command to run.
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def run(command)
|
48
|
+
in_current_dir do
|
49
|
+
pid = Open4.popen4(command) do |pid, stdin, stdout, stderr|
|
50
|
+
@stdout = stdout.readlines.join("\n")
|
51
|
+
@stderr = stderr.readlines.join("\n")
|
52
|
+
end
|
53
|
+
@last_exit_status = pid.exitstatus
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def mkdir(dirname)
|
60
|
+
FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
|
61
|
+
end
|
62
|
+
|
63
|
+
def current_dir
|
64
|
+
File.join(*dirs)
|
65
|
+
end
|
66
|
+
|
67
|
+
def dirs
|
68
|
+
['tmp/fs']
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
|
1
|
+
require_relative 'filesystem'
|
2
2
|
|
3
3
|
module Integration
|
4
4
|
module SpinachRunner
|
5
|
-
include
|
5
|
+
include Filesystem
|
6
6
|
|
7
7
|
def self.included(base)
|
8
8
|
Spinach.hooks.before_scenario do
|
9
|
-
@aruba_timeout_seconds = 6
|
10
9
|
if respond_to?(:in_current_dir)
|
11
10
|
in_current_dir do
|
12
11
|
run "rm -fR rails_app"
|
@@ -16,9 +15,9 @@ module Integration
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def run_feature(command, options={})
|
19
|
-
options[:
|
20
|
-
use_minitest if options[:
|
21
|
-
use_rspec if options[:
|
18
|
+
options[:framework] ||= :minitest
|
19
|
+
use_minitest if options[:framework] == :minitest
|
20
|
+
use_rspec if options[:framework] == :rspec
|
22
21
|
run "../../bin/spinach #{command} #{options[:append]}"
|
23
22
|
end
|
24
23
|
|
data/lib/spinach.rb
CHANGED
@@ -12,6 +12,11 @@ require_relative 'spinach/reporter'
|
|
12
12
|
require_relative 'spinach/cli'
|
13
13
|
require_relative 'spinach/generators'
|
14
14
|
|
15
|
+
require_relative 'spinach/background'
|
16
|
+
require_relative 'spinach/feature'
|
17
|
+
require_relative 'spinach/scenario'
|
18
|
+
require_relative 'spinach/step'
|
19
|
+
|
15
20
|
# Spinach is a BDD framework leveraging the great Gherkin language. This
|
16
21
|
# language is the one used defining features in Cucumber, the BDD framework
|
17
22
|
# Spinach is inspired upon.
|
@@ -50,20 +55,20 @@ module Spinach
|
|
50
55
|
@@hooks ||= Hooks.new
|
51
56
|
end
|
52
57
|
|
53
|
-
# Finds
|
58
|
+
# Finds step definitions given a feature name.
|
54
59
|
#
|
55
60
|
# @param [String] name
|
56
|
-
# The feature name.
|
61
|
+
# The feature name to get the definitions for.
|
57
62
|
#
|
58
|
-
# @return [
|
59
|
-
# the {
|
63
|
+
# @return [StepDefinitions]
|
64
|
+
# the {StepDefinitions} class for the given feature name
|
60
65
|
#
|
61
66
|
# @api public
|
62
|
-
def self.
|
67
|
+
def self.find_step_definitions(name)
|
63
68
|
klass = Spinach::Support.camelize(name)
|
64
69
|
feature_steps.detect do |feature|
|
65
70
|
feature.feature_name.to_s == name.to_s ||
|
66
|
-
|
71
|
+
feature.name == klass
|
67
72
|
end
|
68
73
|
end
|
69
74
|
end
|
data/lib/spinach/capybara.rb
CHANGED
@@ -30,8 +30,14 @@ module Spinach
|
|
30
30
|
include ::Capybara::RSpecMatchers
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
|
+
Spinach.hooks.after_scenario do
|
34
35
|
::Capybara.current_session.reset! if ::Capybara.app
|
36
|
+
::Capybara.use_default_driver
|
37
|
+
end
|
38
|
+
|
39
|
+
Spinach.hooks.on_tag('javascript') do
|
40
|
+
::Capybara.current_driver = ::Capybara.javascript_driver
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
data/lib/spinach/cli.rb
CHANGED
@@ -24,7 +24,7 @@ module Spinach
|
|
24
24
|
features = if @args.any?
|
25
25
|
@args
|
26
26
|
else
|
27
|
-
Dir.glob(File.join
|
27
|
+
Dir.glob(File.join Spinach.config[:features_path], '**', '*.feature')
|
28
28
|
end
|
29
29
|
Spinach::Runner.new(features).run
|
30
30
|
end
|
@@ -61,19 +61,42 @@ module Spinach
|
|
61
61
|
def parse_options
|
62
62
|
reporter_options = {}
|
63
63
|
reporter_options[:backtrace] = false
|
64
|
+
config = {}
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
66
|
+
begin
|
67
|
+
OptionParser.new do |opts|
|
68
|
+
opts.on('-c', '--config_path PATH',
|
69
|
+
'Parse options from file (will get overriden by flags)') do |file|
|
70
|
+
Spinach.config[:config_path] = file
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on('-b', '--backtrace',
|
74
|
+
'Show backtrace of errors') do |show_backtrace|
|
75
|
+
reporter_options[:backtrace] = show_backtrace
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on('-g', '--generate',
|
79
|
+
'Auto-generate the feeature steps files') do
|
80
|
+
Spinach::Generators.bind
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on_tail('--version', 'Show version') do
|
84
|
+
puts Spinach::VERSION
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on('-f', '--features_path PATH',
|
89
|
+
'Path where your features will be searched for') do |path|
|
90
|
+
config[:features_path] = path
|
91
|
+
end
|
92
|
+
end.parse!(@args)
|
93
|
+
|
94
|
+
Spinach.config.parse_from_file
|
95
|
+
config.each{|k,v| Spinach.config[k] = v}
|
96
|
+
rescue OptionParser::ParseError => exception
|
97
|
+
puts exception.message.capitalize
|
98
|
+
exit 1
|
99
|
+
end
|
77
100
|
|
78
101
|
{reporter: reporter_options}
|
79
102
|
end
|
data/lib/spinach/config.rb
CHANGED
@@ -20,29 +20,40 @@ module Spinach
|
|
20
20
|
# to run.
|
21
21
|
#
|
22
22
|
class Config
|
23
|
-
attr_writer :step_definitions_path, :default_reporter, :support_path,
|
24
|
-
:failure_exceptions
|
23
|
+
attr_writer :features_path, :step_definitions_path, :default_reporter, :support_path,
|
24
|
+
:failure_exceptions, :config_path
|
25
|
+
|
26
|
+
# The "features path" holds the place where your features will be
|
27
|
+
# searched for. Defaults to 'features'
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# The features path.
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def features_path
|
34
|
+
@features_path || 'features'
|
35
|
+
end
|
25
36
|
|
26
37
|
# The "step definitions path" holds the place where your feature step
|
27
|
-
# classes will be searched for. Defaults to '
|
38
|
+
# classes will be searched for. Defaults to '#{features_path}/steps'
|
28
39
|
#
|
29
40
|
# @return [String]
|
30
41
|
# The step definitions path.
|
31
42
|
#
|
32
43
|
# @api public
|
33
44
|
def step_definitions_path
|
34
|
-
@step_definitions_path ||
|
45
|
+
@step_definitions_path || "#{self.features_path}/steps"
|
35
46
|
end
|
36
47
|
|
37
48
|
# The "support path" helds the place where you can put your configuration
|
38
|
-
# files.
|
49
|
+
# files. Defaults to '#{features_path}/support'
|
39
50
|
#
|
40
51
|
# @return [String]
|
41
52
|
# The support file path.
|
42
53
|
#
|
43
54
|
# @api public
|
44
55
|
def support_path
|
45
|
-
@support_path ||
|
56
|
+
@support_path || "#{self.features_path}/support"
|
46
57
|
end
|
47
58
|
|
48
59
|
# Allows you to read the config object using a hash-like syntax.
|
@@ -85,5 +96,28 @@ module Spinach
|
|
85
96
|
def failure_exceptions
|
86
97
|
@failure_exceptions ||= []
|
87
98
|
end
|
99
|
+
|
100
|
+
# It allows you to set a config file to parse for all the other options to be set
|
101
|
+
#
|
102
|
+
# @return [String]
|
103
|
+
# The config file name
|
104
|
+
#
|
105
|
+
def config_path
|
106
|
+
@config_path ||= 'config/spinach.yml'
|
107
|
+
end
|
108
|
+
|
109
|
+
# Parse options from the config file
|
110
|
+
#
|
111
|
+
# @return [Boolean]
|
112
|
+
# If the config was parsed from the file
|
113
|
+
#
|
114
|
+
def parse_from_file
|
115
|
+
parsed_opts = YAML.load_file(config_path)
|
116
|
+
parsed_opts.delete_if{|k| k.to_s == 'config_path'}
|
117
|
+
parsed_opts.each_pair{|k,v| self[k] = v}
|
118
|
+
true
|
119
|
+
rescue Errno::ENOENT
|
120
|
+
false
|
121
|
+
end
|
88
122
|
end
|
89
123
|
end
|
data/lib/spinach/dsl.rb
CHANGED
@@ -65,23 +65,26 @@ module Spinach
|
|
65
65
|
module InstanceMethods
|
66
66
|
# Executes a given step.
|
67
67
|
#
|
68
|
-
# @param [String] step
|
69
|
-
# The step name to execute.
|
70
|
-
#
|
71
|
-
# @return [String]
|
72
|
-
# The file and line where the step was defined.
|
73
|
-
#
|
74
68
|
# @api public
|
75
|
-
def
|
76
|
-
underscored_step = Spinach::Support.underscore(step)
|
77
|
-
location = nil
|
69
|
+
def execute(step)
|
70
|
+
underscored_step = Spinach::Support.underscore(step.name)
|
78
71
|
if self.respond_to?(underscored_step)
|
79
|
-
location = method(underscored_step).source_location
|
80
72
|
self.send(underscored_step)
|
81
73
|
else
|
82
74
|
raise Spinach::StepNotDefinedException.new(step)
|
83
75
|
end
|
84
|
-
|
76
|
+
end
|
77
|
+
|
78
|
+
# Gets current step source location.
|
79
|
+
#
|
80
|
+
# @param [String] step
|
81
|
+
# The step name to execute.
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
# The file and line where the step was defined.
|
85
|
+
def step_location_for(step)
|
86
|
+
underscored_step = Spinach::Support.underscore(step)
|
87
|
+
location = method(underscored_step).source_location if self.respond_to?(underscored_step)
|
85
88
|
end
|
86
89
|
|
87
90
|
# @return [String]
|