transpec 0.2.3 → 0.2.4

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