transpec 1.9.3 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +5 -0
  5. data/CONTRIBUTING.md +19 -0
  6. data/README.md +78 -9
  7. data/README.md.erb +68 -10
  8. data/lib/transpec/annotatable.rb +16 -0
  9. data/lib/transpec/cli.rb +35 -27
  10. data/lib/transpec/configuration.rb +1 -0
  11. data/lib/transpec/conversion_error.rb +23 -0
  12. data/lib/transpec/converter.rb +59 -50
  13. data/lib/transpec/dynamic_analyzer.rb +13 -29
  14. data/lib/transpec/dynamic_analyzer/rewriter.rb +3 -10
  15. data/lib/transpec/dynamic_analyzer/runtime_data.rb +16 -8
  16. data/lib/transpec/file_finder.rb +1 -1
  17. data/lib/transpec/git.rb +1 -1
  18. data/lib/transpec/option_parser.rb +12 -10
  19. data/lib/transpec/project.rb +3 -3
  20. data/lib/transpec/record.rb +19 -2
  21. data/lib/transpec/report.rb +29 -13
  22. data/lib/transpec/rspec_version.rb +7 -7
  23. data/lib/transpec/static_context_inspector.rb +1 -5
  24. data/lib/transpec/syntax.rb +11 -16
  25. data/lib/transpec/syntax/be_boolean.rb +1 -1
  26. data/lib/transpec/syntax/be_close.rb +1 -1
  27. data/lib/transpec/syntax/current_example.rb +88 -0
  28. data/lib/transpec/syntax/double.rb +1 -1
  29. data/lib/transpec/syntax/example.rb +60 -54
  30. data/lib/transpec/syntax/have.rb +27 -15
  31. data/lib/transpec/syntax/have/have_record.rb +12 -0
  32. data/lib/transpec/syntax/have/source_builder.rb +18 -16
  33. data/lib/transpec/syntax/its.rb +12 -11
  34. data/lib/transpec/syntax/matcher_definition.rb +1 -1
  35. data/lib/transpec/syntax/method_stub.rb +3 -7
  36. data/lib/transpec/syntax/mixin/matcher_owner.rb +2 -2
  37. data/lib/transpec/syntax/mixin/monkey_patch.rb +3 -5
  38. data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +2 -4
  39. data/lib/transpec/syntax/mixin/owned_matcher.rb +1 -4
  40. data/lib/transpec/syntax/mixin/send.rb +7 -9
  41. data/lib/transpec/syntax/oneliner_should.rb +4 -4
  42. data/lib/transpec/syntax/operator.rb +27 -11
  43. data/lib/transpec/syntax/pending.rb +110 -0
  44. data/lib/transpec/syntax/raise_error.rb +1 -1
  45. data/lib/transpec/syntax/receive.rb +4 -4
  46. data/lib/transpec/syntax/rspec_configure/framework.rb +3 -3
  47. data/lib/transpec/syntax/should.rb +2 -2
  48. data/lib/transpec/syntax/should_receive.rb +3 -3
  49. data/lib/transpec/util.rb +38 -6
  50. data/lib/transpec/version.rb +2 -2
  51. data/spec/support/file_helper.rb +1 -1
  52. data/spec/support/shared_context.rb +3 -8
  53. data/spec/transpec/cli_spec.rb +63 -1
  54. data/spec/transpec/configuration_spec.rb +1 -0
  55. data/spec/transpec/converter_spec.rb +106 -15
  56. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +12 -52
  57. data/spec/transpec/dynamic_analyzer_spec.rb +2 -2
  58. data/spec/transpec/option_parser_spec.rb +3 -2
  59. data/spec/transpec/report_spec.rb +33 -4
  60. data/spec/transpec/rspec_version_spec.rb +5 -2
  61. data/spec/transpec/syntax/current_example_spec.rb +267 -0
  62. data/spec/transpec/syntax/example_spec.rb +156 -122
  63. data/spec/transpec/syntax/have_spec.rb +43 -32
  64. data/spec/transpec/syntax/method_stub_spec.rb +8 -0
  65. data/spec/transpec/syntax/operator_spec.rb +67 -2
  66. data/spec/transpec/syntax/pending_spec.rb +375 -0
  67. metadata +12 -4
  68. data/lib/transpec/context_error.rb +0 -23
