transpec 1.3.1 → 1.4.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +1 -3
  4. data/CHANGELOG.md +9 -0
  5. data/README.md +141 -24
  6. data/README.md.erb +136 -24
  7. data/lib/transpec/ast/node.rb +7 -3
  8. data/lib/transpec/cli.rb +1 -1
  9. data/lib/transpec/configuration.rb +1 -0
  10. data/lib/transpec/converter.rb +31 -2
  11. data/lib/transpec/dynamic_analyzer.rb +1 -1
  12. data/lib/transpec/option_parser.rb +12 -9
  13. data/lib/transpec/project.rb +23 -12
  14. data/lib/transpec/rspec_version.rb +18 -4
  15. data/lib/transpec/static_context_inspector.rb +0 -15
  16. data/lib/transpec/syntax/example.rb +83 -0
  17. data/lib/transpec/syntax/expect.rb +5 -0
  18. data/lib/transpec/syntax/have.rb +111 -54
  19. data/lib/transpec/syntax/method_stub.rb +58 -37
  20. data/lib/transpec/syntax/mixin/allow_no_message.rb +2 -0
  21. data/lib/transpec/syntax/mixin/any_instance.rb +2 -0
  22. data/lib/transpec/syntax/mixin/should_base.rb +39 -0
  23. data/lib/transpec/syntax/oneliner_should.rb +218 -0
  24. data/lib/transpec/syntax/operator_matcher.rb +1 -0
  25. data/lib/transpec/syntax/should.rb +3 -30
  26. data/lib/transpec/util.rb +54 -0
  27. data/lib/transpec/version.rb +2 -2
  28. data/spec/support/shared_context.rb +21 -29
  29. data/spec/transpec/ast/node_spec.rb +1 -1
  30. data/spec/transpec/commit_message_spec.rb +29 -23
  31. data/spec/transpec/configuration_spec.rb +1 -0
  32. data/spec/transpec/converter_spec.rb +208 -5
  33. data/spec/transpec/dynamic_analyzer_spec.rb +2 -2
  34. data/spec/transpec/option_parser_spec.rb +1 -0
  35. data/spec/transpec/project_spec.rb +10 -0
  36. data/spec/transpec/rspec_version_spec.rb +52 -28
  37. data/spec/transpec/static_context_inspector_spec.rb +2 -2
  38. data/spec/transpec/syntax/be_boolean_spec.rb +6 -13
  39. data/spec/transpec/syntax/be_close_spec.rb +2 -9
  40. data/spec/transpec/syntax/double_spec.rb +2 -9
  41. data/spec/transpec/syntax/example_spec.rb +249 -0
  42. data/spec/transpec/syntax/expect_spec.rb +1 -1
  43. data/spec/transpec/syntax/have_spec.rb +127 -22
  44. data/spec/transpec/syntax/its_spec.rb +9 -18
  45. data/spec/transpec/syntax/method_stub_spec.rb +193 -158
  46. data/spec/transpec/syntax/oneliner_should_spec.rb +653 -0
  47. data/spec/transpec/syntax/operator_matcher_spec.rb +7 -8
  48. data/spec/transpec/syntax/raise_error_spec.rb +6 -13
  49. data/spec/transpec/syntax/rspec_configure_spec.rb +1 -8
  50. data/spec/transpec/syntax/should_receive_spec.rb +19 -28
  51. data/spec/transpec/syntax/should_spec.rb +18 -16
  52. data/spec/transpec/util_spec.rb +30 -0
  53. data/transpec.gemspec +8 -7
  54. metadata +49 -28
data/lib/transpec/util.rb CHANGED
@@ -2,12 +2,29 @@
2
2
 
3
3
  module Transpec
4
4
  module Util
5
+ EXAMPLE_GROUP_METHODS = %w(
6
+ describe context
7
+ shared_examples shared_context share_examples_for shared_examples_for
8
+ ).map(&:to_sym).freeze
9
+
10
+ EXAMPLE_METHODS = %w(
11
+ example it specify
12
+ focus focused fit
13
+ pending xexample xit xspecify
14
+ ).map(&:to_sym).freeze
15
+
16
+ HOOK_METHODS = %w(before after around).map(&:to_sym).freeze
17
+
18
+ HELPER_METHODS = %w(subject subject! let let!).map(&:to_sym).freeze
19
+
5
20
  LITERAL_TYPES = %w(
6
21
  true false nil
7
22
  int float
8
23
  str sym regexp
9
24
  ).map(&:to_sym).freeze
10
25
 
26
+ WHITESPACES = [' ', "\t"].freeze
27
+
11
28
  module_function
12
29
 
