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.
- 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]
|