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.
Files changed (69) hide show
  1. data/Gemfile +2 -0
  2. data/README.markdown +18 -12
  3. data/features/background.feature +13 -0
  4. data/features/reporting/show_step_source_location.feature +11 -1
  5. data/features/steps/automatic_feature_generation.rb +7 -7
  6. data/features/steps/background.rb +30 -0
  7. data/features/steps/exit_status.rb +12 -13
  8. data/features/steps/feature_name_guessing.rb +7 -7
  9. data/features/steps/reporting/display_run_summary.rb +22 -22
  10. data/features/steps/reporting/error_reporting.rb +7 -7
  11. data/features/steps/reporting/show_step_source_location.rb +63 -14
  12. data/features/steps/reporting/undefined_feature_reporting.rb +7 -7
  13. data/features/steps/rspec_compatibility.rb +14 -14
  14. data/features/support/error_reporting.rb +5 -5
  15. data/features/support/filesystem.rb +71 -0
  16. data/features/support/spinach_runner.rb +5 -6
  17. data/lib/spinach.rb +11 -6
  18. data/lib/spinach/background.rb +11 -0
  19. data/lib/spinach/capybara.rb +7 -1
  20. data/lib/spinach/cli.rb +36 -13
  21. data/lib/spinach/config.rb +40 -6
  22. data/lib/spinach/dsl.rb +14 -11
  23. data/lib/spinach/exceptions.rb +1 -1
  24. data/lib/spinach/feature.rb +16 -0
  25. data/lib/spinach/frameworks.rb +2 -0
  26. data/lib/spinach/{suites → frameworks}/minitest.rb +0 -0
  27. data/lib/spinach/{suites → frameworks}/rspec.rb +0 -0
  28. data/lib/spinach/generators/feature_generator.rb +12 -23
  29. data/lib/spinach/generators/step_generator.rb +5 -5
  30. data/lib/spinach/hookable.rb +6 -4
  31. data/lib/spinach/hooks.rb +12 -4
  32. data/lib/spinach/parser.rb +6 -8
  33. data/lib/spinach/parser/visitor.rb +109 -0
  34. data/lib/spinach/reporter.rb +10 -6
  35. data/lib/spinach/reporter/stdout.rb +41 -16
  36. data/lib/spinach/reporter/stdout/error_reporting.rb +2 -2
  37. data/lib/spinach/runner.rb +9 -6
  38. data/lib/spinach/runner/feature_runner.rb +40 -34
  39. data/lib/spinach/runner/scenario_runner.rb +63 -36
  40. data/lib/spinach/scenario.rb +12 -0
  41. data/lib/spinach/step.rb +10 -0
  42. data/lib/spinach/version.rb +1 -1
  43. data/spinach.gemspec +3 -3
  44. data/test/spinach/background_test.rb +6 -0
  45. data/test/spinach/capybara_test.rb +30 -13
  46. data/test/spinach/cli_test.rb +46 -1
  47. data/test/spinach/config_test.rb +39 -0
  48. data/test/spinach/dsl_test.rb +12 -10
  49. data/test/spinach/feature_steps_test.rb +3 -3
  50. data/test/spinach/feature_test.rb +6 -0
  51. data/test/spinach/{suites → frameworks}/minitest_test.rb +2 -2
  52. data/test/spinach/generators/feature_generator_test.rb +18 -58
  53. data/test/spinach/generators/step_generator_test.rb +3 -3
  54. data/test/spinach/generators_test.rb +12 -10
  55. data/test/spinach/hookable_test.rb +8 -0
  56. data/test/spinach/hooks_test.rb +6 -7
  57. data/test/spinach/parser/visitor_test.rb +173 -0
  58. data/test/spinach/parser_test.rb +14 -27
  59. data/test/spinach/reporter/stdout/error_reporting_test.rb +9 -9
  60. data/test/spinach/reporter/stdout_test.rb +15 -19
  61. data/test/spinach/reporter_test.rb +15 -0
  62. data/test/spinach/runner/feature_runner_test.rb +79 -69
  63. data/test/spinach/runner/scenario_runner_test.rb +118 -92
  64. data/test/spinach/runner_test.rb +10 -6
  65. data/test/spinach/scenario_test.rb +6 -0
  66. data/test/spinach/step_test.rb +6 -0
  67. data/test/spinach_test.rb +7 -7
  68. metadata +60 -39
  69. 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
