transpec 0.2.3 → 0.2.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51efb48bbde5bb4b866edc7919290fd09c1286ea
4
- data.tar.gz: 0a2a2f6ad3b6310f0dd4ecb53b8ac157c3ab9642
3
+ metadata.gz: e4282d3fbc1652d1d78046fe2a04392fea0cee1c
4
+ data.tar.gz: 6eab8ef44e5a6b8e672246b7fd420bf220cd7006
5
5
  SHA512:
6
- metadata.gz: 2141b0d96bcc0e92b4fa4652010f560abbe10745172b3cf2140fecaec9c454ef9e05ab61a6a73f8df6b0a2e4d13437bdae157c4ec78b95284cd10e6d6452854d
7
- data.tar.gz: d5a6b210ee1559ed1dc5b99f478959108f3a7a2d2b8815bd50961cca190430609266ee1cb946f859aef485f8cbac9373a8dead74605757f537a66713b3f8efe7
6
+ metadata.gz: c4e376be4d7ef6f6c33c2cdc9d31a66f2817d0902be4d83541b3d79f637e83c4b23f0a021791a623d4214ee215e3909ffbaa602e0357f0bc1b1989325cb7c3eb
7
+ data.tar.gz: 4d1b3b9346eea8526948a8475c4c0fba32e2c1c35a1dfeecba9eec5c69f3df11dc0387f38baba7f9b596bfc78e92c0b0c81c18a213998693fc5f5d9fddd5ac50
data/.rubocop.yml CHANGED
@@ -9,7 +9,7 @@ LineLength:
9
9
  Max: 100
10
10
 
11
11
  MethodLength:
12
- Max: 20
12
+ Max: 17
13
13
 
14
14
  WordArray:
15
15
  Enabled: false
@@ -48,10 +48,6 @@ Documentation:
48
48
  IndentationWidth:
49
49
  Enabled: false
50
50
 
51
- # Custom error classes require custom arguments.
52
- RaiseArgs:
53
- Enabled: false
54
-
55
- # Currently ClassLength counts innner class' codes in a namespace class.
51
+ # TODO: Shorten to 100.
56
52
  ClassLength:
57
- Enabled: false
53
+ Max: 186
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Master
4
4
 
5
+ ## v0.2.4
6
+
7
+ * Improve context detection
8
+
5
9
  ## v0.2.3
6
10
 
7
11
  * Fix a bug where arguments of positive error expectation with block were removed (e.g. `expect { }.to raise_error(SpecificErrorClass) { |e| ... }` was converted to `expect { }.to raise_error { |e| ... }` unintentionally)
data/README.md CHANGED
@@ -16,7 +16,7 @@ Note that Transpec does not yet support all conversions for the RSpec changes,
16
16
  and also the changes for RSpec 3 is not fixed and may vary in the future.
17
17
  So it's recommended to follow updates of both RSpec and Transpec.
18
18
 
19
- ## Example
19
+ ## Examples
20
20
 
21
21
  Here's an example spec:
22
22
 
@@ -91,6 +91,14 @@ describe Account do
91
91
  end
92
92
  ```
93
93
 
94
+ ### Real Examples
95
+
96
+ You can see real conversion examples below:
97
+
98
+ * https://github.com/yujinakayama/guard/commit/transpec-demo
99
+ * https://github.com/yujinakayama/mail/commit/transpec-demo
100
+ * https://github.com/yujinakayama/twitter/commit/transpec-demo
101
+
94
102
  ## Installation
95
103
 
96
104
  ```bash
@@ -234,12 +242,50 @@ describe 'converted spec with -p/--no-parentheses-matcher-arg option' do
234
242
  end
