transpec 0.1.3 → 0.2.0

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