spinach 0.1.3 → 0.1.4

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.
@@ -0,0 +1,10 @@
1
+ Feature: Automatic feature generation
2
+ In order to be faster writing features
3
+ As a developer
4
+ I want spinach to automatically generate features for me
5
+
6
+ Scenario: Missing feature
7
+ Given I have defined a "Cheezburger can I has" feature
8
+ When I run spinach with "--generate"
9
+ Then I a feature should exist named "features/steps/cheezburger_can_i_has.rb"
10
+ And that feature should have the example feature steps
@@ -0,0 +1,32 @@
1
+ Feature 'Automatic feature generation' do
2
+ include Integration::SpinachRunner
3
+ Given 'I have defined a "Cheezburger can I has" feature' do
4
+ write_file('features/cheezburger_can_i_has.feature',
5
+ 'Feature: Cheezburger can I has
6
+ Scenario: Some Lulz
7
+ Given I haz a sad
8
+ When I get some lulz
9
+ Then I haz a happy')
10
+ end
11
+
12
+ When 'I run spinach with "--generate"' do
13
+ run_feature 'features/cheezburger_can_i_has.feature', append: '--generate'
14
+ end
15
+
16
+ Then 'I a feature should exist named "features/steps/cheezburger_can_i_has.rb"' do
17
+ stop_processes!
18
+ in_current_dir do
19
+ @file = 'features/steps/cheezburger_can_i_has.rb'
20
+ File.exists?(@file).must_equal true
21
+ end
22
+ end
23
+
24
+ And "that feature should have the example feature steps" do
25
+ in_current_dir do
26
+ content = File.read(@file)
27
+ content.must_include "I haz a sad"
28
+ content.must_include "I get some lulz"
29
+ content.must_include "I haz a happy"
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,3 @@
1
- require 'aruba/api'
2
-
3
1
  Feature "Exit status" do
4
2
  include Integration::SpinachRunner
5
3
 
@@ -12,38 +12,45 @@ Feature "Display run summary" do
12
12
  When I jump from Codegrams roof
13
13
  Then I must be pwned by floor
14
14
 
15
- # Scenario: Failed scenario
16
- # Given I love risk
17
- # When I jump from Codegrams roof
18
- # Then my parachute must open
19
- # Then I must not pwned by floor
20
-
21
- # Scenario: Error scenario
22
- # Given I am not a fool
23
- # When I go downstairs
24
- # Then I must succeed
15
+ Scenario: Failed scenario
16
+ Given I love risk
17
+ When I jump from Codegrams roof
18
+ Then my parachute must open
19
+ Then I must not be pwned by floor
20
+
21
+ Scenario: Error scenario
22
+ Given I am not a fool
23
+ When I go downstairs
24
+ Then I must succeed
25
25
  ')
26
26
  write_file('features/steps/test_feature.rb',
27
27
  'Feature "A test feature" do
28
28
  Given "I am a fool" do
29
29
  end
30
+
30
31
  When "I jump from Codegrams roof" do
31
32
  end
32
- # Given "I love risk" do
33
- # end
34
- # And "my parachute must open" do
35
- # false
36
- # end
37
- # Given I am a fool
38
- # djkadlaksaldkajd
39
- # end
40
- # Given I am not a fool
41
- # end
42
- # When I go downstairs
43
- # end
44
- # Then I must succeed
45
- # true
46
- # end
33
+
34
+ Given "I love risk" do
35
+ end
36
+
37
+ And "my parachute must open" do
38
+ false.must_equal true
39
+ end
40
+
41
+ Given "I am a fool" do
42
+ end
43
+
44
+ Given "I am not a fool" do
45
+ adaksjdald
46
+ end
47
+
48
+ When "I go downstairs" do
49
+ end
50
+
51
+ Then "I must succeed" do
52
+ true
53
+ end
47
54
  end')
48
55
  @feature = "features/test_feature.feature"
49
56
  end
@@ -54,7 +61,7 @@ Feature "Display run summary" do
54
61
 
55
62
  Then "I should see a summary with steps status information" do
56
63
  all_stdout.must_match(
57
- /Summary:.*2.*Successful.*1.*Undefined.*0.*Failed.*0.*Error/
64
+ /Summary:.*4.*Successful.*1.*Undefined.*1.*Failed.*1.*Error/
58
65
  )
59
66
  end
60
67
  end
@@ -8,7 +8,7 @@ module Integration
8
8
  base.class_eval do
9
9
  before_scenario do
10
10
  in_current_dir do
11
- run "rm -fR features"
11
+ FileUtils.rm_rf("features")
12
12
  end
13
13
  end
14
14
  before_scenario do
data/lib/spinach/cli.rb CHANGED
@@ -67,6 +67,9 @@ module Spinach
67
67
  opts.on('-b', '--backtrace', 'Show backtrace of errors') do |v|
68
68
  reporter_options[:backtrace] = v
69
69
  end
70
+ opts.on('-g', '--generate', 'Auto-generate the feeature steps files') do |v|
71
+ Spinach::Generators.bind
72
+ end
70
73
  opts.on_tail('--version', 'Show version') do
71
74
  puts Spinach::VERSION
72
75
  exit
data/lib/spinach/dsl.rb CHANGED
@@ -82,13 +82,13 @@ module Spinach
82
82
  #
83
83
  # @api public
84
84
  def execute_step(step)
85
- step = Spinach::Support.underscore(step)
85
+ undercored_step = Spinach::Support.underscore(step)
86
86
  location = nil
87
- if self.respond_to?(step)
88
- location = method(step).source_location
89
- self.send(step)
87
+ if self.respond_to?(undercored_step)
88
+ location = method(undercored_step).source_location
89
+ self.send(undercored_step)
90
90
  else
91
- raise Spinach::StepNotDefinedException.new(self, step)
91
+ raise Spinach::StepNotDefinedException.new(step)
92
92
  end
93
93
  location
94
94
  end
@@ -28,16 +28,20 @@ module Spinach
28
28
  class StepNotDefinedException < StandardError
29
29
  attr_reader :feature, :step
30
30
 
31
- # @param [Feature] feature
32
- # The container feature.
33
- #
34
31
  # @param [Hash] step
35
32
  # The missing step.
36
33
  #
37
34
  # @api pulic
38
- def initialize(feature, step)
39
- @feature = feature
35
+ def initialize(step)
40
36
  @step = step
41
37
  end
38
+
39
+ # @return [String]
40
+ # A custom message when scenario steps aren't found.
41
+ #
42
+ # @api public
43
+ def message
44
+ "Step '#{@step}' not found"
45
+ end
42
46
  end
43
47
  end
@@ -0,0 +1,96 @@
1
+ module Spinach
2
+ module Generators
3
+ # A feature generator generates and/or writes an example feature steps class
4
+ # given the parsed feture data
5
+ class FeatureGenerator
6
+
7
+ # @param [Hash] data
8
+ # the parsed feature data returned from the {Parser}
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ # @return [Array<Hash>]
14
+ # an array of unique steps found in this scenario, avoiding name
15
+ # repetition
16
+ def steps
17
+ return @steps if @steps
18
+ @steps = []
19
+ if scenarios = @data['elements']
20
+ scenarios.each do |scenario|
21
+ if scenario_steps = scenario['steps']
22
+ scenario_steps.each do |step|
23
+ unless @steps.any?{|s| s['name'] == step['name']}
24
+ @steps << {
25
+ 'keyword' => step['keyword'].strip,
26
+ 'name' => step['name'].strip
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ @steps
34
+ end
35
+
36
+ # @return [String]
37
+ # this feature's name
38
+ def name
39
+ @data['name'].strip if @data['name']
40
+ end
41
+
42
+ # @return [String]
43
+ # an example feature steps definition
44
+ def generate
45
+ result = StringIO.new
46
+ result.puts "Feature '#{Spinach::Support.escape_single_commas name}' do"
47
+ generated_steps = steps.map do |step|
48
+ step_generator = Generators::StepGenerator.new(step)
49
+ step_generator.generate.split("\n").map do |line|
50
+ " #{line}"
51
+ end.join("\n")
52
+ end
53
+ result.puts generated_steps.join("\n\n")
54
+ result.puts "end"
55
+ result.string
56
+ end
57
+
58
+ # @return [String]
59
+ # an example filename for this feature steps
60
+ def filename
61
+ Spinach::Support.underscore (
62
+ Spinach::Support.camelize name
63
+ ) + ".rb"
64
+ end
65
+
66
+ # @return [String]
67
+ # the path where this feature steps may be saved
68
+ def path
69
+ Spinach.config[:step_definitions_path]
70
+ end
71
+
72
+ # @return [String]
73
+ # the expanded path where this feature steps may be saved
74
+ def filename_with_path
75
+ File.expand_path File.join(path, filename)
76
+ end
77
+
78
+ # Stores the example feature steps definition into an expected path
79
+ #
80
+ def store
81
+ if File.exists?(filename_with_path)
82
+ raise FeatureGeneratorException.new("File already exists")
83
+ else
84
+ FileUtils.mkdir_p path
85
+ File.open(filename_with_path, 'w') do |file|
86
+ file.write(generate)
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ class FeatureGeneratorException < Exception; end;
94
+
95
+ end
96
+ end
@@ -0,0 +1,22 @@
1
+ module Spinach
2
+ # A step generator generates an example output for a step given the parsed
3
+ # feature data.
4
+ #
5
+ class Generators::StepGenerator
6
+
7
+ # @param [Hash] data
8
+ # the parsed step data returned from the {Parser}
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ # @return [String]
14
+ # an example step definition
15
+ def generate
16
+ result = StringIO.new
17
+ result.puts "#{@data['keyword']} '#{Spinach::Support.escape_single_commas @data['name']}' do"
18
+ result.puts "end"
19
+ result.string
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module Spinach
2
+ # Spinach generators are a set of utils that auto-generates example features
3
+ # given some parsed feature data.
4
+ #
5
+ module Generators
6
+ # Binds the feature generator to the "feature not found" hook
7
+ def self.bind
8
+ Spinach::Runner::Feature.when_not_found do |data|
9
+ Spinach::Generators.generate_feature(data)
10
+ end
11
+ end
12
+
13
+ # Generates a feature given a parsed feature data
14
+ #
15
+ # @param [Hash] data
16
+ # the parsed feature data
17
+ def self.generate_feature(data)
18
+ FeatureGenerator.new(data).store
19
+ rescue FeatureGeneratorException => e
20
+ $stderr.puts e
21
+ end
22
+ end
23
+ end
24
+
25
+ require_relative 'generators/feature_generator'
26
+ require_relative 'generators/step_generator'
@@ -5,25 +5,31 @@ module Spinach
5
5
  # Parser leverages Gherkin to parse the feature definition.
6
6
  #
7
7
  class Parser
8
- # @param [String] filename
9
- # The filename to parse.
8
+ # @param [String] content
9
+ # The content to parse.
10
10
  #
11
11
  # @api public
12
- def initialize(filename)
13
- @filename = filename
12
+ def initialize(content)
13
+ @content = content
14
14
  @formatter = Gherkin::Formatter::JSONFormatter.new(nil)
15
15
  @parser = Gherkin::Parser::Parser.new(@formatter)
16
16
  end
17
17
 
18
+ # @param [String] filename
19
+ # The filename to parse.
20
+ #
21
+ # @api public
22
+ def self.open_file(filename)
23
+ new File.read(filename)
24
+ end
25
+
18
26
  # Gets the plain text content out of the feature file.
19
27
  #
20
28
  # @return [String]
21
29
  # The plain feature content.
22
30
  #
23
31
  # @api public
24
- def content
25
- File.read(@filename)
26
- end
32
+ attr_reader :content
27
33
 
28
34
  # Parses the feature file and returns an AST as a Hash.
29
35
  #
@@ -11,6 +11,8 @@ module Spinach
11
11
 
12
12
  # The last scenario error
13
13
  attr_accessor :scenario_error
14
+
15
+ # The last scenario
14
16
  attr_accessor :scenario
15
17
 
16
18
  # Initialitzes the runner
@@ -64,6 +66,9 @@ module Spinach
64
66
  # @param [Hash] step
65
67
  # The step in a JSON Gherkin format
66
68
  #
69
+ # @param [Array] step_location
70
+ # The step source location
71
+ #
67
72
  def on_successful_step(step, step_location)
68
73
  output_step('✔', step, :green, step_location)
69
74
  self.scenario = [current_feature, current_scenario, step]
@@ -105,7 +110,7 @@ module Spinach
105
110
  #
106
111
  def on_undefined_step(step, failure)
107
112
  output_step('?', step, :yellow)
108
- self.scenario_error = [current_feature, current_scenario, step]
113
+ self.scenario_error = [current_feature, current_scenario, step, failure]
109
114
  undefined_steps << scenario_error
110
115
  end
111
116
 
@@ -120,20 +125,15 @@ module Spinach
120
125
  def on_feature_not_found(feature, exception)
121
126
  lines = "#{exception.message}\n"
122
127
 
123
- lines << "\nPlease create the file #{Spinach::Support.underscore(exception.missing_class)}.rb at #{Spinach.config[:step_definitions_path]}, with:\n\n"
128
+ generator = Generators::FeatureGenerator.new(feature)
129
+ lines << "\nPlease create the file #{generator.filename} at #{generator.path}, with:\n\n"
124
130
 
125
- lines << "Feature '#{feature['name']}' do\n"
126
-
127
- # TODO: Write the actual steps. We can do this since we have the entire
128
- # feature just here. We should iterate over all the scenarios and return
129
- # the different steps
130
- #
131
- lines << " # Write your steps here\n"
132
- lines << "end\n\n"
131
+ lines << generator.generate
133
132
 
134
133
  lines.split("\n").each do |line|
135
134
  out.puts " #{line}".yellow
136
135
  end
136
+ out.puts "\n\n"
137
137
 
138
138
  undefined_features << feature
139
139
  end
@@ -181,7 +181,7 @@ module Spinach
181
181
  run_summary
182
182
  end
183
183
 
184
- # Prints the feature success summaryfor ths run.
184
+ # Prints the feature success summary for this run.
185
185
  #
186
186
  def run_summary
187
187
  successful_summary = "(".colorize(:green)+successful_steps.length.to_s.colorize(:light_green)+") Successful".colorize(:green)
@@ -191,7 +191,7 @@ module Spinach
191
191
  out.puts "Steps Summary: #{successful_summary}, #{undefined_summary}, #{failed_summary}, #{error_summary}\n\n"
192
192
  end
193
193
 
194
- # Prints the errors for ths run.
194
+ # Prints the errors for this run.
195
195
  #
196
196
  def error_summary
197
197
  error.puts "\nError summary:\n"
@@ -298,17 +298,27 @@ module Spinach
298
298
  #
299
299
  def full_error(error)
300
300
  feature, scenario, step, exception = error
301
- output = String.new
301
+ output = "\n"
302
302
  output += report_exception(exception)
303
303
  output +="\n"
304
304
 
305
- if options[:backtrace]
306
- output += "\n"
307
- exception.backtrace.map do |line|
308
- output << " #{line}\n"
305
+ if exception.kind_of?(Spinach::StepNotDefinedException)
306
+ output << "\n"
307
+ output << " You can define it with: \n\n".yellow
308
+ suggestion = Generators::StepGenerator.new(step).generate
309
+ suggestion.split("\n").each do |line|
310
+ output << " #{line}\n".yellow
309
311
  end
312
+ output << "\n"
310
313
  else
311
- output << " #{exception.backtrace[0]}"
314
+ if options[:backtrace]
315
+ output += "\n"
316
+ exception.backtrace.map do |line|
317
+ output << " #{line}\n"
318
+ end
319
+ else
320
+ output << " #{exception.backtrace[0]}"
321
+ end
312
322
  end
313
323
  output
314
324
  end
@@ -36,7 +36,7 @@ module Spinach
36
36
  #
37
37
  # @api public
38
38
  def data
39
- @data ||= Spinach::Parser.new(filename).parse
39
+ @data ||= Spinach::Parser.open_file(filename).parse
40
40
  end
41
41
 
42
42
  # @return [String]
@@ -66,16 +66,17 @@ module Spinach
66
66
 
67
67
  scenarios.each do |scenario|
68
68
  if !@scenario_line || scenario['line'].to_s == @scenario_line
69
- @success = Scenario.new(feature_name, scenario).run
69
+ success = Scenario.new(feature_name, scenario).run
70
+ @failed = true unless success
70
71
  end
71
72
  end
72
73
 
73
- run_hook :after_run, data
74
-
75
74
  rescue Spinach::FeatureStepsNotFoundException => e
76
75
  run_hook :when_not_found, data, e
76
+ @failed = true
77
77
  ensure
78
- return !!@success
78
+ run_hook :after_run, data
79
+ return !@failed
79
80
  end
80
81
  end
81
82
  end
@@ -38,5 +38,22 @@ module Spinach
38
38
  word.downcase!
39
39
  word
40
40
  end
41
+
42
+ # Escapes the single commas of a given text. Mostly used in the {Generators}
43
+ # classes
44
+ #
45
+ # @param [String] text
46
+ # The text to escape
47
+ #
48
+ # @return [String]
49
+ # The +text+ with escaped commas
50
+ #
51
+ # @example
52
+ # Spinach::Support.escape_single_commas("I've been bad")
53
+ # # => "I\'ve been bad"
54
+ #
55
+ def self.escape_single_commas(text)
56
+ text.sub("'", "\\\\'")
57
+ end
41
58
  end
42
59
  end
@@ -1,4 +1,4 @@
1
1
  module Spinach
2
2
  # Spinach version.
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.4"
4
4
  end
data/lib/spinach.rb CHANGED
@@ -8,6 +8,7 @@ require_relative 'spinach/dsl'
8
8
  require_relative 'spinach/feature_steps'
9
9
  require_relative 'spinach/reporter'
10
10
  require_relative 'spinach/cli'
11
+ require_relative 'spinach/generators'
11
12
 
12
13
  # Spinach is a BDD framework leveraging the great Gherkin language. This
13
14
  # language is the one used defining features in Cucumber, the BDD framework
data/spinach.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |gem|
22
22
  gem.add_development_dependency 'pry'
23
23
  gem.add_development_dependency 'simplecov'
24
24
  gem.add_development_dependency 'rspec'
25
+ gem.add_development_dependency 'fakefs'
25
26
 
26
27
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
28
  gem.files = `git ls-files`.split("\n")
@@ -36,9 +36,9 @@ describe Spinach::FeatureSteps::Capybara do
36
36
 
37
37
  it 'resets the capybara session after each scenario' do
38
38
  @feature_runner = Spinach::Runner::Feature.new(
39
- stub_everything)
39
+ 'a_feature.feature')
40
40
 
41
- Spinach::Parser.any_instance.stubs(content: '
41
+ @feature_runner.stubs(data: Spinach::Parser.new('
42
42
  Feature: A test feature
43
43
  Scenario: A test scenario
44
44
  Given Hello
@@ -46,7 +46,7 @@ describe Spinach::FeatureSteps::Capybara do
46
46
  Scenario: Another test scenario
47
47
  Given Hello
48
48
  Then Goodbye
49
- ').at_least_once
49
+ ').parse).at_least_once
50
50
 
51
51
  Spinach::Runner::Scenario.any_instance.stubs(feature: @feature)
52
52
 
@@ -17,6 +17,17 @@ describe Spinach::Cli do
17
17
  end
18
18
  end
19
19
  end
20
+
21
+ describe 'backtrace' do
22
+ %w{-g --generate}.each do |opt|
23
+ it 'inits the generator if #{opt}' do
24
+ Spinach::Generators.expects(:bind)
25
+ cli = Spinach::Cli.new([opt])
26
+ options = cli.options
27
+ end
28
+ end
29
+ end
30
+
20
31
  describe "version" do
21
32
  %w{-v --version}.each do |opt|
22
33
  it "outputs the version" do
@@ -0,0 +1,128 @@
1
+ require_relative '../../test_helper'
2
+ require_relative '../../../lib/spinach/generators'
3
+
4
+ module Spinach::Generators
5
+ describe FeatureGenerator do
6
+ subject do
7
+ FeatureGenerator.new(data)
8
+ end
9
+
10
+ let(:data) do
11
+ Spinach::Parser.new("
12
+ Feature: Cheezburger can I has
13
+ Scenario: Some Lulz
14
+ Given I haz a sad
15
+ When I get some lulz
16
+ Then I haz a happy
17
+ Scenario: Some sad
18
+ Given I haz a happy
19
+ When I get some lulz
20
+ Then I am OMG ROFLMAO").parse
21
+ end
22
+
23
+ describe "#name" do
24
+ it "returns the scenario name" do
25
+ subject.name.must_equal 'Cheezburger can I has'
26
+ end
27
+
28
+ it "returns nil if no name present" do
29
+ data['name'] = nil
30
+ subject.name.must_equal nil
31
+ end
32
+ end
33
+
34
+ describe "#steps" do
35
+ it "returns a correct number different steps for this data" do
36
+ subject.steps.length.must_equal 4
37
+ end
38
+
39
+ describe "includes the correct steps" do
40
+ it "includes 'Given I haz a sad'" do
41
+ subject.steps.any?{ |step|
42
+ (step['keyword'] == 'Given') && (step['name'] == 'I haz a sad')
43
+ }.must_equal true
44
+ end
45
+
46
+ it "includes 'When I get some lulz'" do
47
+ subject.steps.any?{ |step|
48
+ (step['keyword'] == 'When') && (step['name'] == 'I get some lulz')
49
+ }.must_equal true
50
+ end
51
+
52
+ it "includes 'Then I has a happy'" do
53
+ subject.steps.any?{ |step|
54
+ (step['keyword'] == 'Then') && (step['name'] == 'I haz a happy')
55
+ }.must_equal true
56
+ end
57
+
58
+ it "does not include 'Given I haz a happy'" do
59
+ subject.steps.any?{ |step|
60
+ (step['keyword'] == 'Given') && (step['name'] == 'I haz a happy')
61
+ }.must_equal false
62
+ end
63
+
64
+ it "includes 'Then I am OMG ROFLMAO'" do
65
+ subject.steps.any?{ |step|
66
+ (step['keyword'] == 'Then') && (step['name'] == 'I am OMG ROFLMAO')
67
+ }.must_equal true
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "#generate" do
73
+ it "generates an entire feature_steps class definition" do
74
+ result = subject.generate
75
+ klass = eval(result)
76
+ feature_runner = Spinach::Runner::Feature.new(stub_everything)
77
+ feature_runner.stubs(data: data)
78
+ feature_runner.run.must_equal true
79
+ end
80
+ end
81
+
82
+ describe "#filename" do
83
+ it "returns a valid filename for the feature" do
84
+ subject.filename.must_equal "cheezburger_can_i_has.rb"
85
+ end
86
+ end
87
+
88
+ describe "#path" do
89
+ it "should return a valid path" do
90
+ subject.path.must_include 'features/steps'
91
+ end
92
+ end
93
+
94
+ describe "#filename_with_path" do
95
+ it "should the filename prepended with the path" do
96
+ subject.filename_with_path.
97
+ must_include 'features/steps/cheezburger_can_i_has.rb'
98
+ end
99
+ end
100
+
101
+ describe "#store" do
102
+ it "stores the generated feature into a file" do
103
+ FakeFS.activate!
104
+ subject.store
105
+ File.directory?("features/steps/").must_equal true
106
+ File.exists?("features/steps/cheezburger_can_i_has.rb").must_equal true
107
+ File.read("features/steps/cheezburger_can_i_has.rb").strip.must_equal(
108
+ subject.generate.strip
109
+ )
110
+ FileUtils.rm_rf("features/steps")
111
+ FakeFS.deactivate!
112
+ end
113
+
114
+ it "raises an error if the file already exists and does nothing" do
115
+ file = "features/steps/cheezburger_can_i_has.rb"
116
+ FakeFS.activate!
117
+ FileUtils.mkdir_p "features/steps"
118
+ File.open(file, 'w') do |f|
119
+ f.write("Fake content")
120
+ end
121
+ Proc.new{subject.store}.must_raise(
122
+ Spinach::Generators::FeatureGeneratorException)
123
+ FileUtils.rm_rf("features/steps")
124
+ FakeFS.deactivate!
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,21 @@
1
+ require_relative '../../test_helper'
2
+ require_relative '../../../lib/spinach/generators'
3
+
4
+ module Spinach::Generators
5
+ describe StepGenerator do
6
+ subject do
7
+ StepGenerator.new(data)
8
+ end
9
+
10
+ let(:data) do
11
+ {'keyword' => 'Given', 'name' => "I has a sad"}
12
+ end
13
+
14
+ describe "#generate" do
15
+ it "generates a step" do
16
+ subject.generate.must_match /Given.*I has a sad/
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe Spinach::Generators do
4
+ subject do
5
+ Spinach::Generators
6
+ end
7
+
8
+ let(:data) do
9
+ Spinach::Parser.new("
10
+ Feature: Cheezburger can I has
11
+ Scenario: Some Lulz
12
+ Given I haz a sad
13
+ When I get some lulz
14
+ Then I haz a happy
15
+ Scenario: Some sad
16
+ Given I haz a happy
17
+ When I get some lulz
18
+ Then I am OMG ROFLMAO").parse
19
+ end
20
+
21
+ describe "#bind" do
22
+ it "binds the generator to the missing feature hook" do
23
+ subject.expects(:generate_feature).with(data)
24
+ subject.bind
25
+ Spinach::Runner::Feature.new(stub_everything).run_hook :when_not_found, data
26
+ Spinach::Runner::Feature._when_not_found_callbacks = []
27
+ end
28
+ end
29
+
30
+ describe "#generate_feature" do
31
+ it "generates a file" do
32
+ feature_data = data
33
+ FakeFS.activate!
34
+ subject.generate_feature(feature_data)
35
+ File.exists?("features/steps/cheezburger_can_i_has.rb").must_equal true
36
+ FileUtils.rm_rf("features/steps")
37
+ FakeFS.deactivate!
38
+ end
39
+ it "outputs a message if feature cannot be generated" do
40
+ subject::FeatureGenerator.expects(:new).raises(
41
+ Spinach::Generators::FeatureGeneratorException.new("File already exists"))
42
+ capture_stdout do
43
+ subject.generate_feature(data)
44
+ end.must_include "File already exists"
45
+ File.exists?("features/steps/cheezburger_can_i_has.rb").must_equal false
46
+ end
47
+ end
48
+ end
@@ -2,22 +2,18 @@ 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
- end
7
-
8
- let(:parsed) { @parser.parse }
9
-
10
- describe '#parse' do
11
- before do
12
- @parser.stubs(:content).returns("
13
- Feature: User authentication
5
+ @parser = Spinach::Parser.new(
6
+ "Feature: User authentication
14
7
  Scenario: User logs in
15
8
  Given I am on the front page
16
9
  When I fill in the login form and press 'login'
17
10
  Then I should be on my dashboard
18
11
  ")
19
- end
12
+ end
20
13
 
14
+ let(:parsed) { @parser.parse }
15
+
16
+ describe '#parse' do
21
17
  it 'parses the feature name' do
22
18
  parsed['name'].must_equal 'User authentication'
23
19
  end
@@ -41,10 +37,11 @@ describe Spinach::Parser do
41
37
  end
42
38
  end
43
39
 
44
- describe '#content' do
40
+ describe '.open_file' do
45
41
  it 'reads the disk and returns the file content' do
46
42
  File.expects(:read).with('feature_definition.feature')
47
- @parser.content
43
+ @parser = Spinach::Parser.open_file(
44
+ 'feature_definition.feature')
48
45
  end
49
46
  end
50
47
  end
@@ -68,14 +68,27 @@ describe Spinach::Reporter::Stdout do
68
68
  end
69
69
 
70
70
  describe '#on_successful_step' do
71
+ let(:step) { {'keyword' => 'Given', 'name' => 'I am too cool'} }
71
72
  let(:step_location){['error_step_location', 1]}
72
73
  it 'adds the step to the output buffer' do
73
- @reporter.on_successful_step({'keyword' => 'Given', 'name' => 'I am too cool'}, step_location)
74
+ @reporter.on_successful_step(step, step_location)
74
75
 
75
76
  @out.string.must_include '✔'
76
77
  @out.string.must_include 'Given'
77
78
  @out.string.must_include 'am too cool'
78
79
  end
80
+
81
+ it 'sets the current scenario' do
82
+ @reporter.on_successful_step(step, step_location)
83
+
84
+ @reporter.scenario.must_include step
85
+ end
86
+
87
+ it 'adds the step to the successful steps' do
88
+ @reporter.on_successful_step(step, step_location)
89
+
90
+ @reporter.successful_steps.last.must_include step
91
+ end
79
92
  end
80
93
 
81
94
  describe '#on_failed_step' do
@@ -238,6 +251,14 @@ describe Spinach::Reporter::Stdout do
238
251
  end
239
252
  end
240
253
 
254
+ describe '#run_summary' do
255
+ it 'prints a run summary' do
256
+ @reporter.run_summary
257
+
258
+ @out.string.must_include 'Steps Summary:'
259
+ end
260
+ end
261
+
241
262
  describe '#report_error_steps' do
242
263
  describe 'when some steps have raised an error' do
243
264
  it 'outputs the errors' do
@@ -389,7 +410,7 @@ describe Spinach::Reporter::Stdout do
389
410
  describe 'when given an undefined step exception' do
390
411
  it 'prints the error in yellow' do
391
412
  undefined_error = error
392
- undefined_error.insert(3, Spinach::StepNotDefinedException.new(anything, anything))
413
+ undefined_error.insert(3, Spinach::StepNotDefinedException.new(anything))
393
414
 
394
415
  String.any_instance.expects(:yellow)
395
416
 
@@ -455,7 +476,7 @@ describe Spinach::Reporter::Stdout do
455
476
 
456
477
  describe 'when given an undefined step exception' do
457
478
  it 'prints the error in yellow' do
458
- undefined_exception = Spinach::StepNotDefinedException.new(anything, anything)
479
+ undefined_exception = Spinach::StepNotDefinedException.new(anything)
459
480
 
460
481
  String.any_instance.expects(:yellow)
461
482
 
@@ -21,7 +21,7 @@ describe Spinach::Runner::Feature do
21
21
  it 'returns the parsed data' do
22
22
  parsed_data = {name: 'A cool feature'}
23
23
  parser = stub(parse: parsed_data)
24
- Spinach::Parser.expects(:new).returns(parser)
24
+ Spinach::Parser.expects(:open_file).returns(parser)
25
25
  feature.data.must_equal parsed_data
26
26
  end
27
27
  end
@@ -14,8 +14,8 @@ describe Spinach::Runner::Scenario do
14
14
 
15
15
  let(:feature) { stub_everything }
16
16
  let(:feature_name) { 'My feature' }
17
- let(:scenario) {
18
- scenario = Spinach::Runner::Scenario.new(feature_name, data)
17
+ let(:scenario) {
18
+ scenario = Spinach::Runner::Scenario.new(feature_name, data)
19
19
  scenario.stubs(feature: feature)
20
20
  scenario
21
21
  }
@@ -62,7 +62,7 @@ describe Spinach::Runner::Scenario do
62
62
 
63
63
  it 'rescues a Spinach::StepNotDefinedException' do
64
64
  feature.expects(:execute_step).raises(
65
- Spinach::StepNotDefinedException.new('foo', 'bar'))
65
+ Spinach::StepNotDefinedException.new('bar'))
66
66
  scenario.expects(:run_hook).with(:before_run, has_value("A cool scenario"))
67
67
  scenario.expects(:run_hook).with(:after_run, has_value("A cool scenario"))
68
68
  scenario.expects(:run_hook).with(
@@ -96,14 +96,14 @@ describe Spinach::Runner::Scenario do
96
96
 
97
97
  describe 'hooks' do
98
98
  it 'fires up the scenario hooks' do
99
- feature.expects(:execute_step).raises(Spinach::StepNotDefinedException.new('foo', 'bar'))
99
+ feature.expects(:execute_step).raises(Spinach::StepNotDefinedException.new('bar'))
100
100
  feature.expects(:run_hook).with(:before_scenario, has_value("A cool scenario"))
101
101
  feature.expects(:run_hook).with(:after_scenario, has_value("A cool scenario"))
102
102
  scenario.run
103
103
  end
104
104
 
105
105
  it 'fires up the step hooks' do
106
- feature.expects(:execute_step).raises(Spinach::StepNotDefinedException.new('foo', 'bar'))
106
+ feature.expects(:execute_step).raises(Spinach::StepNotDefinedException.new('bar'))
107
107
  %w{before_step after_step}.each do |hook|
108
108
  feature.expects(:run_hook).with(
109
109
  hook.to_sym, kind_of(Hash))
@@ -55,4 +55,12 @@ describe Spinach::Support do
55
55
  Spinach::Support.underscore(:FeatureName).must_equal 'feature_name'
56
56
  end
57
57
  end
58
+
59
+ describe "#escape" do
60
+ it "escapes the name" do
61
+ Spinach::Support.escape_single_commas(
62
+ "I've been doing bad things"
63
+ ).must_include "I\\'ve been doing bad things"
64
+ end
65
+ end
58
66
  end
data/test/test_helper.rb CHANGED
@@ -16,6 +16,7 @@ require 'ostruct'
16
16
  require 'purdytest'
17
17
  require 'stringio'
18
18
  require 'pry'
19
+ require 'fakefs/safe'
19
20
 
20
21
  require 'spinach'
21
22
  require 'spinach/capybara'
@@ -24,9 +25,11 @@ module Kernel
24
25
  def capture_stdout
25
26
  out = StringIO.new
26
27
  $stdout = out
28
+ $stderr = out
27
29
  yield
28
- return out
30
+ return out.string
29
31
  ensure
30
32
  $stdout = STDOUT
33
+ $stdout = STDERR
31
34
  end
32
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spinach
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,11 +12,11 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2011-10-10 00:00:00.000000000Z
15
+ date: 2011-10-11 00:00:00.000000000Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: gherkin
19
- requirement: &2158625180 !ruby/object:Gem::Requirement
19
+ requirement: &2152679240 !ruby/object:Gem::Requirement
20
20
  none: false
21
21
  requirements:
22
22
  - - ! '>='
@@ -24,10 +24,10 @@ dependencies:
24
24
  version: '0'
25
25
  type: :runtime
26
26
  prerelease: false
27
- version_requirements: *2158625180
27
+ version_requirements: *2152679240
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: minitest
30
- requirement: &2158624760 !ruby/object:Gem::Requirement
30
+ requirement: &2152678820 !ruby/object:Gem::Requirement
31
31
  none: false
32
32
  requirements:
33
33
  - - ! '>='
@@ -35,10 +35,10 @@ dependencies:
35
35
  version: '0'
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *2158624760
38
+ version_requirements: *2152678820
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: colorize
41
- requirement: &2158624340 !ruby/object:Gem::Requirement
41
+ requirement: &2152678400 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ! '>='
@@ -46,10 +46,10 @@ dependencies:
46
46
  version: '0'
47
47
  type: :runtime
48
48
  prerelease: false
49
- version_requirements: *2158624340
49
+ version_requirements: *2152678400
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: hooks
52
- requirement: &2158623920 !ruby/object:Gem::Requirement
52
+ requirement: &2152677960 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ! '>='
@@ -57,10 +57,10 @@ dependencies:
57
57
  version: '0'
58
58
  type: :runtime
59
59
  prerelease: false
60
- version_requirements: *2158623920
60
+ version_requirements: *2152677960
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: purdytest
63
- requirement: &2158623500 !ruby/object:Gem::Requirement
63
+ requirement: &2152677520 !ruby/object:Gem::Requirement
64
64
  none: false
65
65
  requirements:
66
66
  - - ! '>='
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: '0'
69
69
  type: :development
70
70
  prerelease: false
71
- version_requirements: *2158623500
71
+ version_requirements: *2152677520
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: rake
74
- requirement: &2158623080 !ruby/object:Gem::Requirement
74
+ requirement: &2152677060 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ! '>='
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: '0'
80
80
  type: :development
81
81
  prerelease: false
82
- version_requirements: *2158623080
82
+ version_requirements: *2152677060
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: mocha
85
- requirement: &2158622660 !ruby/object:Gem::Requirement
85
+ requirement: &2152676620 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ! '>='
@@ -90,10 +90,10 @@ dependencies:
90
90
  version: '0'
91
91
  type: :development
92
92
  prerelease: false
93
- version_requirements: *2158622660
93
+ version_requirements: *2152676620
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: sinatra
96
- requirement: &2158622240 !ruby/object:Gem::Requirement
96
+ requirement: &2152676180 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ! '>='
@@ -101,10 +101,10 @@ dependencies:
101
101
  version: '0'
102
102
  type: :development
103
103
  prerelease: false
104
- version_requirements: *2158622240
104
+ version_requirements: *2152676180
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: capybara
107
- requirement: &2158621820 !ruby/object:Gem::Requirement
107
+ requirement: &2152675720 !ruby/object:Gem::Requirement
108
108
  none: false
109
109
  requirements:
110
110
  - - ! '>='
@@ -112,10 +112,10 @@ dependencies:
112
112
  version: '0'
113
113
  type: :development
114
114
  prerelease: false
115
- version_requirements: *2158621820
115
+ version_requirements: *2152675720
116
116
  - !ruby/object:Gem::Dependency
117
117
  name: aruba
118
- requirement: &2158621400 !ruby/object:Gem::Requirement
118
+ requirement: &2152675280 !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
121
121
  - - ! '>='
@@ -123,10 +123,10 @@ dependencies:
123
123
  version: '0'
124
124
  type: :development
125
125
  prerelease: false
126
- version_requirements: *2158621400
126
+ version_requirements: *2152675280
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: pry
129
- requirement: &2158620960 !ruby/object:Gem::Requirement
129
+ requirement: &2152674840 !ruby/object:Gem::Requirement
130
130
  none: false
131
131
  requirements:
132
132
  - - ! '>='
@@ -134,10 +134,10 @@ dependencies:
134
134
  version: '0'
135
135
  type: :development
136
136
  prerelease: false
137
- version_requirements: *2158620960
137
+ version_requirements: *2152674840
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: simplecov
140
- requirement: &2158620540 !ruby/object:Gem::Requirement
140
+ requirement: &2152674400 !ruby/object:Gem::Requirement
141
141
  none: false
142
142
  requirements:
143
143
  - - ! '>='
@@ -145,10 +145,10 @@ dependencies:
145
145
  version: '0'
146
146
  type: :development
147
147
  prerelease: false
148
- version_requirements: *2158620540
148
+ version_requirements: *2152674400
149
149
  - !ruby/object:Gem::Dependency
150
150
  name: rspec
151
- requirement: &2158620120 !ruby/object:Gem::Requirement
151
+ requirement: &2152673980 !ruby/object:Gem::Requirement
152
152
  none: false
153
153
  requirements:
154
154
  - - ! '>='
@@ -156,7 +156,18 @@ dependencies:
156
156
  version: '0'
157
157
  type: :development
158
158
  prerelease: false
159
- version_requirements: *2158620120
159
+ version_requirements: *2152673980
160
+ - !ruby/object:Gem::Dependency
161
+ name: fakefs
162
+ requirement: &2152673560 !ruby/object:Gem::Requirement
163
+ none: false
164
+ requirements:
165
+ - - ! '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ type: :development
169
+ prerelease: false
170
+ version_requirements: *2152673560
160
171
  description: Spinach is a BDD framework on top of gherkin
161
172
  email:
162
173
  - info@codegram.com
@@ -179,11 +190,13 @@ files:
179
190
  - bin/spinach
180
191
  - features/exit_status.feature
181
192
  - features/feature_name_guessing.feature
193
+ - features/generate_features.feature
182
194
  - features/reporting/display_run_summary.feature
183
195
  - features/reporting/error_reporting.feature
184
196
  - features/reporting/show_step_source_location.feature
185
197
  - features/reporting/undefined_feature_reporting.feature
186
198
  - features/rspec_compatibility.feature
199
+ - features/steps/automatic_feature_generation.rb
187
200
  - features/steps/exit_status.rb
188
201
  - features/steps/feature_name_guessing.rb
189
202
  - features/steps/reporting/display_run_summary.rb
@@ -201,6 +214,9 @@ files:
201
214
  - lib/spinach/dsl.rb
202
215
  - lib/spinach/exceptions.rb
203
216
  - lib/spinach/feature_steps.rb
217
+ - lib/spinach/generators.rb
218
+ - lib/spinach/generators/feature_generator.rb
219
+ - lib/spinach/generators/step_generator.rb
204
220
  - lib/spinach/parser.rb
205
221
  - lib/spinach/reporter.rb
206
222
  - lib/spinach/reporter/stdout.rb
@@ -219,6 +235,9 @@ files:
219
235
  - test/spinach/config_test.rb
220
236
  - test/spinach/dsl_test.rb
221
237
  - test/spinach/feature_steps_test.rb
238
+ - test/spinach/generators/feature_generator_test.rb
239
+ - test/spinach/generators/step_generator_test.rb
240
+ - test/spinach/generators_test.rb
222
241
  - test/spinach/parser_test.rb
223
242
  - test/spinach/reporter/stdout_test.rb
224
243
  - test/spinach/reporter_test.rb
@@ -243,7 +262,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
243
262
  version: '0'
244
263
  segments:
245
264
  - 0
246
- hash: 317238591053879963
265
+ hash: 3283376077311958142
247
266
  required_rubygems_version: !ruby/object:Gem::Requirement
248
267
  none: false
249
268
  requirements:
@@ -252,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
252
271
  version: '0'
253
272
  segments:
254
273
  - 0
255
- hash: 317238591053879963
274
+ hash: 3283376077311958142
256
275
  requirements: []
257
276
  rubyforge_project:
258
277
  rubygems_version: 1.8.6
@@ -262,11 +281,13 @@ summary: Spinach is a BDD framework on top of gherkin
262
281
  test_files:
263
282
  - features/exit_status.feature
264
283
  - features/feature_name_guessing.feature
284
+ - features/generate_features.feature
265
285
  - features/reporting/display_run_summary.feature
266
286
  - features/reporting/error_reporting.feature
267
287
  - features/reporting/show_step_source_location.feature
268
288
  - features/reporting/undefined_feature_reporting.feature
269
289
  - features/rspec_compatibility.feature
290
+ - features/steps/automatic_feature_generation.rb
270
291
  - features/steps/exit_status.rb
271
292
  - features/steps/feature_name_guessing.rb
272
293
  - features/steps/reporting/display_run_summary.rb
@@ -283,6 +304,9 @@ test_files:
283
304
  - test/spinach/config_test.rb
284
305
  - test/spinach/dsl_test.rb
285
306
  - test/spinach/feature_steps_test.rb
307
+ - test/spinach/generators/feature_generator_test.rb
308
+ - test/spinach/generators/step_generator_test.rb
309
+ - test/spinach/generators_test.rb
286
310
  - test/spinach/parser_test.rb
287
311
  - test/spinach/reporter/stdout_test.rb
288
312
  - test/spinach/reporter_test.rb