235
243
  ```
236
244
 
245
+ ## Troubleshooting
246
+
247
+ You might see the following warning while conversion:
248
+
249
+ ```
250
+ Cannot convert #should into #expect since #expect is not available in the context.
251
+ spec/awesome_spec.rb:4: 1.should == 1
252
+ ```
253
+
254
+ This message would be shown with specs like this:
255
+
256
+ ```ruby
257
+ describe '#should that cannot be converted to #expect' do
258
+ class MyAwesomeTestRunner
259
+ def run
260
+ 1.should == 1
261
+ end
262
+ end
263
+
264
+ it 'is 1' do
265
+ test_runner = MyAwesomeTestRunner.new
266
+ test_runner.run
267
+ end
268
+ end
269
+ ```
270
+
271
+ ### Reason
272
+
273
+ * `should` is defined on `Kernel` (included by `Object`), so you can use `should` almost everywhere.
274
+ * `expect` is defined on `RSpec::Matchers` (included by `RSpec::Core::ExampleGroup`), so you can use `expect` only where `self` is an instance of `RSpec::Core::ExampleGroup`.
275
+
276
+ With the above example, in the context of `1.should == 1`, the `self` is an instance of `MyAwesomeTestRunner`.
277
+ So Transpec tracks contexts and skips conversion if the target syntax cannot be converted in a case like this.
278
+
279
+ ### Solution
280
+
281
+ You need to rewrite the spec by yourself.
282
+
237
283
  ## Supported Conversions
238
284
 
239
285
  ### Standard expectations
240
286
 
241
287
  ```ruby
242
- # Target
288
+ # Targets
243
289
  obj.should matcher
244
290
  obj.should_not matcher
245
291
 
@@ -255,7 +301,7 @@ expect(obj).to_not matcher # with `--negative-form to_not`
255
301
  ### Operator matchers
256
302
 
257
303
  ```ruby
258
- # Target
304
+ # Targets
259
305
  1.should == 1
260
306
  1.should < 2
261
307
  Integer.should === 1
@@ -275,7 +321,7 @@ expect([1, 2, 3]).to match_array([2, 1, 3])
275
321
  ### `be_close` matcher
276
322
 
277
323
  ```ruby
278
- # Target
324
+ # Targets
279
325
  (1.0 / 3.0).should be_close(0.333, 0.001)
280
326
 
281
327
  # Converted
@@ -288,7 +334,7 @@ expect([1, 2, 3]).to match_array([2, 1, 3])
288
334
  ### Expectations on Proc
289
335
 
290
336
  ```ruby
291
- # Target
337
+ # Targets
292
338
  lambda { do_something }.should raise_error
293
339
  proc { do_something }.should raise_error
294
340
  -> { do_something }.should raise_error
@@ -303,7 +349,7 @@ expect { do_something }.to raise_error
303
349
  ### Negative error expectations with specific error
304
350
 
305
351
  ```ruby
306
- # Target
352
+ # Targets
307
353
  expect { do_something }.not_to raise_error(SomeErrorClass)
308
354
  expect { do_something }.not_to raise_error('message')
309
355
  expect { do_something }.not_to raise_error(SomeErrorClass, 'message')
@@ -320,7 +366,7 @@ lambda { do_something }.should_not raise_error # with `--disable expect_to_match
320
366
  ### Message expectations
321
367
 
322
368
  ```ruby
323
- # Target
369
+ # Targets
324
370
  obj.should_receive(:foo)
325
371
  SomeClass.any_instance.should_receive(:foo)
326
372
 
@@ -335,7 +381,7 @@ expect_any_instance_of(SomeClass).to receive(:foo)
335
381
  ### Message expectations that are actually method stubs
336
382
 
337
383
  ```ruby
338
- # Target
384
+ # Targets
339
385
  obj.should_receive(:foo).any_number_of_times
340
386
  obj.should_receive(:foo).at_least(0)
341
387
 
@@ -356,7 +402,7 @@ SomeClass.any_instance.stub(:foo) # with `--disable allow_to_receive`
356
402
  ### Method stubs
357
403
 
358
404
  ```ruby
359
- # Target
405
+ # Targets
360
406
  obj.stub(:foo)
361
407
 
362
408
  obj.stub!(:foo)
@@ -382,7 +428,7 @@ allow_any_instance_of(SomeClass).to receive(:foo)
382
428
  ### Deprecated method stub aliases
383
429
 
384
430
  ```ruby
385
- # Target
431
+ # Targets
386
432
  obj.stub!(:foo)
387
433
  obj.unstub!(:foo)
388
434
 
