spinach 0.1.5.4 → 0.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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]