transpec 0.1.3 → 0.2.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +35 -3
  4. data/README.md.erb +35 -3
  5. data/lib/transpec/cli.rb +50 -4
  6. data/lib/transpec/commit_message.rb +25 -0
  7. data/lib/transpec/git.rb +18 -0
  8. data/lib/transpec/record.rb +28 -0
  9. data/lib/transpec/report.rb +109 -0
  10. data/lib/transpec/rewriter.rb +17 -8
  11. data/lib/transpec/syntax.rb +25 -22
  12. data/lib/transpec/syntax/be_close.rb +6 -0
  13. data/lib/transpec/syntax/double.rb +5 -0
  14. data/lib/transpec/syntax/matcher.rb +17 -1
  15. data/lib/transpec/syntax/method_stub.rb +40 -8
  16. data/lib/transpec/syntax/raise_error.rb +17 -0
  17. data/lib/transpec/syntax/send_node_syntax.rb +4 -0
  18. data/lib/transpec/syntax/should.rb +23 -2
  19. data/lib/transpec/syntax/should_receive.rb +54 -2
  20. data/lib/transpec/version.rb +2 -2
  21. data/spec/.rubocop.yml +1 -1
  22. data/spec/support/shared_context.rb +10 -0
  23. data/spec/transpec/cli_spec.rb +76 -29
  24. data/spec/transpec/commit_message_spec.rb +63 -0
  25. data/spec/transpec/configuration_spec.rb +1 -1
  26. data/spec/transpec/git_spec.rb +114 -38
  27. data/spec/transpec/record_spec.rb +18 -0
  28. data/spec/transpec/report_spec.rb +89 -0
  29. data/spec/transpec/rewriter_spec.rb +5 -0
  30. data/spec/transpec/syntax/be_close_spec.rb +10 -1
  31. data/spec/transpec/syntax/double_spec.rb +10 -0
  32. data/spec/transpec/syntax/matcher_spec.rb +31 -0
  33. data/spec/transpec/syntax/method_stub_spec.rb +53 -44
  34. data/spec/transpec/syntax/raise_error_spec.rb +64 -24
  35. data/spec/transpec/syntax/should_receive_spec.rb +67 -7
  36. data/spec/transpec/syntax/should_spec.rb +26 -0
  37. data/tasks/test.rake +27 -12
  38. metadata +11 -2
@@ -1,5 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'transpec/report'
3
4
  require 'transpec/ast/builder'
4
5
  require 'transpec/ast/scanner'
5
6
  require 'transpec/configuration'
@@ -16,11 +17,12 @@ require 'parser/current'
16
17
 
17
18
  module Transpec
18
19
  class Rewriter
19
- attr_reader :errors
20
+ attr_reader :report, :invalid_context_errors
20
21
 
21
- def initialize(configuration = Configuration.new)
22
+ def initialize(configuration = Configuration.new, report = Report.new)
22
23
  @configuration = configuration
23
- @errors = []
24
+ @report = report
25
+ @invalid_context_errors = []
24
26
  end
25
27
 
26
28
  def rewrite_file!(file_path)
@@ -35,7 +37,10 @@ module Transpec
35
37
 
36
38
  @source_rewriter = Parser::Source::Rewriter.new(source_buffer)
37
39
  failed_overlapping_rewrite = false
38
- @source_rewriter.diagnostics.consumer = proc { failed_overlapping_rewrite = true }
40
+ @source_rewriter.diagnostics.consumer = proc do
41
+ failed_overlapping_rewrite = true
42
+ fail OverlappedRewriteError
43
+ end
39
44
 
40
45
  AST::Scanner.scan(ast) do |node, ancestor_nodes|
41
46
  dispatch_node(node, ancestor_nodes)
@@ -44,7 +49,7 @@ module Transpec
44
49
  rewritten_source = @source_rewriter.process
45
50
 
46
51
  if failed_overlapping_rewrite