13
30
  def proc_literal?(node)
@@ -86,5 +103,42 @@ module Transpec
86
103
  false
87
104
  end
88
105
  end
106
+
107
+ def expand_range_to_adjacent_whitespaces(range, direction = :both)
108
+ source = range.source_buffer.source
109
+ begin_pos = if [:both, :begin].include?(direction)
110
+ find_consecutive_whitespace_position(source, range.begin_pos, :downto)
111
+ else
112
+ range.begin_pos
113
+ end
114
+
115
+ end_pos = if [:both, :end].include?(direction)
116
+ find_consecutive_whitespace_position(source, range.end_pos - 1, :upto) + 1
117
+ else
118
+ range.end_pos
119
+ end
120
+
121
+ Parser::Source::Range.new(range.source_buffer, begin_pos, end_pos)
122
+ end
123
+
124
+ def find_consecutive_whitespace_position(source, origin, method)
125
+ from, to = case method
126
+ when :upto
127
+ [origin + 1, source.length - 1]
128
+ when :downto
129
+ [origin - 1, 0]
130
+ else
131
+ fail "Invalid method #{method}"
132
+ end
133
+
134
+ from.send(method, to).reduce(origin) do |previous_position, position|
135
+ character = source[position]
136
+ if WHITESPACES.include?(character)
137
+ position
138
+ else
139
+ return previous_position
140
+ end
141
+ end
142
+ end
89
143
  end
90
144
  end
@@ -4,8 +4,8 @@ module Transpec
4
4
  # http://semver.org/
5
5
  module Version
6
6
  MAJOR = 1
7
- MINOR = 3
8
- PATCH = 1
7
+ MINOR = 4
8
+ PATCH = 0
9
9
 
10
10
  def self.to_s
11
11
  [MAJOR, MINOR, PATCH].join('.')
@@ -1,30 +1,31 @@
1
1
  # coding: utf-8
2
2
 
3
- require 'transpec/dynamic_analyzer'
4
- require 'transpec/ast/builder'
5
- require 'transpec/syntax/should'
6
- require 'transpec/syntax/expect'
7
- require 'parser'
8
- require 'parser/current'
9
- require 'tmpdir'
10
-
11
3
  # This context requires `source` to be defined with #let.
12
4
  shared_context 'parsed objects' do
13
5
  let(:source_buffer) do
6
+ require 'parser'
14
7
  buffer = Parser::Source::Buffer.new('(string)')
15
8
  buffer.source = source
16
9
  buffer
17
10
  end
18
11
 
19
12
  let(:ast) do
13
+ require 'transpec/ast/builder'
14
+ require 'parser/current'
20
15
  builder = Transpec::AST::Builder.new
21
16
  parser = Parser::CurrentRuby.new(builder)
22
17
  parser.parse(source_buffer)
23
18
  end
24
19
 
25
- let(:source_rewriter) { Parser::Source::Rewriter.new(source_buffer) }
20
+ let(:source_rewriter) do
21
+ require 'parser'
22
+ Parser::Source::Rewriter.new(source_buffer)
23
+ end
26
24
 
27
25
  let(:rewritten_source) { source_rewriter.process }
26
+
27
+ # Include 'dynamic analysis objects' after this context so that this nil will be overridden.
28
+ let(:runtime_data) { nil }
28
29
  end
29
30
 
30
31
  # This context requires `source` to be defined with #let.
@@ -34,6 +35,7 @@ shared_context 'dynamic analysis objects' do
34
35
  let(:source_path) { 'spec/example_spec.rb' }
35
36
 
36
37
  let(:source_buffer) do
38
+ require 'parser'
37
39
  buffer = Parser::Source::Buffer.new(source_path)
38
40
  buffer.source = source
39
41
  buffer
@@ -42,6 +44,8 @@ shared_context 'dynamic analysis objects' do
42
44
  runtime_data_cache = {}
43
45
 
44
46
  let(:runtime_data) do
47
+ require 'transpec/dynamic_analyzer'
48
+
45
49
  if runtime_data_cache[source]
46
50
  runtime_data_cache[source]
47
51
  else
@@ -52,30 +56,16 @@ shared_context 'dynamic analysis objects' do
52
56
  end
53
57
  end
54
58
 
55
- shared_context 'should object' do
56
- let(:should_object) do
57
- ast.each_node do |node|
58
- next unless Transpec::Syntax::Should.target_node?(node)
59
- return Transpec::Syntax::Should.new(node, source_rewriter, runtime_data)
60
- end
61
-
62
- fail 'No should node is found!'
63
- end
64
-
65
- let(:runtime_data) { nil }
66
- end
67
-
68
- shared_context 'expect object' do
69
- let(:expect_object) do
59
+ # This context depends on the context 'parsed objects'.
60
+ shared_context 'syntax object' do |syntax_class, name|
61
+ let(name) do
70
62
  ast.each_node do |node|
