spinach 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Readme.md +3 -0
- data/bin/spinach +3 -1
- data/features/steps/error_reporting.rb +4 -8
- data/features/steps/exit_status.rb +0 -1
- data/features/steps/feature_name_guessing.rb +1 -2
- data/features/support/spinach_runner.rb +0 -2
- data/lib/spinach/capybara.rb +8 -8
- data/lib/spinach/cli.rb +30 -18
- data/lib/spinach/config.rb +29 -10
- data/lib/spinach/dsl.rb +45 -21
- data/lib/spinach/exceptions.rb +21 -8
- data/lib/spinach/feature.rb +8 -4
- data/lib/spinach/parser.rb +13 -7
- data/lib/spinach/reporter/stdout.rb +251 -53
- data/lib/spinach/reporter.rb +43 -26
- data/lib/spinach/runner/feature.rb +39 -26
- data/lib/spinach/runner/scenario.rb +43 -28
- data/lib/spinach/runner.rb +40 -20
- data/lib/spinach/support.rb +8 -7
- data/lib/spinach/version.rb +2 -1
- data/lib/spinach.rb +17 -9
- data/spinach.gemspec +2 -1
- data/test/spinach/capybara_test.rb +32 -3
- data/test/spinach/cli_test.rb +32 -11
- data/test/spinach/config_test.rb +9 -6
- data/test/spinach/dsl_test.rb +8 -1
- data/test/spinach/feature_test.rb +14 -11
- data/test/spinach/parser_test.rb +33 -20
- data/test/spinach/reporter/stdout_test.rb +364 -103
- data/test/spinach/reporter_test.rb +145 -20
- data/test/spinach/runner/feature_test.rb +26 -51
- data/test/spinach/runner/scenario_test.rb +64 -35
- data/test/spinach/runner_test.rb +41 -32
- data/test/spinach/support_test.rb +2 -10
- data/test/spinach_test.rb +14 -14
- data/test/test_helper.rb +13 -5
- metadata +36 -28
- data/examples/steps/user_logs_in.rb +0 -23
- data/examples/user_logs_in.feature +0 -6
- data/examples/user_logs_in.rb +0 -23
data/Gemfile
CHANGED
data/Readme.md
CHANGED
@@ -10,3 +10,6 @@ Spinach is a BDD framework on top of gherkin
|
|
10
10
|
![](http://www.80stees.com/images/products/Popeye_the_Sailor_Man_I_Popeye_Spinach-T-link.jpg)
|
11
11
|
|
12
12
|
*Popeye the Sailor*
|
13
|
+
|
14
|
+
# Build status
|
15
|
+
[![Build Status](https://secure.travis-ci.org/codegram/spinach.png)](http://travis-ci.org/codegram/spinach)
|
data/bin/spinach
CHANGED
@@ -32,7 +32,7 @@ Feature "Error reporting" do
|
|
32
32
|
|
33
33
|
Then 'I should see the error count along with their messages' do
|
34
34
|
check_error_messages
|
35
|
-
|
35
|
+
all_stderr.wont_match /gems.*minitest.*assert_equal/
|
36
36
|
end
|
37
37
|
|
38
38
|
Then 'I should see the error count along with their messages and backtrace' do
|
@@ -41,16 +41,12 @@ Feature "Error reporting" do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
44
|
-
|
45
44
|
def check_error_messages
|
46
|
-
|
47
|
-
all_stdout.must_match /errors.*This scenario will fail.*line 4/
|
45
|
+
all_stderr.must_match /Failures \(1\)/
|
48
46
|
end
|
49
47
|
|
50
48
|
def check_backtrace
|
51
|
-
|
52
|
-
|
53
|
-
all_stdout.must_match /gems.*minitest.*assert_equal/
|
49
|
+
all_stderr.must_match /Failures \(1\)/
|
50
|
+
all_stderr.must_match /gems.*minitest.*assert_equal/
|
54
51
|
end
|
55
|
-
|
56
52
|
end
|
data/lib/spinach/capybara.rb
CHANGED
@@ -4,8 +4,7 @@ require_relative 'feature'
|
|
4
4
|
|
5
5
|
module Spinach
|
6
6
|
class Feature
|
7
|
-
|
8
|
-
# Spinach's capybara module integrates capybara into all features
|
7
|
+
# Spinach's capybara module makes Capybara DSL available in all features.
|
9
8
|
#
|
10
9
|
# @example
|
11
10
|
# require 'spinach/capybara'
|
@@ -16,20 +15,21 @@ module Spinach
|
|
16
15
|
# end
|
17
16
|
#
|
18
17
|
module Capybara
|
18
|
+
# Enhances a Feature with Capybara goodness.
|
19
|
+
#
|
20
|
+
# @param [Class] base
|
21
|
+
# The host class.
|
22
|
+
#
|
23
|
+
# @api public
|
19
24
|
def self.included(base)
|
20
25
|
base.class_eval do
|
21
26
|
include ::Capybara::DSL
|
22
|
-
include InstanceMethods
|
23
27
|
|
24
|
-
|
28
|
+
after_scenario do
|
25
29
|
::Capybara.current_session.reset! if ::Capybara.app
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
29
|
-
module InstanceMethods
|
30
|
-
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
34
|
-
|
35
|
-
Spinach::Feature.send(:include, Spinach::Feature::Capybara)
|
data/lib/spinach/cli.rb
CHANGED
@@ -2,21 +2,24 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Spinach
|
4
4
|
# The cli is a class responsible of handling all the command line interface
|
5
|
-
# logic
|
5
|
+
# logic.
|
6
6
|
#
|
7
7
|
class Cli
|
8
8
|
# @param [Array<String>] arguments
|
9
|
-
#
|
9
|
+
# The command line arguments
|
10
|
+
#
|
11
|
+
# @api public
|
10
12
|
def initialize(args = ARGV)
|
11
13
|
@args = args
|
12
14
|
end
|
13
15
|
|
14
|
-
# Runs all the
|
16
|
+
# Runs all the features.
|
17
|
+
#
|
18
|
+
# @return [true, false]
|
19
|
+
# The exit status - true for success, false for failure.
|
15
20
|
#
|
16
|
-
# @
|
17
|
-
# the exit status - true for success, false for failure
|
21
|
+
# @api public
|
18
22
|
def run
|
19
|
-
init_reporter
|
20
23
|
parse_options
|
21
24
|
features = if @args.any?
|
22
25
|
@args
|
@@ -26,42 +29,51 @@ module Spinach
|
|
26
29
|
Spinach::Runner.new(features).run
|
27
30
|
end
|
28
31
|
|
29
|
-
# Inits the reporter with a default one
|
32
|
+
# Inits the reporter with a default one.
|
33
|
+
#
|
34
|
+
# @api public
|
30
35
|
def init_reporter
|
31
|
-
|
36
|
+
reporter =
|
32
37
|
Spinach::Reporter::Stdout.new(options[:reporter])
|
38
|
+
Spinach.config.default_reporter = reporter
|
39
|
+
reporter.bind
|
33
40
|
end
|
34
41
|
|
35
|
-
#
|
42
|
+
# @return [Hash]
|
43
|
+
# A hash of options separated by its type.
|
36
44
|
#
|
37
45
|
# @example
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# }
|
46
|
+
# Cli.new.options
|
47
|
+
# # => { reporter: { backtrace: true } }
|
41
48
|
#
|
42
|
-
# @
|
49
|
+
# @api public
|
43
50
|
def options
|
44
51
|
@options ||= parse_options
|
45
52
|
end
|
46
53
|
|
47
|
-
|
54
|
+
private
|
48
55
|
|
56
|
+
# Parses the arguments into options.
|
57
|
+
#
|
58
|
+
# @return [Hash]
|
59
|
+
# A hash of options separated by its type.
|
60
|
+
#
|
61
|
+
# @api private
|
49
62
|
def parse_options
|
50
63
|
reporter_options = {}
|
51
64
|
reporter_options[:backtrace] = false
|
52
65
|
|
53
66
|
OptionParser.new do |opts|
|
54
|
-
opts.on('-b', '--backtrace',
|
67
|
+
opts.on('-b', '--backtrace', 'Show backtrace of errors') do |v|
|
55
68
|
reporter_options[:backtrace] = v
|
56
69
|
end
|
57
|
-
opts.on_tail('--version',
|
70
|
+
opts.on_tail('--version', 'Show version') do
|
58
71
|
puts Spinach::VERSION
|
59
72
|
exit
|
60
73
|
end
|
61
74
|
end.parse!(@args)
|
75
|
+
|
62
76
|
{reporter: reporter_options}
|
63
77
|
end
|
64
|
-
|
65
|
-
|
66
78
|
end
|
67
79
|
end
|
data/lib/spinach/config.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
module Spinach
|
2
|
-
|
3
2
|
# Accesses spinach config. Allows you to configure several runtime options,
|
4
3
|
# like the step definitions path.
|
5
4
|
#
|
6
|
-
# @return [
|
7
|
-
#
|
5
|
+
# @return [Config]
|
6
|
+
# The config object
|
8
7
|
#
|
9
8
|
# @example
|
10
9
|
# Spinach.config[:step_definitions_path]
|
@@ -12,6 +11,7 @@ module Spinach
|
|
12
11
|
# Spinach.config[:step_definitions_path] = 'integration/steps'
|
13
12
|
# # => 'integration/steps'
|
14
13
|
#
|
14
|
+
# @api public
|
15
15
|
def self.config
|
16
16
|
@config ||= Config.new
|
17
17
|
end
|
@@ -22,16 +22,24 @@ module Spinach
|
|
22
22
|
class Config
|
23
23
|
attr_writer :step_definitions_path, :default_reporter, :support_path
|
24
24
|
|
25
|
-
# The "step definitions path"
|
25
|
+
# The "step definitions path" holds the place where your feature classes
|
26
26
|
# will be searched for. Defaults to 'features/steps'
|
27
27
|
#
|
28
|
+
# @return [String]
|
29
|
+
# The step definitions path.
|
30
|
+
#
|
31
|
+
# @api public
|
28
32
|
def step_definitions_path
|
29
33
|
@step_definitions_path || 'features/steps'
|
30
34
|
end
|
31
35
|
|
32
|
-
# The "support path" helds the place where
|
33
|
-
# files
|
36
|
+
# The "support path" helds the place where you can put your configuration
|
37
|
+
# files.
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
# The support file path.
|
34
41
|
#
|
42
|
+
# @api public
|
35
43
|
def support_path
|
36
44
|
@support_path || 'features/support'
|
37
45
|
end
|
@@ -46,22 +54,33 @@ module Spinach
|
|
46
54
|
|
47
55
|
# Allows you to read the config object using a hash-like syntax.
|
48
56
|
#
|
57
|
+
# @param [String] attribute
|
58
|
+
# The attribute to fetch.
|
59
|
+
#
|
49
60
|
# @example
|
50
61
|
# Spinach.config[:step_definitions_path]
|
51
|
-
#
|
62
|
+
# # => 'features/steps'
|
52
63
|
#
|
64
|
+
# @api public
|
53
65
|
def [](attribute)
|
54
66
|
self.send(attribute)
|
55
67
|
end
|
56
68
|
|
57
|
-
# Allows you to set config properties using a hash-like syntax
|
69
|
+
# Allows you to set config properties using a hash-like syntax.
|
70
|
+
#
|
71
|
+
# @param [#to_s] attribute
|
72
|
+
# The attribute to set.
|
73
|
+
#
|
74
|
+
# @param [Object] value
|
75
|
+
# The value to set the attribute to.
|
58
76
|
#
|
59
77
|
# @example
|
60
78
|
# Spinach.config[:step_definitions_path] = 'integration/steps'
|
61
79
|
# # => 'integration/steps'
|
62
80
|
#
|
63
|
-
|
64
|
-
|
81
|
+
# @api public
|
82
|
+
def []=(attribute, value)
|
83
|
+
self.send("#{attribute}=", value)
|
65
84
|
end
|
66
85
|
end
|
67
86
|
end
|
data/lib/spinach/dsl.rb
CHANGED
@@ -1,34 +1,53 @@
|
|
1
|
+
require 'hooks'
|
2
|
+
|
1
3
|
module Spinach
|
2
|
-
# Spinach DSL aims to provide an easy way to define steps and
|
3
|
-
#
|
4
|
+
# Spinach DSL aims to provide an easy way to define steps and hooks into your
|
5
|
+
# feature classes.
|
4
6
|
#
|
5
7
|
module DSL
|
6
|
-
|
8
|
+
# @param [Class] base
|
9
|
+
# The host class.
|
10
|
+
#
|
11
|
+
# @api public
|
7
12
|
def self.included(base)
|
8
13
|
base.class_eval do
|
9
14
|
include InstanceMethods
|
10
15
|
extend ClassMethods
|
16
|
+
include Hooks
|
17
|
+
|
18
|
+
define_hook :before
|
19
|
+
define_hook :after
|
20
|
+
define_hook :before_scenario
|
21
|
+
define_hook :after_scenario
|
22
|
+
define_hook :before_step
|
23
|
+
define_hook :after_step
|
24
|
+
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
28
|
+
# Class methods to extend the host class.
|
29
|
+
#
|
14
30
|
module ClassMethods
|
15
|
-
|
31
|
+
# The feature name.
|
32
|
+
attr_reader :feature_name
|
16
33
|
# Defines an action to perform given a particular step literal.
|
17
34
|
#
|
18
|
-
# @param [String] step
|
19
|
-
# The step
|
35
|
+
# @param [String] step
|
36
|
+
# The step name.
|
37
|
+
#
|
20
38
|
# @param [Proc] block
|
21
|
-
#
|
39
|
+
# Action to perform in that step.
|
22
40
|
#
|
23
41
|
# @example
|
24
|
-
# class MyFeature
|
42
|
+
# class MyFeature < Spinach::Feature
|
25
43
|
# When "I go to the toilet" do
|
26
44
|
# @sittin_on_the_toilet.must_equal true
|
27
45
|
# end
|
28
46
|
# end
|
29
47
|
#
|
30
|
-
|
31
|
-
|
48
|
+
# @api public
|
49
|
+
def Given(step, &block)
|
50
|
+
define_method(step, &block)
|
32
51
|
end
|
33
52
|
|
34
53
|
alias_method :When, :Given
|
@@ -36,39 +55,44 @@ module Spinach
|
|
36
55
|
alias_method :And, :Given
|
37
56
|
alias_method :But, :Given
|
38
57
|
|
39
|
-
#
|
58
|
+
# Sets the feature name.
|
59
|
+
#
|
60
|
+
# @param [String] name
|
61
|
+
# The name.
|
40
62
|
#
|
41
63
|
# @example
|
42
64
|
# class MyFeature < Spinach::Feature
|
43
65
|
# feature "Satisfy needs"
|
44
66
|
# end
|
45
67
|
#
|
68
|
+
# @api public
|
46
69
|
def feature(name)
|
47
70
|
@feature_name = name
|
48
71
|
end
|
49
|
-
|
50
|
-
# @return [String] this feature's name
|
51
|
-
#
|
52
|
-
attr_reader :feature_name
|
53
72
|
end
|
54
73
|
|
74
|
+
# Instance methods to include in the host class.
|
75
|
+
#
|
55
76
|
module InstanceMethods
|
56
|
-
|
57
|
-
# Execute a given step.
|
77
|
+
# Executes a given step.
|
58
78
|
#
|
59
79
|
# @param [String] step
|
60
|
-
#
|
80
|
+
# The step name to execute.
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
# The file and line where the step was defined.
|
61
84
|
#
|
85
|
+
# @api public
|
62
86
|
def execute_step(step)
|
63
87
|
if self.respond_to?(step)
|
64
88
|
self.send(step)
|
65
89
|
else
|
66
|
-
raise Spinach::StepNotDefinedException.new(
|
67
|
-
self, step
|
68
|
-
)
|
90
|
+
raise Spinach::StepNotDefinedException.new(self, step)
|
69
91
|
end
|
70
92
|
end
|
71
93
|
|
94
|
+
# @return [String]
|
95
|
+
# The feature name.
|
72
96
|
def name
|
73
97
|
self.class.feature_name
|
74
98
|
end
|
data/lib/spinach/exceptions.rb
CHANGED
@@ -1,28 +1,41 @@
|
|
1
1
|
module Spinach
|
2
|
-
# This class represents the
|
2
|
+
# This class represents the exception raised when Spinach can't find a class
|
3
3
|
# for a feature.
|
4
|
+
#
|
4
5
|
class FeatureNotFoundException < StandardError
|
6
|
+
# @param [Array] options
|
7
|
+
# An array consisting of the missing class and the feature.
|
8
|
+
#
|
9
|
+
# @api pulic
|
5
10
|
def initialize(options)
|
6
|
-
@
|
7
|
-
@feature = options.last
|
11
|
+
@missing_class, @feature = options
|
8
12
|
end
|
9
13
|
|
14
|
+
# @return [String]
|
15
|
+
# A custom message when a feature class is not found.
|
16
|
+
#
|
17
|
+
# @api public
|
10
18
|
def message
|
11
|
-
"Could not find class for `#{@feature}` feature. Please create a #{@
|
19
|
+
"Could not find class for `#{@feature}` feature. Please create a #{@missing_class}.rb file at #{Spinach.config[:step_definitions_path]}"
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
15
|
-
# This class represents the
|
16
|
-
# Scenario.
|
23
|
+
# This class represents the exception raised when Spinach can't find a step
|
24
|
+
# for a {Scenario}.
|
17
25
|
#
|
18
26
|
class StepNotDefinedException < StandardError
|
19
|
-
|
20
27
|
attr_reader :feature, :step
|
21
28
|
|
29
|
+
# @param [Feature] feature
|
30
|
+
# The container feature.
|
31
|
+
#
|
32
|
+
# @param [Hash] step
|
33
|
+
# The missing step.
|
34
|
+
#
|
35
|
+
# @api pulic
|
22
36
|
def initialize(feature, step)
|
23
37
|
@feature = feature
|
24
38
|
@step = step
|
25
39
|
end
|
26
40
|
end
|
27
|
-
|
28
41
|
end
|
data/lib/spinach/feature.rb
CHANGED
@@ -2,21 +2,25 @@ require 'minitest/spec'
|
|
2
2
|
MiniTest::Spec.new nil
|
3
3
|
|
4
4
|
module Spinach
|
5
|
-
# The feature class is the class
|
5
|
+
# The feature class is the class which all the features must inherit from.
|
6
6
|
#
|
7
7
|
class Feature
|
8
8
|
include DSL
|
9
9
|
include MiniTest::Assertions
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# Registers the feature class for later use.
|
12
|
+
#
|
13
|
+
# @param [Class] base
|
14
|
+
# The host class.
|
15
|
+
#
|
16
|
+
# @api public
|
14
17
|
def self.inherited(base)
|
15
18
|
Spinach.features << base
|
16
19
|
end
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
23
|
+
# Syntactic sugar. Define the "Feature do" syntax.
|
20
24
|
Object.send(:define_method, :Feature) do |name, &block|
|
21
25
|
Class.new(Spinach::Feature) do
|
22
26
|
feature name
|
data/lib/spinach/parser.rb
CHANGED
@@ -2,29 +2,35 @@ require 'gherkin'
|
|
2
2
|
require 'gherkin/formatter/json_formatter'
|
3
3
|
|
4
4
|
module Spinach
|
5
|
-
#
|
6
|
-
# needed from the plain feature definition.
|
5
|
+
# Parser leverages Gherkin to parse the feature definition.
|
7
6
|
#
|
8
7
|
class Parser
|
9
|
-
|
10
8
|
# @param [String] filename
|
11
|
-
#
|
9
|
+
# The filename to parse.
|
12
10
|
#
|
11
|
+
# @api public
|
13
12
|
def initialize(filename)
|
14
13
|
@filename = filename
|
15
14
|
@formatter = Gherkin::Formatter::JSONFormatter.new(nil)
|
16
15
|
@parser = Gherkin::Parser::Parser.new(@formatter)
|
17
16
|
end
|
18
17
|
|
19
|
-
# Gets the plain text content of the feature file.
|
18
|
+
# Gets the plain text content out of the feature file.
|
19
|
+
#
|
20
20
|
# @return [String]
|
21
|
-
#
|
21
|
+
# The plain feature content.
|
22
22
|
#
|
23
|
+
# @api public
|
23
24
|
def content
|
24
25
|
File.read(@filename)
|
25
26
|
end
|
26
27
|
|
27
|
-
#
|
28
|
+
# Parses the feature file and returns an AST as a Hash.
|
29
|
+
#
|
30
|
+
# @return [Hash]
|
31
|
+
# The parsed Gherkin output.
|
32
|
+
#
|
33
|
+
# @api public
|
28
34
|
def parse
|
29
35
|
@parser.parse(content, @filename, __LINE__-1)
|
30
36
|
@formatter.gherkin_object
|