spinach 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
  group :test do
7
7
  gem 'guard'
8
8
  gem 'guard-minitest'
9
+ gem 'pry', git: 'git://github.com/pry/pry.git'
9
10
  end
10
11
 
11
12
  group :darwin do
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
@@ -6,4 +6,6 @@ rescue LoadError
6
6
  require_relative '../lib/spinach'
7
7
  end
8
8
 
9
- exit Spinach::Cli.new(ARGV).run
9
+ cli = Spinach::Cli.new(ARGV)
10
+ cli.init_reporter
11
+ exit cli.run
@@ -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
- all_stdout.wont_match /gems.*minitest.*assert_equal/
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
- all_stdout.must_include "Error summary"
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
- all_stdout.must_include "Error summary"
52
- all_stdout.must_match /errors.*This scenario will fail.*line 4/
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
@@ -46,5 +46,4 @@ Feature "Exit status" do
46
46
  Then "the exit status should be 1" do
47
47
  last_exit_status.must_equal 1
48
48
  end
49
-
50
49
  end
@@ -27,7 +27,6 @@ Feature "Feature name guessing" do
27
27
  end
28
28
 
29
29
  Then 'I want "MyCoolFeature" class to be used to run it' do
30
- all_stderr.must_be_empty
30
+ last_exit_status.must_equal 0
31
31
  end
32
-
33
32
  end
@@ -5,10 +5,8 @@ module Integration
5
5
  include Aruba::Api
6
6
 
7
7
  private
8
-
9
8
  def run_feature(command, options=nil)
10
9
  run "../../bin/spinach #{command} #{options}"
11
10
  end
12
-
13
11
  end
14
12
  end
@@ -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
- def after
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
- # the command line arguments
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 feature
16
+ # Runs all the features.
17
+ #
18
+ # @return [true, false]
19
+ # The exit status - true for success, false for failure.
15
20
  #
16
- # @return [Boolean]
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
- Spinach.config.default_reporter =
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
- # Returns a hash of options, separated by its type:
42
+ # @return [Hash]
43
+ # A hash of options separated by its type.
36
44
  #
37
45
  # @example
38
- # {
39
- # reporter: { backtrace: true }
40
- # }
46
+ # Cli.new.options
47
+ # # => { reporter: { backtrace: true } }
41
48
  #
42
- # @return [Hash]
49
+ # @api public
43
50
  def options
44
51
  @options ||= parse_options
45
52
  end
46
53
 
47
- private
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', "Show backtrace of errors") do |v|
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', "Show version") do
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
@@ -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 [Spinach::Config]
7
- # the config object
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" helds the place where your feature classes
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 your can put your configuration
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
- # # => 'features/steps'
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
- def []=(attribute, params)
64
- self.send("#{attribute}=", params)
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 other domain
3
- # specific actions into your feature classes
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 name
19
- # The step literal
35
+ # @param [String] step
36
+ # The step name.
37
+ #
20
38
  # @param [Proc] block
21
- # action to perform in that step
39
+ # Action to perform in that step.
22
40
  #
23
41
  # @example
24
- # class MyFeature << Spinach::Feature
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
- def Given(string, &block)
31
- define_method(string, &block)
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
- # Defines this feature's name
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
- # the step to execute
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
@@ -1,28 +1,41 @@
1
1
  module Spinach
2
- # This class represents the feature raised when Spniach can't find a class
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
- @not_found_class = options.first
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 #{@not_found_class}.rb file at #{Spinach.config[:step_definitions_path]}"
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 step raised when Spinach can't find a step for a
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
@@ -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 where all the features must inherit from.
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
- def before; end;
12
- def after; end;
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
@@ -2,29 +2,35 @@ require 'gherkin'
2
2
  require 'gherkin/formatter/json_formatter'
3
3
 
4
4
  module Spinach
5
- # Spinach's parser uses Gherkin in the guts to extract all the information
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
- # the file to parse
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
- # the plain feature content
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
- # @return [Hash] the parsed gerkin output
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