@@ -13,6 +13,7 @@ module Transpec
13
13
  [:convert_stub, true],
14
14
  [:convert_have_items, true],
15
15
  [:convert_its, true],
16
+ [:convert_pending, true],
16
17
  [:convert_deprecated_method, true],
17
18
  [:parenthesize_matcher_arg, true],
18
19
  [:add_receiver_arg_to_any_instance_implementation_block, true],
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/annotatable'
4
+
5
+ module Transpec
6
+ class ConversionError < StandardError
7
+ include Annotatable
8
+ end
9
+
10
+ class ContextError < ConversionError
11
+ def initialize(original_syntax, target_syntax, source_range)
12
+ message = build_message(original_syntax, target_syntax)
13
+ super(message, source_range)
14
+ end
15
+
16
+ private
17
+
18
+ def build_message(original_syntax, target_syntax)
19
+ "Cannot convert #{original_syntax} into #{target_syntax} " +
20
+ "since #{target_syntax} is not available in the context."
21
+ end
22
+ end
23
+ end
@@ -9,18 +9,17 @@ require 'transpec/syntax'
9
9
  Transpec::Syntax.require_all
10
10
 
11
11
  module Transpec
12
- class Converter < BaseRewriter
13
- attr_reader :configuration, :rspec_version, :runtime_data, :report, :context_errors
12
+ class Converter < BaseRewriter # rubocop:disable ClassLength
13
+ attr_reader :configuration, :rspec_version, :runtime_data, :report
14
14
 
15
15
  alias_method :convert_file!, :rewrite_file!
16
16
  alias_method :convert, :rewrite
17
17
 
18
- def initialize(configuration = nil, rspec_version = nil, runtime_data = nil, report = nil)
18
+ def initialize(configuration = nil, rspec_version = nil, runtime_data = nil)
19
19
  @configuration = configuration || Configuration.new
20
20
  @rspec_version = rspec_version || Transpec.current_rspec_version
21
21
  @runtime_data = runtime_data
22
- @report = report || Report.new
23
- @context_errors = []
22
+ @report = Report.new
24
23
  end
25
24
 
26
25
  def process(ast, source_rewriter)
@@ -32,9 +31,9 @@ module Transpec
32
31
 
33
32
  def dispatch_node(node, source_rewriter)
34
33
  Syntax.standalone_syntaxes.each do |syntax_class|
35
- next unless syntax_class.conversion_target_node?(node, @runtime_data)
34
+ next unless syntax_class.conversion_target_node?(node, runtime_data)
36
35
 
37
- syntax = syntax_class.new(node, source_rewriter, @runtime_data, @report)
36
+ syntax = syntax_class.new(node, source_rewriter, runtime_data, report)
38
37
 
39
38
  handler_name = "process_#{syntax_class.snake_case_name}"
40
39
  send(handler_name, syntax)
@@ -42,15 +41,15 @@ module Transpec
42
41
  break
43
42
  end
44
43
  rescue OverlappedRewriteError # rubocop:disable HandleExceptions
45
- rescue ContextError => error
46
- @context_errors << error
44
+ rescue ConversionError => error
45
+ report.conversion_errors << error
47
46
  end
48
47
 
49
48
  def process_should(should)
50
- if @configuration.convert_should?
49
+ if configuration.convert_should?
51
50
  should.expectize!(
52
- @configuration.negative_form_of_to,
53
- @configuration.parenthesize_matcher_arg?
51
+ configuration.negative_form_of_to,
52
+ configuration.parenthesize_matcher_arg?
54
53
  )
55
54
  end
56
55
 
@@ -59,22 +58,22 @@ module Transpec
59
58
  end
60
59
 