@@ -397,7 +443,7 @@ obj.unstub(:foo)
397
443
  ### Method stubs with deprecated specification of number of times
398
444
 
399
445
  ```ruby
400
- # Target
446
+ # Targets
401
447
  obj.stub(:foo).any_number_of_times
402
448
  obj.stub(:foo).at_least(0)
403
449
 
@@ -412,7 +458,7 @@ obj.stub(:foo) # with `--disable allow_to_receive`
412
458
  ### Deprecated test double aliases
413
459
 
414
460
  ```ruby
415
- # Target
461
+ # Targets
416
462
  stub('something')
417
463
  mock('something')
418
464
 
data/README.md.erb CHANGED
@@ -16,7 +16,7 @@ Note that Transpec does not yet support all conversions for the RSpec changes,
16
16
  and also the changes for RSpec 3 is not fixed and may vary in the future.
17
17
  So it's recommended to follow updates of both RSpec and Transpec.
18
18
 
19
- ## Example
19
+ ## Examples
20
20
 
21
21
  Here's an example spec:
22
22
 
@@ -64,6 +64,14 @@ Transpec would convert it to the following form:
64
64
  <%= Transpec::Rewriter.new.rewrite(example) -%>
65
65
  ```
66
66
 
67
+ ### Real Examples
68
+
69
+ You can see real conversion examples below:
70
+
71
+ * https://github.com/yujinakayama/guard/commit/transpec-demo
72
+ * https://github.com/yujinakayama/mail/commit/transpec-demo
73
+ * https://github.com/yujinakayama/twitter/commit/transpec-demo
74
+
67
75
  ## Installation
68
76
 
69
77
  ```bash
@@ -230,12 +238,50 @@ rewriter.rewrite(parenthesizing_example)
230
238
  -%>
231
239
  ```
232
240
 
241
+ ## Troubleshooting
242
+
243
+ You might see the following warning while conversion:
244
+
245
+ ```
246
+ Cannot convert #should into #expect since #expect is not available in the context.
247
+ spec/awesome_spec.rb:4: 1.should == 1
248
+ ```
249
+
250
+ This message would be shown with specs like this:
251
+
252
+ ```ruby
253
+ describe '#should that cannot be converted to #expect' do
254
+ class MyAwesomeTestRunner
255
+ def run
256
+ 1.should == 1
257
+ end
258
+ end
259
+
260
+ it 'is 1' do
261
+ test_runner = MyAwesomeTestRunner.new
262
+ test_runner.run
263
+ end
264
+ end
265
+ ```
266
+
267
+ ### Reason
268
+
269
+ * `should` is defined on `Kernel` (included by `Object`), so you can use `should` almost everywhere.
270
+ * `expect` is defined on `RSpec::Matchers` (included by `RSpec::Core::ExampleGroup`), so you can use `expect` only where `self` is an instance of `RSpec::Core::ExampleGroup`.
271
+
272
+ With the above example, in the context of `1.should == 1`, the `self` is an instance of `MyAwesomeTestRunner`.
273
+ So Transpec tracks contexts and skips conversion if the target syntax cannot be converted in a case like this.
274
+
275
+ ### Solution
276
+
277
+ You need to rewrite the spec by yourself.
278
+
233
279
  ## Supported Conversions
234
280
 
235
281
  ### Standard expectations
236
282
 
237
283
  ```ruby
238
- # Target
284
+ # Targets
239
285
  obj.should matcher
240
286
  obj.should_not matcher
241
287
 
@@ -251,7 +297,7 @@ expect(obj).to_not matcher # with `--negative-form to_not`
251
297
  ### Operator matchers
252
298
 
253
299
  ```ruby
254
- # Target
300
+ # Targets
255
301
  1.should == 1
256
302
  1.should < 2
257
303
  Integer.should === 1
@@ -271,7 +317,7 @@ expect([1, 2, 3]).to match_array([2, 1, 3])
271
317
  ### `be_close` matcher
272
318
 
273
319
  ```ruby
274
- # Target
320
+ # Targets
275
321
  (1.0 / 3.0).should be_close(0.333, 0.001)
276
322
 
277
323
  # Converted