71
- next unless Transpec::Syntax::Expect.target_node?(node)
72
- return Transpec::Syntax::Expect.new(node, source_rewriter, runtime_data)
63
+ next unless syntax_class.target_node?(node)
64
+ return syntax_class.new(node, source_rewriter, runtime_data)
73
65
  end
74
66
 
75
- fail 'No expect node is found!'
67
+ fail "No #{syntax_class.name} node is found!"
76
68
  end
77
-
78
- let(:runtime_data) { nil }
79
69
  end
80
70
 
81
71
  shared_context 'isolated environment' do
@@ -89,6 +79,8 @@ shared_context 'isolated environment' do
89
79
  end
90
80
 
91
81
  shared_context 'inside of git repository' do
82
+ require 'tmpdir'
83
+
92
84
  around do |example|
93
85
  Dir.mkdir('repo')
94
86
  Dir.chdir('repo') do
@@ -43,7 +43,7 @@ module Transpec
43
43
  end
44
44
  end
45
45
 
46
- context 'when the node has parent' do
46
+ context 'when the node does not have parent' do
47
47
  it 'returns nil' do
48
48
  ast.parent_node.should be_nil
49
49
  end
@@ -26,37 +26,43 @@ module Transpec
26
26
 
27
27
  let(:lines) { commit_message.to_s.lines.to_a }
28
28
 
29
- it 'has concise summary at first line' do
30
- lines[0].chomp.should == 'Convert specs to the latest RSpec syntax with Transpec'
29
+ describe 'first line' do
30
+ it 'has concise summary' do
31
+ lines[0].chomp.should == 'Convert specs to the latest RSpec syntax with Transpec'
32
+ end
31
33
  end
32
34
 
33
- it 'has blank line at second line' do
34
- lines[1].chomp.should be_empty
35
+ describe 'second line' do
36
+ it 'has blank line' do
37
+ lines[1].chomp.should be_empty
38
+ end
35
39
  end
36
40
 
37
41
  let(:body_lines) { lines[2..-1] }
38
42
 
39
- it 'has Transpec description at the beginning of the body' do
40
- body_lines[0].chomp
41
- .should match(/^This conversion is done by Transpec \d+\.\d+\.\d+ with the following command:$/)
42
- body_lines[1].chomp
43
- .should == ' transpec --force --generate-commit-message'
44
- end
43
+ describe 'body' do
44
+ it 'has Transpec description at the beginning' do
45
+ body_lines[0].chomp
46
+ .should match(/^This conversion is done by Transpec \d+\.\d+\.\d+ with the following command:$/)
47
+ body_lines[1].chomp
48
+ .should == ' transpec --force --generate-commit-message'
49
+ end
45
50
 
46
- it 'has blank line after the preface in the body' do
47
- body_lines[2].chomp.should be_empty
48
- end
51
+ it 'has blank line after the preface' do
52
+ body_lines[2].chomp.should be_empty
53
+ end
49
54
 
50
- it 'has conversion summary in the body' do
51
- body_lines[3..-1].join('').should == <<-END.gsub(/^\s+\|/, '')
52
- |* 2 conversions
53
- | from: obj.should
54
- | to: expect(obj).to
55
- |
56
- |* 1 conversion
57
- | from: obj.stub(:message)
58
- | to: allow(obj).to receive(:message)
59
- END
55
+ it 'has conversion summary' do
56
+ body_lines[3..-1].join('').should == <<-END.gsub(/^\s+\|/, '')
57
+ |* 2 conversions
58
+ | from: obj.should
59
+ | to: expect(obj).to
60
+ |
61
+ |* 1 conversion
62
+ | from: obj.stub(:message)
63
+ | to: allow(obj).to receive(:message)
64
+ END
65
+ end
60
66
  end
61
67
  end
62
68
  end
@@ -10,6 +10,7 @@ module Transpec
10
10
  context 'by default' do