47
- rewriter = self.class.new(@configuration)
52
+ rewriter = self.class.new(@configuration, @report)
48
53
  rewritten_source = rewriter.rewrite(rewritten_source, name)
49
54
  end
50
55
 
@@ -71,7 +76,8 @@ module Transpec
71
76
  syntax = syntax_class.new(
72
77
  node,
73
78
  ancestor_nodes,
74
- @source_rewriter
79
+ @source_rewriter,
80
+ @report
75
81
  )
76
82
 
77
83
  handler_name = "process_#{syntax_class.snake_case_name}"
@@ -79,8 +85,9 @@ module Transpec
79
85
 
80
86
  break
81
87
  end
82
- rescue Syntax::NotInExampleGroupContextError => error
83
- @errors << error
88
+ rescue OverlappedRewriteError # rubocop:disable HandleExceptions
89
+ rescue Syntax::InvalidContextError => error
90
+ @invalid_context_errors << error
84
91
  end
85
92
 
86
93
  def process_should(should)
@@ -152,5 +159,7 @@ module Transpec
152
159
  !@configuration.convert_to_allow_to_receive?
153
160
  rspec_configure.mock_syntaxes == [:should]
154
161
  end
162
+
163
+ class OverlappedRewriteError < StandardError; end
155
164
  end
156
165
  end
@@ -1,30 +1,12 @@
1
1
  # coding: utf-8
2
2
 
3
3
  require 'transpec/context'
4
+ require 'transpec/report'
5
+ require 'transpec/record'
4
6
 
5
7
  module Transpec
6
8
  class Syntax
7
- class NotInExampleGroupContextError < StandardError
8
- attr_reader :message, :source_range
9
-
10
- def initialize(source_range, original_syntax, target_syntax)
11
- @source_range = source_range
12
- @message = build_message(original_syntax, target_syntax)
13
- end
14
-
15
- def source_buffer
16
- @source_range.source_buffer
17
- end
18
-
19
- private
20
-
21
- def build_message(original_syntax, target_syntax)
22
- "Cannot convert #{original_syntax} into #{target_syntax} " +
23
- "since #{target_syntax} is not available in the context."
24
- end
25
- end
26
-
27
- attr_reader :node, :ancestor_nodes, :source_rewriter
9
+ attr_reader :node, :ancestor_nodes, :source_rewriter, :report
28
10
 
29
11
  def self.all
30
12
  @subclasses ||= []
@@ -48,10 +30,11 @@ module Transpec
48
30
  target_method_names.include?(method_name)
49
31
  end
50
32
 
51
- def initialize(node, ancestor_nodes, source_rewriter)
33
+ def initialize(node, ancestor_nodes, source_rewriter, report = Report.new)
52
34
  @node = node
53
35
  @ancestor_nodes = ancestor_nodes
54
36
  @source_rewriter = source_rewriter
37
+ @report = report
55
38
  end
56
39
 
57
40
  def context
@@ -91,5 +74,25 @@ module Transpec
91
74
  def replace(range, content)
92
75
  @source_rewriter.replace(range, content)
93
76
  end
77
+
78
+ class InvalidContextError < StandardError
79
+ attr_reader :message, :source_range
80
+
81
+ def initialize(source_range, original_syntax, target_syntax)
82
+ @source_range = source_range
83
+ @message = build_message(original_syntax, target_syntax)
84
+ end
85
+
86
+ def source_buffer
87
+ @source_range.source_buffer
88
+ end
89
+
90
+ private
91
+
92
+ def build_message(original_syntax, target_syntax)
93
+ "Cannot convert #{original_syntax} into #{target_syntax} " +
94
+ "since #{target_syntax} is not available in the context."
95
+ end
96
+ end
94
97
  end
95
98
  end
@@ -16,6 +16,8 @@ module Transpec
16
16
  be_within_source << ')'
17
17
 
18
18
  replace(expression_range, be_within_source)
19
+
20
+ register_record
19
21
  end
20
22
 
21
23
  private