- 'Feature: Feature without steps
8
+ write_file('features/feature_without_steps.feature', """
9
+ Feature: Feature without steps
10
10
 
11
- Scenario: A scenario without steps
12
- Given I have no steps
13
- Then I should do nothing
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
- all_stderr.must_match /Undefined features.*1/
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
- 'Feature: Feature with failures
9
+ write_file('features/feature_with_failures.feature', """
10
+ Feature: Feature with failures
11
11
 
12
- Scenario: This scenario will fail
13
- Given true is false
14
- Then remove all the files in my hard drive
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
- run_feature "features/#{@feature}.feature", suite: :rspec
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
- 'Feature: Greeting
52
+ write_file('features/greeting.feature', """
53
+ Feature: Greeting
54
54
 
55
- Scenario: Greeting
56
- Given I am on the front page
57
- Then I should see hello world
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
- require 'aruba/api'
1
+ require_relative 'spinach_runner'
2
2
 
3
3
  module Integration
4
4
  module ErrorReporting
5
- include Aruba::Api
5
+ include Filesystem
6
6
 
7
7
  def check_error_messages(n = 1)
8
- all_stderr.must_match /Failures \(1\)/
8
+ @stderr.must_match /Failures \(1\)/
9
9
  end
10
10
 
11
11
  def check_backtrace(n = 1)
12
- all_stderr.must_match /Failures \(1\)/
13
- all_stderr.must_match /gems.*(minitest|rspec).*assert_equal/
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
- require 'aruba/api'
1
+ require_relative 'filesystem'
2
2
 
3
3
  module Integration
4
4
  module SpinachRunner
5
- include Aruba::Api
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[:suite] ||= :minitest
20
- use_minitest if options[:suite] == :minitest
21
- use_rspec if options[:suite] == :rspec
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 a feature given a feature name.
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 [FeatureSteps]
59
- # the {FeatureSteps} class for the given feature name
63
+ # @return [StepDefinitions]
64
+ # the {StepDefinitions} class for the given feature name
60
65
  #
61
66
  # @api public
62
- def self.find_feature_steps(name)
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
- feature.name == klass
71
+ feature.name == klass
67
72
  end
68
73
  end
69
74
  end
@@ -0,0 +1,11 @@
1
+ module Spinach
2
+ class Background
3
+ attr_accessor :line
4
+ attr_accessor :steps, :feature
5
+
6
+ def initialize(feature)
7
+ @feature = feature
8
+ @steps = []
9
+ end
10
+ end
11
+ end
@@ -30,8 +30,14 @@ module Spinach
30
30
  include ::Capybara::RSpecMatchers
31
31
  end
32
32
  end
33
- Spinach.hooks.before_scenario do
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 'features', '**', '*.feature')
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
- OptionParser.new do |opts|
66
- opts.on('-b', '--backtrace', 'Show backtrace of errors') do |v|
67
- reporter_options[:backtrace] = v
68
- end
69
- opts.on('-g', '--generate', 'Auto-generate the feeature steps files') do |v|
70
- Spinach::Generators.bind
71
- end
72
- opts.on_tail('--version', 'Show version') do
73
- puts Spinach::VERSION
74
- exit
75
- end
76
- end.parse!(@args)
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
@@ -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 'features/steps'
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 || 'features/steps'
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 || 'features/support'
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 execute_step(step)
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
- location
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]