61
60
  def process_oneliner_should(oneliner_should)
62
- negative_form = @configuration.negative_form_of_to
63
- parenthesize = @configuration.parenthesize_matcher_arg?
61
+ negative_form = configuration.negative_form_of_to
62
+ parenthesize = configuration.parenthesize_matcher_arg?
64
63
 
65
64
  # TODO: Referencing oneliner_should.have_matcher.project_requires_collection_matcher?
66
65
  # from this converter is considered bad design.
67
- should_convert_have_items = @configuration.convert_have_items? &&
66
+ should_convert_have_items = configuration.convert_have_items? &&
68
67
  oneliner_should.have_matcher &&
69
68
  !oneliner_should.have_matcher.project_requires_collection_matcher?
70
69
 
71
70
  if should_convert_have_items
72
- if @configuration.convert_should?
71
+ if configuration.convert_should?
73
72
  oneliner_should.convert_have_items_to_standard_expect!(negative_form, parenthesize)
74
73
  else
75
74
  oneliner_should.convert_have_items_to_standard_should!
76
75
  end
77
- elsif @configuration.convert_oneliner? && @rspec_version.oneliner_is_expected_available?
76
+ elsif configuration.convert_oneliner? && rspec_version.oneliner_is_expected_available?
78
77
  oneliner_should.expectize!(negative_form, parenthesize)
79
78
  end
80
79
 
@@ -93,79 +92,89 @@ module Transpec
93
92
 
94
93
  def process_should_receive(should_receive)
95
94
  if should_receive.useless_expectation?
96
- if @configuration.convert_deprecated_method?
97
- if @configuration.convert_stub?
98
- should_receive.allowize_useless_expectation!(@configuration.negative_form_of_to)
95
+ if configuration.convert_deprecated_method?
96
+ if configuration.convert_stub?
97
+ should_receive.allowize_useless_expectation!(configuration.negative_form_of_to)
99
98
  else
100
99
  should_receive.stubize_useless_expectation!
101
100
  end
102
- elsif @configuration.convert_should_receive?
103
- should_receive.expectize!(@configuration.negative_form_of_to)
101
+ elsif configuration.convert_should_receive?
102
+ should_receive.expectize!(configuration.negative_form_of_to)
104
103
  end
105
- elsif @configuration.convert_should_receive?
106
- should_receive.expectize!(@configuration.negative_form_of_to)
104
+ elsif configuration.convert_should_receive?
105
+ should_receive.expectize!(configuration.negative_form_of_to)
107
106
  end
108
107
 
109
108
  process_messaging_host(should_receive)
110
109
  end
111
110
 
112
111
  def process_double(double)
113
- double.convert_to_double! if @configuration.convert_deprecated_method?
112
+ double.convert_to_double! if configuration.convert_deprecated_method?
114
113
  end
115
114
 
116
115
  def process_method_stub(method_stub)
117
- if @configuration.convert_stub?
116
+ if configuration.convert_stub?
118
117
  if !method_stub.hash_arg? ||
119
- @rspec_version.receive_messages_available? ||
120
- @configuration.convert_stub_with_hash_to_stub_and_return?
121
- method_stub.allowize!(@rspec_version)
122
- elsif @configuration.convert_deprecated_method?
118
+ rspec_version.receive_messages_available? ||
119
+ configuration.convert_stub_with_hash_to_stub_and_return?
120
+ method_stub.allowize!(rspec_version)
121
+ elsif configuration.convert_deprecated_method?
123
122
  method_stub.convert_deprecated_method!
124
123
  end
125
- elsif @configuration.convert_deprecated_method?
124
+ elsif configuration.convert_deprecated_method?
126
125
  method_stub.convert_deprecated_method!
127
126
  end
128
127
 
129
- method_stub.remove_no_message_allowance! if @configuration.convert_deprecated_method?
128
+ method_stub.remove_no_message_allowance! if configuration.convert_deprecated_method?
130
129
 
131
130
  process_messaging_host(method_stub)
132
131
  end
133
132
 