@@ -27,6 +29,10 @@ module Transpec
27
29
  def self.target_method_names
28
30
  [:be_close]
29
31
  end
32
+
33
+ def register_record
34
+ @report.records << Record.new('be_close(expected, delta)', 'be_within(delta).of(expected)')
35
+ end
30
36
  end
31
37
  end
32
38
  end
@@ -11,6 +11,7 @@ module Transpec
11
11
  def convert_to_double!
12
12
  return if method_name == :double
13
13
  replace(selector_range, 'double')
14
+ register_record
14
15
  end
15
16
 
16
17
  private
@@ -22,6 +23,10 @@ module Transpec
22
23
  def self.target_method_names
23
24
  [:double, :mock, :stub]
24
25
  end
26
+
27
+ def register_record
28
+ @report.records << Record.new("#{method_name}('something')", "double('something')")
29
+ end
25
30
  end
26
31
  end
27
32
  end
@@ -13,9 +13,10 @@ module Transpec
13
13
  false
14
14
  end
15
15
 
16
- def initialize(node, source_rewriter)
16
+ def initialize(node, source_rewriter, report = Report.new)
17
17
  @node = node
18
18
  @source_rewriter = source_rewriter
19
+ @report = report
19
20
  end
20
21
 
21
22
  def correct_operator!(parenthesize_arg = true)
@@ -47,11 +48,13 @@ module Transpec
47
48
  handle_anterior_of_operator!
48
49
  replace(selector_range, 'eq')
49
50
  parenthesize!(parenthesize_arg)
51
+ register_record(nil, 'eq(expected)')
50
52
  end
51
53
 
52
54
  def convert_to_be_operator!
53
55
  return if prefixed_with_be?
54
56
  insert_before(selector_range, 'be ')
57
+ register_record(nil, "be #{method_name} expected")
55
58
  end
56
59
 
57
60
  def convert_to_match!(parenthesize_arg)
@@ -64,6 +67,14 @@ module Transpec
64
67
  end
65
68
 
66
69
  parenthesize!(parenthesize_arg)
70
+
71
+ # Need to register record after all source rewrites are done
72
+ # to avoid false record when failed with overlapped rewrite.
73
+ if arg_node.type == :array
74
+ register_record('=~ [1, 2]', 'match_array([1, 2])')
75
+ else
76
+ register_record('=~ /pattern/', 'match(/pattern/)')
77
+ end
67
78
  end
68
79
 
69
80
  def handle_anterior_of_operator!
@@ -113,6 +124,11 @@ module Transpec
113
124
  here_document?(arg_node) ||
114
125
  arg_node.each_descendent_node.any? { |n| here_document?(n) }
115
126
  end
127
+
128
+ def register_record(original_syntax, converted_syntax)
129
+ original_syntax ||= "#{method_name} expected"
130
+ @report.records << Record.new(original_syntax, converted_syntax)
131
+ end
116
132
  end
117
133
  end
118
134
  end
@@ -23,7 +23,7 @@ module Transpec
23
23
  fail 'Already replaced deprecated method, cannot allowize.' if @replaced_deprecated_method
24
24
 
25
25
  unless context.in_example_group?
26
- fail NotInExampleGroupContextError.new(selector_range, "##{method_name}", '#allow')
26
+ fail InvalidContextError.new(selector_range, "##{method_name}", '#allow')
27
27
  end
28
28
 
29
29
  if arg_node.type == :hash
@@ -34,20 +34,19 @@ module Transpec
34
34
  replace(expression_range, expression)
35
35
  end
36
36
 
37
+ register_record(:allow)
38
+
37
39
  @allowized = true
38
40
  end
39
41
 
40
42
  def replace_deprecated_method!
41
- replacement_method_name = case method_name
42
- when :stub! then 'stub'
43
- when :unstub! then 'unstub'
44
- end
45
-
46
- return unless replacement_method_name
43
+ return unless replacement_method_for_deprecated_method
47
44
 
48
45
  fail 'Already allowized, cannot replace deprecated method.' if @allowized