11
11
  [
12
12
  [:convert_should?, true],
13
+ [:convert_oneliner?, true],
13
14
  [:convert_should_receive?, true],
14
15
  [:convert_stub?, true],
15
16
  [:convert_have_items?, true],
@@ -210,15 +210,179 @@ module Transpec
210
210
  end
211
211
  end
212
212
 
213
+ describe '#process_oneliner_should' do
214
+ let(:should_object) { double('oneliner_should_object').as_null_object }
215
+
216
+ shared_examples 'does nothing' do
217
+ it 'does nothing' do
218
+ should_object.should_not_receive(:expectize!)
219
+ should_object.should_not_receive(:convert_have_items_to_standard_should!)
220
+ should_object.should_not_receive(:convert_have_items_to_standard_expect!)
221
+ converter.process_oneliner_should(should_object)
222
+ end
223
+ end
224
+
225
+ shared_examples 'invokes OnelinerShould#expectize! if available' do
226
+ context 'when RSpecVersion#one_liner_is_expected_available? returns true' do
227
+ before { rspec_version.stub(:one_liner_is_expected_available?).and_return(true) }
228
+
229
+ it 'invokes OnelinerShould#expectize!' do
230
+ should_object.should_receive(:expectize!)
231
+ converter.process_oneliner_should(should_object)
232
+ end
233
+ end
234
+
235
+ context 'when RSpecVersion#one_liner_is_expected_available? returns false' do
236
+ before { rspec_version.stub(:one_liner_is_expected_available?).and_return(false) }
237
+ include_examples 'does nothing'
238
+ end
239
+ end
240
+
241
+ shared_examples 'converts to standard expecatations' do
242
+ context 'and Configuration#convert_should? is true' do
243
+ before { configuration.convert_should = true }
244
+
245
+ it 'invokes OnelinerShould#convert_have_items_to_standard_expect!' do
246
+ should_object.should_receive(:convert_have_items_to_standard_expect!)
247
+ converter.process_oneliner_should(should_object)
248
+ end
249
+ end
250
+
251
+ context 'and Configuration#convert_should? is false' do
252
+ before { configuration.convert_should = false }
253
+
254
+ it 'invokes OnelinerShould#convert_have_items_to_standard_should!' do
255
+ should_object.should_receive(:convert_have_items_to_standard_should!)
256
+ converter.process_oneliner_should(should_object)
257
+ end
258
+ end
259
+ end
260
+
261
+ context 'when Configuration#convert_oneliner? is true' do
262
+ before { configuration.convert_oneliner = true }
263
+
264
+ context 'and the OnelinerShould has #have matcher' do
265
+ before do
266
+ should_object.stub(:have_matcher).and_return(double('have_matcher').as_null_object)
267
+ end
268
+
269
+ context 'and Configuration#convert_have_items? is true' do
270
+ before { configuration.convert_have_items = true }
271
+
272
+ context 'and Have#project_requires_collection_matcher? is true' do
273
+ before do
274
+ should_object.have_matcher
275
+ .stub(:project_requires_collection_matcher?).and_return(true)
276
+ end
277
+ include_examples 'invokes OnelinerShould#expectize! if available'
278
+ end
279
+
280
+ context 'and Have#project_requires_collection_matcher? is false' do
281
+ before do
282
+ should_object.have_matcher
283
+ .stub(:project_requires_collection_matcher?).and_return(false)
284
+ end
285
+ include_examples 'converts to standard expecatations'
286
+ end
287
+ end
288
+
289
+ context 'and Configuration#convert_have_items? is false' do
290
+ before { configuration.convert_have_items = false }
291
+ include_examples 'invokes OnelinerShould#expectize! if available'
292
+ end
293
+ end
294
+
295
+ context 'and the OnelinerShould does not have #have matcher' do
296
+ before do
297
+ should_object.stub(:have_matcher).and_return(nil)
298
+ end
299
+
300
+ context 'and Configuration#convert_have_items? is true' do
301
+ before { configuration.convert_have_items = true }
302
+ include_examples 'invokes OnelinerShould#expectize! if available'
303
+ end
304
+
305
+ context 'and Configuration#convert_have_items? is false' do
306
+ before { configuration.convert_have_items = false }
307
+ include_examples 'invokes OnelinerShould#expectize! if available'
308
+ end
309
+ end
310
+ end
311
+
312
+ context 'when Configuration#convert_oneliner? is false' do
313
+ before { configuration.convert_oneliner = false }
314
+
315
+ context 'and the OnelinerShould has #have matcher' do
316
+ before do
317
+ should_object.stub(:have_matcher).and_return(double('have_matcher').as_null_object)
318
+ end
319
+
320
+ context 'and Configuration#convert_have_items? is true' do
321
+ before { configuration.convert_have_items = true }
322
+
323
+ context 'and Have#project_requires_collection_matcher? is true' do
324
+ before do
325
+ should_object.have_matcher
326
+ .stub(:project_requires_collection_matcher?).and_return(true)
327
+ end
328
+ include_examples 'does nothing'
329
+ end
330
+
331
+ context 'and Have#project_requires_collection_matcher? is false' do
332
+ before do
333
+ should_object.have_matcher
334
+ .stub(:project_requires_collection_matcher?).and_return(false)
335
+ end
336
+ include_examples 'converts to standard expecatations'
337
+ end
338
+ end
339
+
340
+ context 'and Configuration#convert_have_items? is false' do
341
+ before { configuration.convert_have_items = false }
342
+ include_examples 'does nothing'
343
+ end
344
+ end
345
+
346
+ context 'and the OnelinerShould does not have #have matcher' do
347
+ before do
348
+ should_object.stub(:have_matcher).and_return(nil)
349
+ end
350
+
351
+ context 'and Configuration#convert_have_items? is true' do
352
+ before { configuration.convert_have_items = true }
353
+ include_examples 'does nothing'
354
+ end
355
+
356
+ context 'and Configuration#convert_have_items? is false' do
357
+ before { configuration.convert_have_items = false }
358
+ include_examples 'does nothing'
359
+ end
360
+ end
361
+ end
362
+ end
363
+
213
364
  describe '#process_expect' do
214
365
  let(:expect_object) { double('expect_object').as_null_object }
215
366
 
216
367
  context 'when Configuration#convert_have_items? is true' do
217
368
  before { configuration.convert_have_items = true }
218
369
 
219
- it 'invokes Have#convert_to_standard_expectation!' do
220
- expect_object.have_matcher.should_receive(:convert_to_standard_expectation!)
221
- converter.process_expect(expect_object)
370
+ context 'and Configuration#parenthesize_matcher_arg is true' do
371
+ before { configuration.parenthesize_matcher_arg = true }
372
+
373
+ it 'invokes Have#convert_to_standard_expectation! with true' do
374
+ expect_object.have_matcher.should_receive(:convert_to_standard_expectation!).with(true)
375
+ converter.process_expect(expect_object)
376
+ end
377
+ end
378
+
379
+ context 'and Configuration#parenthesize_matcher_arg is false' do
380
+ before { configuration.parenthesize_matcher_arg = false }
381
+
382
+ it 'invokes Have#convert_to_standard_expectation! with false' do
383
+ expect_object.have_matcher.should_receive(:convert_to_standard_expectation!).with(false)
384
+ converter.process_expect(expect_object)
385
+ end
222
386
  end
223
387
  end
224
388
 
@@ -394,8 +558,8 @@ module Transpec
394
558
  let(:method_stub_object) { double('method_stub_object').as_null_object }
395
559
 
396
560
  shared_examples 'invokes MethodStub#allowize!' do
397
- it 'invokes MethodStub#allowize! with #receive_messages availability' do
398
- method_stub_object.should_receive(:allowize!).with(rspec_version.receive_messages_available?)
561
+ it 'invokes MethodStub#allowize! with RSpecVersion' do
562
+ method_stub_object.should_receive(:allowize!).with(rspec_version)
399
563
  converter.process_method_stub(method_stub_object)
400
564
  end
401
565
  end
@@ -627,6 +791,45 @@ module Transpec
627
791
  end
628
792
  end
629
793
 
794
+ describe '#process_example' do
795
+ let(:example_object) { double('example_object').as_null_object }
796
+
797
+ context 'when RSpecVersion#yielded_example_available? returns true' do
798
+ before { rspec_version.stub(:yielded_example_available?).and_return(true) }
799
+
800
+ context 'when Configuration#convert_deprecated_method? is true' do
801
+ before { configuration.convert_deprecated_method = true }
802
+
803
+ it 'invokes Example#convert!' do
804
+ example_object.should_receive(:convert!)
805
+ converter.process_example(example_object)
806
+ end
807
+ end
808
+
809
+ context 'when Configuration#convert_deprecated_method? is false' do
810
+ before { configuration.convert_deprecated_method = false }
811
+
812
+ it 'does not invoke Its#convert_to_describe_subject_it!' do
813
+ example_object.should_not_receive(:convert!)
814
+ converter.process_example(example_object)
815
+ end
816
+ end
817
+ end
818
+
819
+ context 'when RSpecVersion#yielded_example_available? returns false' do
820
+ before { rspec_version.stub(:yielded_example_available?).and_return(false) }
821
+
822
+ context 'when Configuration#convert_deprecated_method? is true' do
823
+ before { configuration.convert_deprecated_method = true }
824
+
825
+ it 'does nothing' do
826
+ example_object.should_not_receive(:convert!)
827
+ converter.process_example(example_object)
828
+ end
829
+ end
830
+ end
831
+ end
832
+
630
833
  describe '#process_rspec_configure' do
631
834
  let(:rspec_configure) { double('rspec_configure').as_null_object }
632
835