cucumber2rspec 0.1.0

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