134
133
  def process_be_boolean(be_boolean)
135
- return unless @rspec_version.be_truthy_available?
136
- return unless @configuration.convert_deprecated_method?
134
+ return unless rspec_version.be_truthy_available?
135
+ return unless configuration.convert_deprecated_method?
137
136
 
138
- case @configuration.boolean_matcher_type
137
+ case configuration.boolean_matcher_type
139
138
  when :conditional
140
- be_boolean.convert_to_conditional_matcher!(@configuration.form_of_be_falsey)
139
+ be_boolean.convert_to_conditional_matcher!(configuration.form_of_be_falsey)
141
140
  when :exact
142
141
  be_boolean.convert_to_exact_matcher!
143
142
  end
144
143
  end
145
144
 
146
145
  def process_be_close(be_close)
147
- be_close.convert_to_be_within! if @configuration.convert_deprecated_method?
146
+ be_close.convert_to_be_within! if configuration.convert_deprecated_method?
148
147
  end
149
148
 
150
149
  def process_raise_error(raise_error)
151
150
  return unless raise_error
152
- if @configuration.convert_deprecated_method?
151
+ if configuration.convert_deprecated_method?
153
152
  raise_error.remove_error_specification_with_negative_expectation!
154
153
  end
155
154
  end
156
155
 
157
156
  def process_its(its)
158
- its.convert_to_describe_subject_it! if @configuration.convert_its?
157
+ its.convert_to_describe_subject_it! if configuration.convert_its?
159
158
  end
160
159
 
161
160
  def process_example(example)
162
- return unless @rspec_version.yielded_example_available?
163
- example.convert! if @configuration.convert_deprecated_method?
161
+ return if !rspec_version.rspec_2_99? || !configuration.convert_pending?
162
+ example.convert_pending_to_skip!
163
+ end
164
+
165
+ def process_pending(pending)
166
+ return if !rspec_version.rspec_2_99? || !configuration.convert_pending?
167
+ pending.convert_deprecated_syntax!
168
+ end
169
+
170
+ def process_current_example(current_example)
171
+ return unless rspec_version.yielded_example_available?
172
+ current_example.convert! if configuration.convert_deprecated_method?
164
173
  end
165
174
 
166
175
  def process_matcher_definition(matcher_definition)
167
- return unless @rspec_version.non_should_matcher_protocol_available?
168
- matcher_definition.convert_deprecated_method! if @configuration.convert_deprecated_method?
176
+ return unless rspec_version.non_should_matcher_protocol_available?
177
+ matcher_definition.convert_deprecated_method! if configuration.convert_deprecated_method?
169
178
  end
170
179
 
171
180
  def process_rspec_configure(rspec_configure)
@@ -177,7 +186,7 @@ module Transpec
177
186
  rspec_configure.mocks.syntaxes = :expect
178
187
  end
179
188
 
180
- if rspec_version.migration_term_of_any_instance_implementation_block? &&
189
+ if rspec_version.rspec_2_99? &&
181
190
  configuration.convert_deprecated_method?
182
191
  should_yield = configuration.add_receiver_arg_to_any_instance_implementation_block?
183
192
  rspec_configure.mocks.yield_receiver_to_any_instance_implementation_blocks = should_yield
@@ -185,8 +194,8 @@ module Transpec
185
194
  end
186
195
 
187
196
  def process_have(have)
188
- return if !have || !@configuration.convert_have_items?
189
- have.convert_to_standard_expectation!(@configuration.parenthesize_matcher_arg)
197
+ return if !have || !configuration.convert_have_items?
198
+ have.convert_to_standard_expectation!(configuration.parenthesize_matcher_arg)
190
199
  end
191
200
 
192
201
  def process_messaging_host(messaging_host)
@@ -202,22 +211,22 @@ module Transpec
202
211
 
203
212
  def process_any_instance_block(messaging_host)
204
213
  return unless messaging_host
205
- return unless rspec_version.migration_term_of_any_instance_implementation_block?
214
+ return unless rspec_version.rspec_2_99?
206
215
  return unless configuration.convert_deprecated_method?