49
46
 
50
- replace(selector_range, replacement_method_name)
47
+ replace(selector_range, replacement_method_for_deprecated_method)
48
+
49
+ register_record(:deprecated)
51
50
 
52
51
  @replaced_deprecated_method = true
53
52
  end
@@ -107,6 +106,39 @@ module Transpec
107
106
  message_source.prepend(':') if node.type == :sym && !message_source.start_with?(':')
108
107
  message_source
109
108
  end
109
+
110
+ def replacement_method_for_deprecated_method
111
+ case method_name
112
+ when :stub! then 'stub'
113
+ when :unstub! then 'unstub'
114
+ else nil
115
+ end
116
+ end
117
+
118
+ def register_record(conversion_type)
119
+ @report.records << Record.new(original_syntax, converted_syntax(conversion_type))
120
+ end
121
+
122
+ def original_syntax
123
+ syntax = any_instance? ? 'SomeClass.any_instance' : 'obj'
124
+ syntax << ".#{method_name}"
125
+ syntax << (arg_node.type == :hash ? '(:message => value)' : '(:message)')
126
+ end
127
+
128
+ def converted_syntax(conversion_type)
129
+ case conversion_type
130
+ when :allow
131
+ syntax = any_instance? ? 'allow_any_instance_of(SomeClass)' : 'allow(obj)'
132
+ syntax << '.to receive(:message)'
133
+ syntax << '.and_return(value)' if arg_node.type == :hash
134
+ when :deprecated
135
+ syntax = 'obj.'
136
+ syntax << replacement_method_for_deprecated_method
137
+ syntax << '(:message)'
138
+ end
139
+
140
+ syntax
141
+ end
110
142
  end
111
143
  end
112
144
  end
@@ -15,6 +15,8 @@ module Transpec
15
15
  return if arg_nodes.empty?
16
16
 
17
17
  remove(parentheses_range)
18
+
19
+ register_record
18
20
  end
19
21
 
20
22
  def positive?
@@ -31,6 +33,21 @@ module Transpec
31
33
  def self.target_method_names
32
34
  [:raise_error]
33
35
  end
36
+
37
+ def register_record
38
+ original_syntax = 'expect { }.not_to raise_error('
39
+
40
+ if arg_nodes.first.type == :const
41
+ original_syntax << 'SpecificErrorClass'
42
+ original_syntax << ', message' if arg_nodes.count >= 2
43
+ else
44
+ original_syntax << 'message'
45
+ end
46
+
47
+ original_syntax << ')'
48
+
49
+ @report.records << Record.new(original_syntax, 'expect { }.not_to raise_error')
50
+ end
34
51
  end
35
52
  end
36
53
  end
@@ -19,6 +19,10 @@ module Transpec
19
19
  @node.children[2]
20
20
  end
21
21
 
22
+ def arg_nodes
23
+ @node.children[2..-1]
24
+ end
25
+
22
26
  def selector_range
23
27
  @node.loc.selector
24
28
  end
@@ -16,7 +16,7 @@ module Transpec
16
16
 
17
17
  def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
18
18
  unless context.in_example_group?
19
- fail NotInExampleGroupContextError.new(selector_range, "##{method_name}", '#expect')
19
+ fail InvalidContextError.new(selector_range, "##{method_name}", '#expect')
20
20
  end
21
21
 
22
22
  if proc_literal?(subject_node)
@@ -27,11 +27,13 @@ module Transpec
27
27
 
28
28
  replace(should_range, positive? ? 'to' : negative_form)
29
29
 
30
+ register_record(negative_form)
31
+
30
32
  matcher.correct_operator!(parenthesize_matcher_arg)
31
33
  end
32
34
 
33
35
  def matcher
34
- @matcher ||= Matcher.new(matcher_node, @source_rewriter)
36
+ @matcher ||= Matcher.new(matcher_node, @source_rewriter, @report)
35
37
  end
36
38
 
37
39
  def matcher_node