@@ -284,7 +330,7 @@ expect([1, 2, 3]).to match_array([2, 1, 3])
284
330
  ### Expectations on Proc
285
331
 
286
332
  ```ruby
287
- # Target
333
+ # Targets
288
334
  lambda { do_something }.should raise_error
289
335
  proc { do_something }.should raise_error
290
336
  -> { do_something }.should raise_error
@@ -299,7 +345,7 @@ expect { do_something }.to raise_error
299
345
  ### Negative error expectations with specific error
300
346
 
301
347
  ```ruby
302
- # Target
348
+ # Targets
303
349
  expect { do_something }.not_to raise_error(SomeErrorClass)
304
350
  expect { do_something }.not_to raise_error('message')
305
351
  expect { do_something }.not_to raise_error(SomeErrorClass, 'message')
@@ -316,7 +362,7 @@ lambda { do_something }.should_not raise_error # with `--disable expect_to_match
316
362
  ### Message expectations
317
363
 
318
364
  ```ruby
319
- # Target
365
+ # Targets
320
366
  obj.should_receive(:foo)
321
367
  SomeClass.any_instance.should_receive(:foo)
322
368
 
@@ -331,7 +377,7 @@ expect_any_instance_of(SomeClass).to receive(:foo)
331
377
  ### Message expectations that are actually method stubs
332
378
 
333
379
  ```ruby
334
- # Target
380
+ # Targets
335
381
  obj.should_receive(:foo).any_number_of_times
336
382
  obj.should_receive(:foo).at_least(0)
337
383
 
@@ -352,7 +398,7 @@ SomeClass.any_instance.stub(:foo) # with `--disable allow_to_receive`
352
398
  ### Method stubs
353
399
 
354
400
  ```ruby
355
- # Target
401
+ # Targets
356
402
  obj.stub(:foo)
357
403
 
358
404
  obj.stub!(:foo)
@@ -378,7 +424,7 @@ allow_any_instance_of(SomeClass).to receive(:foo)
378
424
  ### Deprecated method stub aliases
379
425
 
380
426
  ```ruby
381
- # Target
427
+ # Targets
382
428
  obj.stub!(:foo)
383
429
  obj.unstub!(:foo)
384
430
 
@@ -393,7 +439,7 @@ obj.unstub(:foo)
393
439
  ### Method stubs with deprecated specification of number of times
394
440
 
395
441
  ```ruby
396
- # Target
442
+ # Targets
397
443
  obj.stub(:foo).any_number_of_times
398
444
  obj.stub(:foo).at_least(0)
399
445
 
@@ -408,7 +454,7 @@ obj.stub(:foo) # with `--disable allow_to_receive`
408
454
  ### Deprecated test double aliases
409
455
 
410
456
  ```ruby
411
- # Target
457
+ # Targets
412
458
  stub('something')
413
459
  mock('something')
414
460
 
data/lib/transpec/cli.rb CHANGED
@@ -39,17 +39,9 @@ module Transpec
39
39
 
40
40
  fail_if_should_not_continue!
41
41
 
42
- paths = non_option_args
42
+ base_paths = base_target_paths(non_option_args)
43
43
 
44
- if paths.empty?
45
- if Dir.exists?('spec')
46
- paths = ['spec']
47
- else
48
- fail ArgumentError, 'Specify target files or directories.'
49
- end
50
- end
51
-
52
- target_files(paths).each do |file_path|
44
+ target_files(base_paths).each do |file_path|
53
45
  process_file(file_path)
54
46
  end
55
47
 
@@ -71,7 +63,7 @@ module Transpec
71
63
  @report.invalid_context_errors.concat(rewriter.invalid_context_errors)
72
64
 
73
65
  rewriter.invalid_context_errors.each do |error|
74
- warn_not_in_example_group_context_error(error)
66
+ warn_invalid_context_error(error)
75
67
  end
76
68
  rescue Parser::SyntaxError => error
77
69
  @report.syntax_errors << error
@@ -176,6 +168,12 @@ module Transpec
176
168
 
177
169
  private
178
170
 