207
216
  return unless configuration.add_receiver_arg_to_any_instance_implementation_block?
208
217
  messaging_host.add_receiver_arg_to_any_instance_implementation_block!
209
218
  end
210
219
 
211
220
  def need_to_modify_expectation_syntax_configuration?(rspec_configure)
212
- return false unless @configuration.convert_should?
221
+ return false unless configuration.convert_should?
213
222
  rspec_configure.expectations.syntaxes == [:should]
214
223
  rescue Syntax::RSpecConfigure::Framework::UnknownSyntaxError
215
224
  false
216
225
  end
217
226
 
218
227
  def need_to_modify_mock_syntax_configuration?(rspec_configure)
219
- return false if !@configuration.convert_should_receive? &&
220
- !@configuration.convert_stub?
228
+ return false if !configuration.convert_should_receive? &&
229
+ !configuration.convert_stub?
221
230
  rspec_configure.mocks.syntaxes == [:should]
222
231
  rescue Syntax::RSpecConfigure::Framework::UnknownSyntaxError
223
232
  false
@@ -13,7 +13,7 @@ require 'English'
13
13
 
14
14
  module Transpec
15
15
  class DynamicAnalyzer
16
- ANALYSIS_METHOD = 'transpec_analysis'
16
+ ANALYSIS_METHOD = 'transpec_analyze'
17
17
  HELPER_FILE = 'transpec_analysis_helper.rb'
18
18
  RESULT_FILE = 'transpec_analysis_result.json'
19
19
  HELPER_SOURCE = <<-END
@@ -26,16 +26,6 @@ module Transpec
26
26
  @data ||= {}
27
27
  end
28
28
 
29
- def self.node_id(filename, begin_pos, end_pos)
30
- absolute_path = File.expand_path(filename)
31
- relative_path = Pathname.new(absolute_path).relative_path_from(base_pathname).to_s
32
- [relative_path, begin_pos, end_pos].join('_')
33
- end
34
-
35
- def self.base_pathname
36
- @base_pathname ||= Pathname.new(@base_path)
37
- end
38
-
39
29
  at_exit do
40
30
  # Use JSON rather than Marshal so that:
41
31
  # * Unknown third-party class information won't be serialized.
@@ -50,28 +40,22 @@ module Transpec
50
40
  end
51
41
  end
52
42
 
53
- def #{ANALYSIS_METHOD}(object, context, analysis_codes, filename, begin_pos, end_pos)
54
- pair_array = analysis_codes.map do |key, (target_type, code)|
43
+ def #{ANALYSIS_METHOD}(object, context, node_id, analysis_codes)
44
+ node_data = {}
45
+
46
+ analysis_codes.each do |key, (target_type, code)|
55
47
  target = case target_type
56
48
  when :object then object
57
49
  when :context then context
58
50
  end
59
51
 
60
- eval_data = {}
61
-
62
52
  begin
63
- eval_data[:result] = target.instance_eval(code)
64
- rescue Exception => error
65
- eval_data[:error] = error
53
+ node_data[key] = target.instance_eval(code)
54
+ rescue Exception
66
55
  end
67
-
68
- [key, eval_data]
69
56
  end
70
57
 
71
- object_data = Hash[pair_array]
72
-
73
- id = TranspecAnalysis.node_id(filename, begin_pos, end_pos)
74
- TranspecAnalysis.data[id] = object_data
58
+ TranspecAnalysis.data[node_id] = node_data
75
59
 
76
60
  object
77
61
  end
@@ -93,7 +77,7 @@ module Transpec
93
77
  end
94
78
 
95
79
  def default_rspec_command
96
- if @project.require_bundler?
80
+ if project.require_bundler?
97
81
  'bundle exec rspec'
98
82
  else
99
83
  'rspec'
@@ -128,9 +112,9 @@ module Transpec
128
112
  @in_copied_project = true
129
113
 
130
114
  Dir.mktmpdir do |tmpdir|
