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.
Files changed (41) hide show
  1. data/Gemfile +1 -0
  2. data/Readme.md +3 -0
  3. data/bin/spinach +3 -1
  4. data/features/steps/error_reporting.rb +4 -8
  5. data/features/steps/exit_status.rb +0 -1
  6. data/features/steps/feature_name_guessing.rb +1 -2
  7. data/features/support/spinach_runner.rb +0 -2
  8. data/lib/spinach/capybara.rb +8 -8
  9. data/lib/spinach/cli.rb +30 -18
  10. data/lib/spinach/config.rb +29 -10
  11. data/lib/spinach/dsl.rb +45 -21
  12. data/lib/spinach/exceptions.rb +21 -8
  13. data/lib/spinach/feature.rb +8 -4
  14. data/lib/spinach/parser.rb +13 -7
  15. data/lib/spinach/reporter/stdout.rb +251 -53
  16. data/lib/spinach/reporter.rb +43 -26
  17. data/lib/spinach/runner/feature.rb +39 -26
  18. data/lib/spinach/runner/scenario.rb +43 -28
  19. data/lib/spinach/runner.rb +40 -20
  20. data/lib/spinach/support.rb +8 -7
  21. data/lib/spinach/version.rb +2 -1
  22. data/lib/spinach.rb +17 -9
  23. data/spinach.gemspec +2 -1
  24. data/test/spinach/capybara_test.rb +32 -3
  25. data/test/spinach/cli_test.rb +32 -11
  26. data/test/spinach/config_test.rb +9 -6
  27. data/test/spinach/dsl_test.rb +8 -1
  28. data/test/spinach/feature_test.rb +14 -11
  29. data/test/spinach/parser_test.rb +33 -20
  30. data/test/spinach/reporter/stdout_test.rb +364 -103
  31. data/test/spinach/reporter_test.rb +145 -20
  32. data/test/spinach/runner/feature_test.rb +26 -51
  33. data/test/spinach/runner/scenario_test.rb +64 -35
  34. data/test/spinach/runner_test.rb +41 -32
  35. data/test/spinach/support_test.rb +2 -10
  36. data/test/spinach_test.rb +14 -14
  37. data/test/test_helper.rb +13 -5
  38. metadata +36 -28
  39. data/examples/steps/user_logs_in.rb +0 -23
  40. data/examples/user_logs_in.feature +0 -6
  41. data/examples/user_logs_in.rb +0 -23
@@ -1,22 +1,38 @@
1
+ require 'hooks'
2
+
1
3
  module Spinach
2
- # Spinach's runner gets the parsed data from the feature and performs the
3
- # actual calls to the feature classes.
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
- # an array of feature filenames to run
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, reporter).run
50
- @failed = true unless success
67
+ success = Feature.new(filename).run
68
+ successful = false unless success
51
69
  end
52
- reporter.end
53
- @failed ? false : true
70
+
71
+ run_hook :after_run, successful
72
+
73
+ successful
54
74
  end
55
75
 
56
- # Requires step definitions and support files
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
 
@@ -1,19 +1,20 @@
1
- require 'active_support/inflector/methods'
2
-
3
1
  module Spinach
4
- # A module to offer common helpers
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
- # @returns [String]
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
- ActiveSupport::Inflector.camelize(name.to_s.downcase.strip.squeeze(' ').gsub(' ','_'))
17
+ name.to_s.strip.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
17
18
  end
18
19
  end
19
20
  end
@@ -1,3 +1,4 @@
1
1
  module Spinach
2
- VERSION = "0.0.6"
2
+ # Spianch version.
3
+ VERSION = "0.1.0"
3
4
  end
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
- # Spinach is a BDD framework in top of gherkin. Its main goals are:
14
- # * No magic: All features are implemented using actual classes.
15
- # * Reusability: Steps are methods, so they can be put inside modules.
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<Spinach::Feature>]
23
- # all the registered features
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 any feature was registered.
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
- # the feature name
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 'activesupport'
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
- it "includes capybara into all features" do
27
+
28
+ it 'includes capybara into all features' do
21
29
  @feature.kind_of? Capybara
22
30
  end
23
- it "goes to a capybara page and returns its result" do
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
@@ -1,38 +1,59 @@
1
1
  require_relative '../test_helper'
2
2
 
3
3
  describe Spinach::Cli do
4
- describe "#options" do
5
- it "defaults" do
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
- describe "backtrace" do
10
+
11
+ describe 'backtrace' do
11
12
  %w{-b --backtrace}.each do |opt|
12
- it "sets the backtrace if #{opt}" do
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
- describe "#init_reporter" do
21
- it "inits the default reporter" do
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
- describe "#run" do
26
- describe "when a particular feature list is passed" do
27
- it "runs the feature" do
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
- describe "when no feature is passed" do
35
- it "runs the feature" do
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'])
@@ -1,20 +1,23 @@
1
1
  require_relative '../test_helper'
2
2
 
3
3
  describe Spinach::Config do
4
- describe "#step_definitions_path" do
5
- it "returns a default" do
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
- it "can be overwritten" do
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
- describe "#support_path" do
14
- it "returns a default" do
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
- it "can be overwritten" do
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
@@ -8,7 +8,7 @@ describe Spinach::DSL do
8
8
  end
9
9
 
10
10
  describe 'class methods' do
11
- describe "#When" do
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 "ancestors" do
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
- before do
30
- @feature = Class.new(Spinach::Feature) do
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
- @feature.execute_step('I go to the toilet')
40
+ feature.execute_step('I go to the toilet')
41
41
 
42
- @feature.pee.must_equal true
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
- @feature.execute_step 'I am lost'
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 "Object#Feature" do
54
- it "creates a feature class" do
55
- feature = Feature("Hola") do
53
+ describe 'Object#Feature' do
54
+ it 'creates a feature class' do
55
+ feature = Feature('Hola') do
56
56
  attr_accessor :test
57
- When "Test" do
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("Test")
65
+ instance.execute_step('Test')
66
+
64
67
  instance.test.must_equal true
65
68
  end
66
69
  end
@@ -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("feature_definition.feature")
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
- describe "#parse" do
7
+
8
+ let(:parsed) { @parser.parse }
9
+
10
+ describe '#parse' do
15
11
  before do
16
- @parsed = @parser.parse
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
- it "parses the feature name" do
19
- @parsed['name'].must_equal "User authentication"
20
+
21
+ it 'parses the feature name' do
22
+ parsed['name'].must_equal 'User authentication'
20
23
  end
21
- describe "scenario" do
24
+
25
+ describe 'scenario' do
22
26
  before do
23
- @scenario = @parsed['elements'][0]
27
+ @scenario = parsed['elements'][0]
24
28
  end
25
- it "parses the scenario name" do
26
- @scenario['name'].must_equal "User logs in"
29
+
30
+ it 'parses the scenario name' do
31
+ @scenario['name'].must_equal 'User logs in'
27
32
  end
28
- it "parses the scenario steps" do
29
- @scenario['steps'][0]['name'].must_equal "I am on the front page"
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
- "I fill in the login form and press \"login\""
37
+ 'I fill in the login form and press \'login\''
32
38
  )
33
- @scenario['steps'][2]['name'].must_equal "I should be on my dashboard"
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