@@ -61,6 +63,25 @@ module Transpec
61
63
  selector_range.join(expression_range.end)
62
64
  end
63
65
  end
66
+
67
+ def register_record(negative_form_of_to)
68
+ if proc_literal?(subject_node)
69
+ original_syntax = 'lambda { }.should'
70
+ converted_syntax = 'expect { }.'
71
+ else
72
+ original_syntax = 'obj.should'
73
+ converted_syntax = 'expect(obj).'
74
+ end
75
+
76
+ if positive?
77
+ converted_syntax << 'to'
78
+ else
79
+ original_syntax << '_not'
80
+ converted_syntax << negative_form_of_to
81
+ end
82
+
83
+ @report.records << Record.new(original_syntax, converted_syntax)
84
+ end
64
85
  end
65
86
  end
66
87
  end
@@ -18,6 +18,7 @@ module Transpec
18
18
 
19
19
  def expectize!(negative_form = 'not_to')
20
20
  convert_to_syntax!('expect', negative_form)
21
+ register_record(:expect, negative_form)
21
22
  end
22
23
 
23
24
  def allowize_useless_expectation!(negative_form = 'not_to')
@@ -25,20 +26,24 @@ module Transpec
25
26
 
26
27
  convert_to_syntax!('allow', negative_form)
27
28
  remove_allowance_for_no_message!
29
+
30
+ register_record(:allow, negative_form)
28
31
  end
29
32
 
30
- def stubize_useless_expectation!(negative_form = 'not_to')
33
+ def stubize_useless_expectation!
31
34
  return unless useless_expectation?
32
35
 
33
36
  replace(selector_range, 'stub')
34
37
  remove_allowance_for_no_message!
38
+
39
+ register_record(:stub)
35
40
  end
36
41
 
37
42
  private
38
43
 
39
44
  def convert_to_syntax!(syntax, negative_form)
40
45
  unless context.in_example_group?
41
- fail NotInExampleGroupContextError.new(selector_range, "##{method_name}", "##{syntax}")
46
+ fail InvalidContextError.new(selector_range, "##{method_name}", "##{syntax}")
42
47
  end
43
48
 
44
49
  if any_instance?
@@ -121,6 +126,53 @@ module Transpec
121
126
  return child_node if child_node.type == :block
122
127
  end
123
128
  end
129
+
130
+ def register_record(conversion_type, negative_form_of_to = nil)
131
+ @report.records << Record.new(
132
+ original_syntax(conversion_type),
133
+ converted_syntax(conversion_type, negative_form_of_to)
134
+ )
135
+ end
136
+
137
+ def original_syntax(conversion_type)
138
+ syntax = if any_instance? && conversion_type != :stub
139
+ 'SomeClass.any_instance.'
140
+ else
141
+ 'obj.'
142
+ end
143
+
144
+ syntax << (positive? ? 'should_receive' : 'should_not_receive')
145
+ syntax << '(:message)'
146
+
147
+ if [:allow, :stub].include?(conversion_type)
148
+ syntax << '.any_number_of_times' if any_number_of_times?
149
+ syntax << '.at_least(0)' if at_least_zero?
150
+ end
151
+
152
+ syntax
153
+ end
154
+
155
+ def converted_syntax(conversion_type, negative_form_of_to)
156
+ return 'obj.stub(:message)' if conversion_type == :stub
157
+
158
+ syntax = case conversion_type
159
+ when :expect
160
+ if any_instance?
161
+ 'expect_any_instance_of(SomeClass).'
162
+ else
163
+ 'expect(obj).'
164
+ end
165
+ when :allow
166
+ if any_instance?
167
+ 'allow_any_instance_of(SomeClass).'
168
+ else
169
+ 'allow(obj).'
170
+ end
171
+ end
172
+
173
+ syntax << (positive? ? 'to' : negative_form_of_to)
174
+ syntax << ' receive(:message)'
175
+ end
124
176
  end
125
177
  end
126
178
  end