spinach 0.0.6 → 0.1.0
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 +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/lib/spinach/runner.rb
CHANGED
@@ -1,22 +1,38 @@
|
|
1
|
+
require 'hooks'
|
2
|
+
|
1
3
|
module Spinach
|
2
|
-
#
|
3
|
-
#
|
4
|
+
# Runner gets the parsed data from the feature and performs the actual calls
|
5
|
+
# to the feature classes.
|
4
6
|
#
|
5
7
|
class Runner
|
8
|
+
include Hooks
|
9
|
+
|
10
|
+
# The feature files to run
|
11
|
+
attr_reader :filenames
|
12
|
+
|
13
|
+
# The default path where the steps are located
|
14
|
+
attr_reader :step_definitions_path
|
15
|
+
|
16
|
+
# The default path where the support files are located
|
17
|
+
attr_reader :support_path
|
18
|
+
|
19
|
+
define_hook :before_run
|
20
|
+
define_hook :after_run
|
6
21
|
|
7
22
|
# Initializes the runner with a parsed feature
|
8
23
|
#
|
9
24
|
# @param [Array<String>] filenames
|
10
|
-
#
|
25
|
+
# A list of feature filenames to run
|
11
26
|
#
|
12
27
|
# @param [Hash] options
|
13
28
|
#
|
14
29
|
# @option options [String] :step_definitions_path
|
15
|
-
# The path in which step definitions are found
|
30
|
+
# The path in which step definitions are found.
|
16
31
|
#
|
17
32
|
# @option options [String] :support_path
|
18
|
-
# The path with the support ruby files
|
33
|
+
# The path with the support ruby files.
|
19
34
|
#
|
35
|
+
# @api public
|
20
36
|
def initialize(filenames, options = {})
|
21
37
|
@filenames = filenames
|
22
38
|
|
@@ -25,13 +41,9 @@ module Spinach
|
|
25
41
|
|
26
42
|
@support_path = options.delete(:support_path ) ||
|
27
43
|
Spinach.config.support_path
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def reporter
|
32
|
-
@reporter ||= Spinach::config.default_reporter
|
33
44
|
end
|
34
45
|
|
46
|
+
# The feature files to run
|
35
47
|
attr_reader :filenames
|
36
48
|
|
37
49
|
# The default path where the steps are located
|
@@ -42,45 +54,53 @@ module Spinach
|
|
42
54
|
|
43
55
|
# Runs this runner and outputs the results in a colorful manner.
|
44
56
|
#
|
57
|
+
# @return [true, false]
|
58
|
+
# Whether the run was succesful.
|
59
|
+
#
|
60
|
+
# @api public
|
45
61
|
def run
|
46
62
|
require_dependencies
|
47
63
|
|
64
|
+
successful = true
|
65
|
+
|
48
66
|
filenames.each do |filename|
|
49
|
-
success = Feature.new(filename
|
50
|
-
|
67
|
+
success = Feature.new(filename).run
|
68
|
+
successful = false unless success
|
51
69
|
end
|
52
|
-
|
53
|
-
|
70
|
+
|
71
|
+
run_hook :after_run, successful
|
72
|
+
|
73
|
+
successful
|
54
74
|
end
|
55
75
|
|
56
|
-
#
|
76
|
+
# Loads step definitions and support files.
|
57
77
|
#
|
78
|
+
# @api public
|
58
79
|
def require_dependencies
|
59
80
|
(support_files + step_definition_files).each do |file|
|
60
81
|
require file
|
61
82
|
end
|
62
83
|
end
|
63
84
|
|
64
|
-
# List of step definition files
|
65
|
-
#
|
66
85
|
# @return [Array<String>] files
|
86
|
+
# The step definition files.
|
67
87
|
#
|
88
|
+
# @api public
|
68
89
|
def step_definition_files
|
69
90
|
Dir.glob(
|
70
91
|
File.expand_path File.join(step_definitions_path, '**', '*.rb')
|
71
92
|
)
|
72
93
|
end
|
73
94
|
|
74
|
-
# List of support files
|
75
|
-
#
|
76
95
|
# @return [Array<String>] files
|
96
|
+
# The support files.
|
77
97
|
#
|
98
|
+
# @api public
|
78
99
|
def support_files
|
79
100
|
Dir.glob(
|
80
101
|
File.expand_path File.join(support_path, '**', '*.rb')
|
81
102
|
)
|
82
103
|
end
|
83
|
-
|
84
104
|
end
|
85
105
|
end
|
86
106
|
|
data/lib/spinach/support.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
|
-
require 'active_support/inflector/methods'
|
2
|
-
|
3
1
|
module Spinach
|
4
|
-
# A module to offer
|
2
|
+
# A module to offer helpers for string mangling.
|
3
|
+
#
|
5
4
|
module Support
|
6
|
-
# A helper to camelize string that proxies ActiveSupport#camelize
|
7
|
-
#
|
8
5
|
# @param [String] name
|
6
|
+
# The name to camelize.
|
9
7
|
#
|
10
|
-
# @
|
8
|
+
# @return [String]
|
9
|
+
# The +name+ in camel case.
|
11
10
|
#
|
12
11
|
# @example
|
13
12
|
# Spinach::Support.camelize('User authentication')
|
14
13
|
# => 'UserAuthentication'
|
14
|
+
#
|
15
|
+
# @api public
|
15
16
|
def self.camelize(name)
|
16
|
-
|
17
|
+
name.to_s.strip.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
data/lib/spinach/version.rb
CHANGED
data/lib/spinach.rb
CHANGED
@@ -9,35 +9,43 @@ require_relative 'spinach/feature'
|
|
9
9
|
require_relative 'spinach/reporter'
|
10
10
|
require_relative 'spinach/cli'
|
11
11
|
|
12
|
-
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# Spinach is a BDD framework leveraging the great Gherkin language. This
|
13
|
+
# language is the one used defining features in Cucumber, the BDD framework
|
14
|
+
# Spinach is inspired upon.
|
15
|
+
#
|
16
|
+
# Its main design goals are:
|
17
|
+
#
|
18
|
+
# * No magic: All features are implemented using normal Ruby classes.
|
19
|
+
# * Reusability: Steps are methods, so they can be reused using modules, a
|
20
|
+
# common, normal practice among rubyists.
|
16
21
|
# * Proper encapsulation: No conflicts between steps from different
|
17
22
|
# scenarios.
|
18
23
|
#
|
19
24
|
module Spinach
|
20
25
|
@@features = []
|
21
26
|
|
22
|
-
# @return [Array<
|
23
|
-
#
|
27
|
+
# @return [Array<Feature>]
|
28
|
+
# All the registered features.
|
24
29
|
#
|
30
|
+
# @api public
|
25
31
|
def self.features
|
26
32
|
@@features
|
27
33
|
end
|
28
34
|
|
29
|
-
# Resets Spinach to a pristine state, as if
|
35
|
+
# Resets Spinach to a pristine state, as if no feature was ever registered.
|
30
36
|
# Mostly useful in Spinach's own testing.
|
31
37
|
#
|
38
|
+
# @api semipublic
|
32
39
|
def self.reset_features
|
33
40
|
@@features = []
|
34
41
|
end
|
35
42
|
|
36
|
-
# Finds a feature given a feature name
|
43
|
+
# Finds a feature given a feature name.
|
37
44
|
#
|
38
45
|
# @param [String] name
|
39
|
-
#
|
46
|
+
# The feature name.
|
40
47
|
#
|
48
|
+
# @api public
|
41
49
|
def self.find_feature(name)
|
42
50
|
klass = Spinach::Support.camelize(name)
|
43
51
|
@@features.detect do |feature|
|
data/spinach.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.add_runtime_dependency 'gherkin'
|
13
13
|
gem.add_runtime_dependency 'minitest'
|
14
14
|
gem.add_runtime_dependency 'colorize'
|
15
|
-
gem.add_runtime_dependency '
|
15
|
+
gem.add_runtime_dependency 'hooks'
|
16
16
|
gem.add_development_dependency 'purdytest'
|
17
17
|
gem.add_development_dependency 'rake'
|
18
18
|
gem.add_development_dependency 'mocha'
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_development_dependency 'capybara'
|
21
21
|
gem.add_development_dependency 'aruba'
|
22
22
|
gem.add_development_dependency 'pry'
|
23
|
+
gem.add_development_dependency 'simplecov'
|
23
24
|
|
24
25
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
26
|
gem.files = `git ls-files`.split("\n")
|
@@ -9,19 +9,48 @@ describe Spinach::Feature::Capybara do
|
|
9
9
|
'Hello world!'
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
12
13
|
Capybara.app = @sinatra_app
|
14
|
+
|
13
15
|
@feature = Class.new(Spinach::Feature) do
|
16
|
+
include Spinach::Feature::Capybara
|
17
|
+
Given 'Hello' do
|
18
|
+
end
|
19
|
+
Then 'Goodbye' do
|
20
|
+
end
|
14
21
|
def go_home
|
15
|
-
visit
|
22
|
+
visit '/'
|
16
23
|
page
|
17
24
|
end
|
18
25
|
end.new
|
19
26
|
end
|
20
|
-
|
27
|
+
|
28
|
+
it 'includes capybara into all features' do
|
21
29
|
@feature.kind_of? Capybara
|
22
30
|
end
|
23
|
-
|
31
|
+
|
32
|
+
it 'goes to a capybara page and returns its result' do
|
24
33
|
page = @feature.go_home
|
25
34
|
page.has_content?('Hello world').must_equal true
|
26
35
|
end
|
36
|
+
|
37
|
+
it 'resets the capybara session after each scenario' do
|
38
|
+
@feature_runner = Spinach::Runner::Feature.new(
|
39
|
+
stub_everything)
|
40
|
+
|
41
|
+
Spinach::Parser.any_instance.stubs(content: '
|
42
|
+
Feature: A test feature
|
43
|
+
Scenario: A test scenario
|
44
|
+
Given Hello
|
45
|
+
Then Goodbye
|
46
|
+
Scenario: Another test scenario
|
47
|
+
Given Hello
|
48
|
+
Then Goodbye
|
49
|
+
').at_least_once
|
50
|
+
|
51
|
+
@feature_runner.stubs(feature: @feature).at_least_once
|
52
|
+
Capybara.current_session.expects(:reset!).twice
|
53
|
+
|
54
|
+
@feature_runner.run
|
55
|
+
end
|
27
56
|
end
|
data/test/spinach/cli_test.rb
CHANGED
@@ -1,38 +1,59 @@
|
|
1
1
|
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe Spinach::Cli do
|
4
|
-
describe
|
5
|
-
it
|
4
|
+
describe '#options' do
|
5
|
+
it 'sets the default options' do
|
6
6
|
cli = Spinach::Cli.new([])
|
7
7
|
options = cli.options
|
8
8
|
options[:reporter][:backtrace].must_equal false
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
|
+
describe 'backtrace' do
|
11
12
|
%w{-b --backtrace}.each do |opt|
|
12
|
-
it
|
13
|
+
it 'sets the backtrace if #{opt}' do
|
13
14
|
cli = Spinach::Cli.new([opt])
|
14
15
|
options = cli.options
|
15
16
|
options[:reporter][:backtrace].must_equal true
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
20
|
+
describe "version" do
|
21
|
+
%w{-v --version}.each do |opt|
|
22
|
+
it "outputs the version" do
|
23
|
+
cli = Spinach::Cli.new([opt])
|
24
|
+
cli.expects(:exit)
|
25
|
+
cli.expects(:puts).with(Spinach::VERSION)
|
26
|
+
output = capture_stdout do
|
27
|
+
cli.options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
19
32
|
end
|
20
|
-
|
21
|
-
|
33
|
+
|
34
|
+
describe '#init_reporter' do
|
35
|
+
it 'inits the default reporter' do
|
36
|
+
cli = Spinach::Cli.new([])
|
37
|
+
reporter = stub
|
38
|
+
reporter.expects(:bind)
|
39
|
+
Spinach::Reporter::Stdout.stubs(new: reporter)
|
40
|
+
cli.init_reporter
|
22
41
|
Spinach.config.default_reporter.wont_equal nil
|
23
42
|
end
|
24
43
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
44
|
+
|
45
|
+
describe '#run' do
|
46
|
+
describe 'when a particular feature list is passed' do
|
47
|
+
it 'runs the feature' do
|
28
48
|
cli = Spinach::Cli.new(['features/some_feature.feature'])
|
29
49
|
Spinach::Runner.expects(:new).with(['features/some_feature.feature']).
|
30
50
|
returns(stub(:run))
|
31
51
|
cli.run
|
32
52
|
end
|
33
53
|
end
|
34
|
-
|
35
|
-
|
54
|
+
|
55
|
+
describe 'when no feature is passed' do
|
56
|
+
it 'runs the feature' do
|
36
57
|
cli = Spinach::Cli.new([])
|
37
58
|
Dir.expects(:glob).with('features/**/*.feature').
|
38
59
|
returns(['features/some_feature.feature'])
|
data/test/spinach/config_test.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe Spinach::Config do
|
4
|
-
describe
|
5
|
-
it
|
4
|
+
describe '#step_definitions_path' do
|
5
|
+
it 'returns a default' do
|
6
6
|
(Spinach.config[:step_definitions_path].kind_of? String).must_equal true
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
|
+
it 'can be overwritten' do
|
9
10
|
Spinach.config[:step_definitions_path] = 'steps'
|
10
11
|
Spinach.config[:step_definitions_path].must_equal 'steps'
|
11
12
|
end
|
12
13
|
end
|
13
|
-
|
14
|
-
|
14
|
+
|
15
|
+
describe '#support_path' do
|
16
|
+
it 'returns a default' do
|
15
17
|
(Spinach.config[:support_path].kind_of? String).must_equal true
|
16
18
|
end
|
17
|
-
|
19
|
+
|
20
|
+
it 'can be overwritten' do
|
18
21
|
Spinach.config[:support_path] = 'support'
|
19
22
|
Spinach.config[:support_path].must_equal 'support'
|
20
23
|
end
|
data/test/spinach/dsl_test.rb
CHANGED
@@ -8,7 +8,7 @@ describe Spinach::DSL do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
describe 'class methods' do
|
11
|
-
describe
|
11
|
+
describe '#When' do
|
12
12
|
it 'defines a method with the step name' do
|
13
13
|
@feature.When('I say goodbye') do
|
14
14
|
'You say hello'
|
@@ -32,5 +32,12 @@ describe Spinach::DSL do
|
|
32
32
|
@feature.feature_name.must_equal 'User salutes'
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
describe "#name" do
|
37
|
+
it "responds with a feature's name" do
|
38
|
+
@feature.feature("A cool feature")
|
39
|
+
@feature.new.name.must_equal "A cool feature"
|
40
|
+
end
|
41
|
+
end
|
35
42
|
end
|
36
43
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe Spinach::Feature do
|
4
|
-
describe
|
4
|
+
describe 'ancestors' do
|
5
5
|
it 'includes minitest helpers' do
|
6
6
|
Spinach::Feature.ancestors.must_include MiniTest::Assertions
|
7
7
|
end
|
@@ -26,8 +26,8 @@ describe Spinach::Feature do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
describe 'instance methods' do
|
29
|
-
|
30
|
-
|
29
|
+
let(:feature) do
|
30
|
+
Class.new(Spinach::Feature) do
|
31
31
|
When 'I go to the toilet' do
|
32
32
|
@pee = true
|
33
33
|
end
|
@@ -37,30 +37,33 @@ describe Spinach::Feature do
|
|
37
37
|
|
38
38
|
describe 'execute_step' do
|
39
39
|
it 'runs defined step correctly' do
|
40
|
-
|
40
|
+
feature.execute_step('I go to the toilet')
|
41
41
|
|
42
|
-
|
42
|
+
feature.pee.must_equal true
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'raises an exception if step is not defined' do
|
46
46
|
proc {
|
47
|
-
|
47
|
+
feature.execute_step 'I am lost'
|
48
48
|
}.must_raise Spinach::StepNotDefinedException
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
describe
|
54
|
-
it
|
55
|
-
feature = Feature(
|
53
|
+
describe 'Object#Feature' do
|
54
|
+
it 'creates a feature class' do
|
55
|
+
feature = Feature('Hola') do
|
56
56
|
attr_accessor :test
|
57
|
-
When
|
57
|
+
When 'Test' do
|
58
58
|
self.test = true
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
61
62
|
Spinach.features.must_include feature
|
63
|
+
|
62
64
|
instance = feature.new
|
63
|
-
instance.execute_step(
|
65
|
+
instance.execute_step('Test')
|
66
|
+
|
64
67
|
instance.test.must_equal true
|
65
68
|
end
|
66
69
|
end
|
data/test/spinach/parser_test.rb
CHANGED
@@ -2,36 +2,49 @@ require_relative '../test_helper'
|
|
2
2
|
|
3
3
|
describe Spinach::Parser do
|
4
4
|
before do
|
5
|
-
@parser = Spinach::Parser.new(
|
6
|
-
@parser.stubs(:content).returns('
|
7
|
-
Feature: User authentication
|
8
|
-
Scenario: User logs in
|
9
|
-
Given I am on the front page
|
10
|
-
When I fill in the login form and press "login"
|
11
|
-
Then I should be on my dashboard
|
12
|
-
')
|
5
|
+
@parser = Spinach::Parser.new('feature_definition.feature')
|
13
6
|
end
|
14
|
-
|
7
|
+
|
8
|
+
let(:parsed) { @parser.parse }
|
9
|
+
|
10
|
+
describe '#parse' do
|
15
11
|
before do
|
16
|
-
@
|
12
|
+
@parser.stubs(:content).returns("
|
13
|
+
Feature: User authentication
|
14
|
+
Scenario: User logs in
|
15
|
+
Given I am on the front page
|
16
|
+
When I fill in the login form and press 'login'
|
17
|
+
Then I should be on my dashboard
|
18
|
+
")
|
17
19
|
end
|
18
|
-
|
19
|
-
|
20
|
+
|
21
|
+
it 'parses the feature name' do
|
22
|
+
parsed['name'].must_equal 'User authentication'
|
20
23
|
end
|
21
|
-
|
24
|
+
|
25
|
+
describe 'scenario' do
|
22
26
|
before do
|
23
|
-
@scenario =
|
27
|
+
@scenario = parsed['elements'][0]
|
24
28
|
end
|
25
|
-
|
26
|
-
|
29
|
+
|
30
|
+
it 'parses the scenario name' do
|
31
|
+
@scenario['name'].must_equal 'User logs in'
|
27
32
|
end
|
28
|
-
|
29
|
-
|
33
|
+
|
34
|
+
it 'parses the scenario steps' do
|
35
|
+
@scenario['steps'][0]['name'].must_equal 'I am on the front page'
|
30
36
|
@scenario['steps'][1]['name'].must_equal(
|
31
|
-
|
37
|
+
'I fill in the login form and press \'login\''
|
32
38
|
)
|
33
|
-
@scenario['steps'][2]['name'].must_equal
|
39
|
+
@scenario['steps'][2]['name'].must_equal 'I should be on my dashboard'
|
34
40
|
end
|
35
41
|
end
|
36
42
|
end
|
43
|
+
|
44
|
+
describe '#content' do
|
45
|
+
it 'reads the disk and returns the file content' do
|
46
|
+
File.expects(:read).with('feature_definition.feature')
|
47
|
+
@parser.content
|
48
|
+
end
|
49
|
+
end
|
37
50
|
end
|