171
+ def base_target_paths(args)
172
+ return args unless args.empty?
173
+ return ['spec'] if Dir.exists?('spec')
174
+ fail ArgumentError, 'Specify target files or directories.'
175
+ end
176
+
179
177
  def ruby_files_in_directory(directory_path)
180
178
  ruby_file_paths = []
181
179
 
@@ -229,7 +227,7 @@ module Transpec
229
227
  warn "Syntax error at #{error.diagnostic.location}. Skipping the file.".color(:red)
230
228
  end
231
229
 
232
- def warn_not_in_example_group_context_error(error)
230
+ def warn_invalid_context_error(error)
233
231
  message = error.message.color(:yellow) + $RS
234
232
  message << highlighted_source(error)
235
233
  warn message
@@ -8,11 +8,45 @@ module Transpec
8
8
 
9
9
  SCOPE_TYPES = [:module, :class, :sclass, :def, :defs, :block].freeze
10
10
 
11
- EXAMPLE_GROUP_METHOD_NAMES = [
11
+ EXAMPLE_GROUP_METHODS = [
12
12
  :describe, :context,
13
13
  :shared_examples, :shared_context, :share_examples_for, :shared_examples_for
14
14
  ].freeze
15
15
 
16
+ EXAMPLE_METHODS = [
17
+ :example, :it, :specify,
18
+ :focus, :focused, :fit,
19
+ :pending, :xexample, :xit, :xspecify
20
+ ].freeze
21
+
22
+ HOOK_METHODS = [:before, :after, :around].freeze
23
+
24
+ HELPER_METHODS = [:subject, :subject!, :let, :let!]
25
+
26
+ NON_MONKEY_PATCH_EXPECTATION_AVAILABLE_CONTEXT = [
27
+ [:example_group, :example],
28
+ [:example_group, :each_before_after],
29
+ [:example_group, :all_before_after],
30
+ [:example_group, :around],
31
+ [:example_group, :helper],
32
+ [:example_group, :def],
33
+ [:rspec_configure, :each_before_after],
34
+ [:rspec_configure, :all_before_after],
35
+ [:rspec_configure, :around],
36
+ [:rspec_configure, :def],
37
+ [:module, :def]
38
+ ].freeze
39
+
40
+ NON_MONKEY_PATCH_MOCK_AVAILABLE_CONTEXT = [
41
+ [:example_group, :example],
42
+ [:example_group, :each_before_after],
43
+ [:example_group, :helper],
44
+ [:example_group, :def],
45
+ [:rspec_configure, :each_before_after],
46
+ [:rspec_configure, :def],
47
+ [:module, :def]
48
+ ].freeze
49
+
16
50
  attr_reader :nodes
17
51
 
18
52
  # @param nodes [Array] An array containing from root node to the target node.
@@ -23,79 +57,94 @@ module Transpec
23
57
  def scopes
24
58
  @scopes ||= begin
25
59
  scopes = @nodes.map { |node| scope_type(node) }
26
- scopes.compact
60
+ scopes.compact!
61
+ scopes.extend(ArrayExtension)
62
+ end
63
+ end
64
+
65
+ def non_monkey_patch_expectation_available?
66
+ return @expectation_available if instance_variable_defined?(:@expectation_available)
67
+
68
+ return @expectation_available = true if scopes == [:def]
69
+
70
+ @expectation_available = NON_MONKEY_PATCH_EXPECTATION_AVAILABLE_CONTEXT.any? do |suffix|
71
+ scopes.end_with?(suffix)
27
72
  end
28
73
  end
29
74
 
30
- def in_example_group?
31
- return @in_example_group if instance_variable_defined?(:@in_example_group)
32
- @in_example_group = in_method_or_block_in_example_group? ||
33
- in_method_or_block_in_rspec_configure? ||
34
- in_method_in_module? ||
35
- in_method_in_top_level?
75
+ alias_method :expect_to_matcher_available?, :non_monkey_patch_expectation_available?
76
+
77
+ def non_monkey_patch_mock_available?
78
+ return @mock_available if instance_variable_defined?(:@mock_available)
79
+
80
+ return @mock_available = true if scopes == [:def]
81
+
82
+ @mock_available = NON_MONKEY_PATCH_MOCK_AVAILABLE_CONTEXT.any? do |suffix|
83
+ scopes.end_with?(suffix)
84
+ end
36
85
  end
37
86
 
87
+ alias_method :expect_to_receive_available?, :non_monkey_patch_mock_available?
88
+ alias_method :allow_to_receive_available?, :non_monkey_patch_mock_available?
89
+
38
90
  private
39
91
 
40
92
  def scope_type(node)
41
93
  return nil unless SCOPE_TYPES.include?(node.type)
42
- return node.type unless node.type == :block
43
94
 
44
- send_node = node.children.first
95
+ case node.type
96
+ when :block
97
+ special_block_type(node)
98
+ when :defs
99
+ if node.children.first.type == :self
100
+ nil
101
+ else
102
+ node.type
103
+ end
104
+ else
105
+ node.type
106
+ end
107
+ end
108
+
109
+ def special_block_type(block_node)
110
+ send_node = block_node.children.first
45
111
  receiver_node, method_name, *_ = *send_node
46
112
 
47
113
  if const_name(receiver_node) == 'RSpec' && method_name == :configure
48
114
  :rspec_configure
115
+ elsif HOOK_METHODS.include?(method_name)
116
+ hook_type(send_node)
49
117
  elsif receiver_node
50
- node.type
51
- elsif EXAMPLE_GROUP_METHOD_NAMES.include?(method_name)
118
+ nil
119
+ elsif EXAMPLE_GROUP_METHODS.include?(method_name)
52
120
  :example_group
121
+ elsif EXAMPLE_METHODS.include?(method_name)
122
+ :example
123
+ elsif HELPER_METHODS.include?(method_name)
124
+ :helper
53
125
  else
54
- node.type
126
+ nil
55
127
  end
56
128
  end
57
129
 
58
- def in_method_or_block_in_example_group?
59
- scopes_in_example_group = inner_scopes_of(:example_group)
60
- return false unless scopes_in_example_group
61
- return false if include_class_scope?(scopes_in_example_group)
62
- include_method_or_block_scope?(scopes_in_example_group)
63
- end
64
-
65
- def in_method_or_block_in_rspec_configure?
66
- scopes_in_rspec_configure = inner_scopes_of(:rspec_configure)
67
- return false unless scopes_in_rspec_configure
68
- return false if include_class_scope?(scopes_in_rspec_configure)
69
- include_method_or_block_scope?(scopes_in_rspec_configure)
70
- end
71
-
72
- def in_method_in_module?
73
- scopes_in_module = inner_scopes_of(:module)
74
- return false unless scopes_in_module
75
- return false if include_class_scope?(scopes_in_module)
76
- scopes_in_module.include?(:def)
77
- end
130
+ def hook_type(send_node)
131
+ _, method_name, arg_node, *_ = *send_node
78
132
 
79
- def in_method_in_top_level?
80
- return false unless scopes.first == :def
81
- scopes_in_method = scopes[1..-1]
82
- !include_class_scope?(scopes_in_method)
83
- end
133
+ return :around if method_name == :around
84
134
 
85
- def inner_scopes_of(scope_type)
86
- index = scopes.rindex(scope_type)
87
- return nil unless index
88
- scopes[Range.new(index + 1, -1)]
89
- end
135
+ if arg_node && [:sym, :str].include?(arg_node.type)
136
+ hook_arg = arg_node.children.first.to_sym
137
+ return :all_before_after if hook_arg == :all
138
+ end
90
139
 
91
- def include_class_scope?(scopes)
92
- !(scopes & [:class, :sclass]).empty?
140
+ :each_before_after
93
141
  end
94
142
 
95
- def include_method_or_block_scope?(scopes)
96
- # TODO: Should validate whether the method taking the block is RSpec's
97
- # special method. (e.g. #subject, #let, #before, #after)
98
- !(scopes & [:def, :block]).empty?
143
+ module ArrayExtension
144
+ def end_with?(*args)
145
+ tail = args.flatten
146
+ self[-(tail.size)..-1] == tail
147
+ end
99
148
  end
100
149
  end
101
150
  end