131
- copy_recursively(@project.path, tmpdir)
132
- @copied_project_path = File.join(tmpdir, @project.basename)
133
- Dir.chdir(@copied_project_path) do
115
+ copy_recursively(project.path, tmpdir)
116
+ copied_project_path = File.join(tmpdir, project.basename)
117
+ Dir.chdir(copied_project_path) do
134
118
  yield
135
119
  end
136
120
  end
@@ -139,7 +123,7 @@ module Transpec
139
123
  end
140
124
 
141
125
  def run_rspec(paths)
142
- @project.with_bundler_clean_env do
126
+ project.with_bundler_clean_env do
143
127
  ENV['SPEC_OPTS'] = ['-r', "./#{HELPER_FILE}"].shelljoin
144
128
 
145
129
  command = "#{rspec_command} #{paths.shelljoin}"
@@ -54,11 +54,11 @@ module Transpec
54
54
 
55
55
  def process_requests(source_rewriter)
56
56
  requests.each do |node, analysis_codes|
57
- inject_analysis_method(node, analysis_codes, source_rewriter)
57
+ inject_analysis_code(node, analysis_codes, source_rewriter)
58
58
  end
59
59
  end
60
60
 
61
- def inject_analysis_method(node, analysis_codes, source_rewriter)
61
+ def inject_analysis_code(node, analysis_codes, source_rewriter)
62
62
  front, rear = build_wrapper_codes(node, analysis_codes)
63
63
 
64
64
  source_range = if (block_node = block_node_taken_by_method(node))
@@ -73,15 +73,8 @@ module Transpec
73
73
  end
74
74
 
75
75
  def build_wrapper_codes(node, analysis_codes)
76
- source_range = node.loc.expression
77
-
78
76
  front = "#{ANALYSIS_METHOD}(("
79
-
80
- rear = format(
81
- '), self, %s, __FILE__, %d, %d)',
82
- hash_literal(analysis_codes), source_range.begin_pos, source_range.end_pos
83
- )
84
-
77
+ rear = format('), self, %s, %s)', node_id(node).inspect, hash_literal(analysis_codes))
85
78
  [front, rear]
86
79
  end
87
80
 
@@ -1,5 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'transpec/util'
3
4
  require 'json'
4
5
  require 'ostruct'
5
6
  require 'pathname'
@@ -7,6 +8,8 @@ require 'pathname'
7
8
  module Transpec
8
9
  class DynamicAnalyzer
9
10
  class RuntimeData
11
+ include Util
12
+
10
13
  attr_reader :data
11
14
 
12
15
  def self.load(string_or_io)
@@ -19,16 +22,21 @@ module Transpec
19
22
  @data = data
20
23
  end
21
24
 
22
- def [](node)
23
- @data[node_id(node)]
25
+ def [](node, key = nil)
26
+ node_data = data[node_id(node)]
27
+ return nil unless node_data
28
+ return node_data unless key
29
+ node_data[key]
30
+ end
31
+
32
+ def run?(node)
33
+ !self[node].nil?
24
34
  end
25
35
 
26
- def node_id(node)
27
- source_range = node.loc.expression
28
- source_buffer = source_range.source_buffer
29
- absolute_path = File.expand_path(source_buffer.name)
30
- relative_path = Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
31
- [relative_path, source_range.begin_pos, source_range.end_pos].join('_')
36
+ def present?(node, key)
37
+ node_data = self[node]
38
+ return false unless node_data
39
+ node_data.respond_to?(key)
32
40
  end
33
41
 
34
42
  class CompatibleOpenStruct < OpenStruct
@@ -12,7 +12,7 @@ module Transpec
12
12
  file_paths.concat(ruby_files_in_directory(path))
13
13
  elsif File.file?(path)
14
14
  file_paths << path
15
- elsif !File.exists?(path)
15
+ elsif !File.exist?(path)
16
16
  fail ArgumentError, "No such file or directory #{path.inspect}"
17
17
  end
18
18
  end