spinach 0.1.3 → 0.1.4

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