transpec 1.9.3 → 1.10.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.
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