cucumber2rspec 0.1.0

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,20 @@
1
+ #! /usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../lib/cucumber2rspec'
3
+ require 'logger'
4
+ Cucumber2RSpec.logger = Logger.new(STDERR)
5
+ Cucumber2RSpec.logger.level = Logger::DEBUG
6
+
7
+ unless ARGV.length >= 2
8
+ puts "Usage: cucumber2rspec dir/with/step_definitions feature1 feature2 ..."
9
+ exit
10
+ end
11
+
12
+ steps_dir = ARGV.shift
13
+ Cucumber2RSpec.log { "Loading steps ..." }
14
+ Dir[File.join(steps_dir, '**', '*_steps.rb')].each {|step_file| require step_file }
15
+
16
+ feature = ARGV.shift
17
+ Cucumber2RSpec.log { "Feature to convert: #{ File.basename(feature) }" }
18
+ puts Cucumber2RSpec.translate_file feature
19
+
20
+ # vim:set ft=ruby:
@@ -0,0 +1,87 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+
3
+ # Last tested with cucumber (0.3.11, 0.3.3)
4
+ #
5
+ # Note: String#indent comes from cucumber
6
+
7
+ require 'rubygems'
8
+ require 'parse_tree'
9
+ require 'parse_tree_extensions'
10
+ require 'ruby2ruby'
11
+ require 'cucumber'
12
+
13
+ $step_match = method(:step_match)
14
+
15
+ # ...
16
+ module Cucumber2RSpec
17
+
18
+ class << self
19
+ attr_accessor :logger
20
+ end
21
+
22
+ def self.log &block
23
+ logger.debug(&block) if logger
24
+ end
25
+
26
+ def self.parser
27
+ Cucumber.load_language('en')
28
+ @parser ||= Cucumber::Parser::FeatureParser.new
29
+ end
30
+
31
+ def self.step_match text
32
+ $step_match.call(text)
33
+ end
34
+
35
+ def self.translate feature_text
36
+ Cucumber2RSpec::Feature.new(feature_text).code
37
+ end
38
+
39
+ def self.translate_file filepath
40
+ Cucumber2RSpec.log { "translate_file(#{filepath.inspect})" }
41
+ translate File.read(filepath)
42
+ end
43
+
44
+ def self.replace_in_sexp sexp, to_replace, replace_with
45
+ while sexp.include?(to_replace)
46
+ index = sexp.index(to_replace)
47
+ sexp.delete_at index
48
+ sexp.insert index, replace_with
49
+ end
50
+
51
+ sexp.each do|obj|
52
+ replace_in_sexp(obj, to_replace, replace_with) if obj.is_a?(Sexp)
53
+ end
54
+
55
+ sexp
56
+ end
57
+
58
+ def self.block_variable_names a_proc
59
+ sexp = a_proc.to_sexp
60
+ local_assignments = []
61
+
62
+ # this is where the variables hang out ...
63
+ if sexp[2] and sexp[2].is_a?(Sexp)
64
+
65
+ # there's only 1 local assigned variable
66
+ if sexp[2][0] == :lasgn
67
+ local_assignments << sexp[2][1]
68
+
69
+ # there's an array of locally assigned variables
70
+ elsif sexp[2][0] == :masgn and sexp[2][1][0] == :array # mass assignment using an array
71
+ array_of_variables = a_proc.to_sexp[2][1]
72
+ the_type = array_of_variables.shift
73
+ array_of_variables.each do |sub_sexp|
74
+ local_assignments << sub_sexp[1] if sub_sexp[0] == :lasgn
75
+ end
76
+ end
77
+ end
78
+
79
+ local_assignments
80
+ end
81
+
82
+ end
83
+
84
+ require 'cucumber2rspec/feature'
85
+ require 'cucumber2rspec/background'
86
+ require 'cucumber2rspec/scenario'
87
+ require 'cucumber2rspec/step'
@@ -0,0 +1,29 @@
1
+ module Cucumber2RSpec #:nodoc:
2
+
3
+ # ...
4
+ class Background
5
+ attr_reader :feature, :_background
6
+
7
+ def initialize feature, cucumber_ast_background
8
+ @feature = feature
9
+ @_background = cucumber_ast_background # Cucumber::Ast::Background
10
+ end
11
+
12
+ def steps
13
+ _background.instance_variable_get('@steps').map {|step| Step.new(self, step) }
14
+ end
15
+
16
+ def code_without_block
17
+ Step.code_for_steps(steps)
18
+ end
19
+
20
+ def code
21
+ Cucumber2RSpec.log { ' Background' }
22
+ the_code = " before do\n"
23
+ the_code << code_without_block.sub(/\n$/, '') # kill the last newline
24
+ the_code << ' end'
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,35 @@
1
+ module Cucumber2RSpec #:nodoc:
2
+
3
+ # ...
4
+ class Feature
5
+ attr_reader :raw, :name, :_feature
6
+
7
+ def initialize raw_feature_text
8
+ @raw = raw_feature_text
9
+ @_feature = Cucumber2RSpec.parser.parse_or_fail(raw) # Cucumber::Ast::Feature
10
+ end
11
+
12
+ def name
13
+ # @_feature.name returns "Feature: Foo\n In order to ..."
14
+ match = _feature.name.match(/Feature: ([\w ]+)/)
15
+ match[1] if match
16
+ end
17
+
18
+ def background
19
+ _background = _feature.instance_variable_get('@background')
20
+ Background.new self, _background if _background
21
+ end
22
+
23
+ def scenarios
24
+ _feature.instance_variable_get('@feature_elements').map {|scenario| Scenario.new(self, scenario) }
25
+ end
26
+
27
+ def code
28
+ Cucumber2RSpec.log { name }
29
+ the_code = 'describe ' + name.inspect + ' do' + "\n\n"
30
+ the_code << background.code + "\n\n" if background
31
+ scenarios.each {|scenario| the_code << scenario.code + "\n\n" }
32
+ the_code << "end"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ module Cucumber2RSpec #:nodoc:
2
+
3
+ # ...
4
+ class Scenario
5
+ attr_reader :feature, :_scenario
6
+
7
+ def initialize feature, cucumber_ast_scenario
8
+ @feature = feature
9
+ @_scenario = cucumber_ast_scenario # Cucumber::Ast::Scenario
10
+ end
11
+
12
+ def name
13
+ _scenario.name
14
+ end
15
+
16
+ def steps
17
+ _scenario.instance_variable_get('@steps').map {|step| Step.new(self, step) }
18
+ end
19
+
20
+ def code_without_block
21
+ code = Step.code_for_steps(steps)
22
+
23
+ if background = feature.background
24
+ # remove the background code from the scenario
25
+ code = code.sub background.code_without_block.sub(/\n$/, ''), ''
26
+ else
27
+ code
28
+ end
29
+ end
30
+
31
+ def code
32
+ Cucumber2RSpec.log { ' ' + name }
33
+ the_code = ' it ' + name.inspect + " do\n"
34
+ the_code << code_without_block
35
+ the_code << ' end'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,159 @@
1
+ module Cucumber2RSpec #:nodoc:
2
+
3
+ # ...
4
+ class Step
5
+ attr_reader :scenario, :_step
6
+
7
+ def self.code_for_steps steps
8
+ the_code = ''
9
+ groups = {
10
+ 'Given' => [],
11
+ 'When' => [],
12
+ 'Then' => []
13
+ }
14
+
15
+ last_keyword = 'Given'
16
+ steps.each do |step|
17
+ keyword = (step.keyword == 'And') ? last_keyword : step.keyword
18
+ groups[keyword] << step.code
19
+ last_keyword = step.keyword unless step.keyword == 'And'
20
+ end
21
+
22
+ groups['Given'].each {|code| the_code << (code.indent(4) + "\n") }
23
+ the_code << "\n" if groups['Given'].length > 0
24
+
25
+ groups['When' ].each {|code| the_code << (code.indent(4) + "\n") }
26
+ the_code << "\n" if groups['When'].length > 0
27
+
28
+ groups['Then' ].each {|code| the_code << (code.indent(4) + "\n") }
29
+ the_code
30
+ end
31
+
32
+ def initialize scenario, cucumber_ast_step_invocation
33
+ @scenario = scenario
34
+ @_step = cucumber_ast_step_invocation # Cucumber::Ast::StepInvocation
35
+ end
36
+
37
+ def keyword
38
+ _step.respond_to?(:actual_keyword) ? _step.actual_keyword : _step.keyword
39
+ end
40
+
41
+ def table
42
+ _step.multiline_arg if _step.respond_to?(:multiline_arg)
43
+ end
44
+
45
+ def has_table?
46
+ !! table
47
+ end
48
+
49
+ def text
50
+ if has_table?
51
+ _step.to_sexp[ _step.to_sexp.length - 2 ] # the last argument will be the table!
52
+ else
53
+ _step.to_sexp.last
54
+ end
55
+ end
56
+
57
+ def full_text
58
+ "#{keyword} #{text}"
59
+ end
60
+
61
+ def _step_definition
62
+ Cucumber2RSpec.step_match(text).instance_variable_get('@step_definition')
63
+ end
64
+
65
+ def regexp
66
+ Regexp.new _step_definition.instance_variable_get('@regexp')
67
+ end
68
+
69
+ def raw_variable_names
70
+ Cucumber2RSpec.block_variable_names(the_proc)
71
+ end
72
+
73
+ def variable_names
74
+ # if has_table? then we ignore the last block variable (which is the table)
75
+ variables = raw_variable_names
76
+ variables.pop if has_table?
77
+ variables
78
+ end
79
+
80
+ def table_variable_name
81
+ raw_variable_names.last if has_table?
82
+ end
83
+
84
+ def the_proc
85
+ _step_definition.instance_variable_get('@proc')
86
+ end
87
+
88
+ def matches
89
+ return [] if variable_names.empty?
90
+
91
+ match = text.match(regexp)
92
+ if match
93
+ matches = {}
94
+ if match.captures.length != variable_names.length
95
+ raise "Problem getting #{ variable_names.inspect } from #{ text.inspect }."
96
+ end
97
+ variable_names.each_with_index do |name, i|
98
+ matches[name] = match.captures[i]
99
+ end
100
+ matches
101
+ else
102
+ []
103
+ end
104
+ end
105
+
106
+ def code
107
+ Cucumber2RSpec.log { ' ' + text }
108
+
109
+ if has_table?
110
+ # require cucumber and create a local variable defining the cucumber table
111
+ header = "require 'cucumber'\n"
112
+ header << "#{table_variable_name} = Cucumber::Ast::Table.new(#{ table.raw.inspect })\n"
113
+ end
114
+
115
+ if variable_names.empty?
116
+ ruby = the_proc.to_ruby
117
+
118
+ if has_table?
119
+ # get rid of proc { |table| }
120
+ ruby = ruby.sub(/^proc \{ \|#{table_variable_name}\|\s+/, '').sub(/\s\}$/, '')
121
+ else
122
+ # get rid of proc { }
123
+ ruby = ruby.sub(/^proc \{\s+/, '').sub(/\s\}$/, '')
124
+ end
125
+
126
+ else
127
+ # TODO replace with the_proc.to_sexp
128
+ sexp_for_proc = ParseTree.new.process(the_proc.to_ruby) # turn the proc into an Sexp
129
+ matches.each do |name, value|
130
+ str = Sexp.new(:str, value)
131
+
132
+ # s(:lvar, :var) => s(:str, "dogs")
133
+ Cucumber2RSpec.replace_in_sexp sexp_for_proc, Sexp.new(:lvar, name), str
134
+
135
+ # s(:evstr, s(:str, "dogs")) => s(:str, "dogs")
136
+ Cucumber2RSpec.replace_in_sexp sexp_for_proc, Sexp.new(:evstr, str), str
137
+ end
138
+ ruby = Ruby2Ruby.new.process(sexp_for_proc) # turn it back into ruby
139
+ if ruby =~ /^proc do/
140
+ # get rid of the proc do |x| end
141
+ ruby = ruby.sub(/^proc do \|([\w+, ]+)\|\n /, '').sub(/\send$/, '')
142
+ elsif ruby =~ /^proc \{ |/
143
+ # get rid of proc { |whatever, ...| }
144
+ ruby = ruby.sub(/^proc \{ \|([^\|]+)\|\s+/, '').sub(/\s\}$/, '')
145
+ elsif ruby =~ /^proc \{/
146
+ # get rid of proc { }
147
+ ruby = ruby.sub(/^proc \{\s+/, '').sub(/\s\}$/, '')
148
+ else
149
+ raise "Not sure how to clean up the code for: #{ ruby }"
150
+ end
151
+ end
152
+
153
+ ruby = header + ruby if has_table?
154
+
155
+ # get rid of any spaces after any newlines
156
+ ruby.gsub(/\n[ ]+/, "\n")
157
+ end
158
+ end
159
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cucumber2rspec
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - remi
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-31 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: cucumber
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 3
30
+ - 11
31
+ version: 0.3.11
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: ParseTree
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 3
43
+ - 0
44
+ - 3
45
+ version: 3.0.3
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: ruby2ruby
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 1
57
+ - 2
58
+ - 2
59
+ version: 1.2.2
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ description: Convert Cucumber features to RSpec
63
+ email: remi@remitaylor.com
64
+ executables:
65
+ - cucumber2rspec
66
+ extensions: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ files:
71
+ - lib/cucumber2rspec/background.rb
72
+ - lib/cucumber2rspec/step.rb
73
+ - lib/cucumber2rspec/scenario.rb
74
+ - lib/cucumber2rspec/feature.rb
75
+ - lib/cucumber2rspec.rb
76
+ - bin/cucumber2rspec
77
+ has_rdoc: true
78
+ homepage: http://github.com/devfu/cucumber2rspec
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.3.6
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: Convert Cucumber features to RSpec
107
+ test_files: []
108
+