transpec 1.9.3 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/CONTRIBUTING.md +19 -0
- data/README.md +78 -9
- data/README.md.erb +68 -10
- data/lib/transpec/annotatable.rb +16 -0
- data/lib/transpec/cli.rb +35 -27
- data/lib/transpec/configuration.rb +1 -0
- data/lib/transpec/conversion_error.rb +23 -0
- data/lib/transpec/converter.rb +59 -50
- data/lib/transpec/dynamic_analyzer.rb +13 -29
- data/lib/transpec/dynamic_analyzer/rewriter.rb +3 -10
- data/lib/transpec/dynamic_analyzer/runtime_data.rb +16 -8
- data/lib/transpec/file_finder.rb +1 -1
- data/lib/transpec/git.rb +1 -1
- data/lib/transpec/option_parser.rb +12 -10
- data/lib/transpec/project.rb +3 -3
- data/lib/transpec/record.rb +19 -2
- data/lib/transpec/report.rb +29 -13
- data/lib/transpec/rspec_version.rb +7 -7
- data/lib/transpec/static_context_inspector.rb +1 -5
- data/lib/transpec/syntax.rb +11 -16
- data/lib/transpec/syntax/be_boolean.rb +1 -1
- data/lib/transpec/syntax/be_close.rb +1 -1
- data/lib/transpec/syntax/current_example.rb +88 -0
- data/lib/transpec/syntax/double.rb +1 -1
- data/lib/transpec/syntax/example.rb +60 -54
- data/lib/transpec/syntax/have.rb +27 -15
- data/lib/transpec/syntax/have/have_record.rb +12 -0
- data/lib/transpec/syntax/have/source_builder.rb +18 -16
- data/lib/transpec/syntax/its.rb +12 -11
- data/lib/transpec/syntax/matcher_definition.rb +1 -1
- data/lib/transpec/syntax/method_stub.rb +3 -7
- data/lib/transpec/syntax/mixin/matcher_owner.rb +2 -2
- data/lib/transpec/syntax/mixin/monkey_patch.rb +3 -5
- data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +2 -4
- data/lib/transpec/syntax/mixin/owned_matcher.rb +1 -4
- data/lib/transpec/syntax/mixin/send.rb +7 -9
- data/lib/transpec/syntax/oneliner_should.rb +4 -4
- data/lib/transpec/syntax/operator.rb +27 -11
- data/lib/transpec/syntax/pending.rb +110 -0
- data/lib/transpec/syntax/raise_error.rb +1 -1
- data/lib/transpec/syntax/receive.rb +4 -4
- data/lib/transpec/syntax/rspec_configure/framework.rb +3 -3
- data/lib/transpec/syntax/should.rb +2 -2
- data/lib/transpec/syntax/should_receive.rb +3 -3
- data/lib/transpec/util.rb +38 -6
- data/lib/transpec/version.rb +2 -2
- data/spec/support/file_helper.rb +1 -1
- data/spec/support/shared_context.rb +3 -8
- data/spec/transpec/cli_spec.rb +63 -1
- data/spec/transpec/configuration_spec.rb +1 -0
- data/spec/transpec/converter_spec.rb +106 -15
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +12 -52
- data/spec/transpec/dynamic_analyzer_spec.rb +2 -2
- data/spec/transpec/option_parser_spec.rb +3 -2
- data/spec/transpec/report_spec.rb +33 -4
- data/spec/transpec/rspec_version_spec.rb +5 -2
- data/spec/transpec/syntax/current_example_spec.rb +267 -0
- data/spec/transpec/syntax/example_spec.rb +156 -122
- data/spec/transpec/syntax/have_spec.rb +43 -32
- data/spec/transpec/syntax/method_stub_spec.rb +8 -0
- data/spec/transpec/syntax/operator_spec.rb +67 -2
- data/spec/transpec/syntax/pending_spec.rb +375 -0
- metadata +12 -4
- data/lib/transpec/context_error.rb +0 -23
@@ -17,14 +17,12 @@ module Transpec
|
|
17
17
|
code << " && respond_to?(#{method.inspect})"
|
18
18
|
end
|
19
19
|
|
20
|
-
rewriter.register_request(
|
20
|
+
rewriter.register_request(node, key, code, :context)
|
21
21
|
end
|
22
22
|
|
23
23
|
def check_syntax_availability(key)
|
24
|
-
|
25
|
-
|
26
|
-
if node_data
|
27
|
-
node_data[key].result
|
24
|
+
if runtime_data.present?(node, key)
|
25
|
+
runtime_data[node, key]
|
28
26
|
else
|
29
27
|
static_context_inspector.send(key)
|
30
28
|
end
|
@@ -32,9 +32,7 @@ module Transpec
|
|
32
32
|
|
33
33
|
def any_instance?
|
34
34
|
return true unless any_instance_target_node.nil?
|
35
|
-
|
36
|
-
return false unless node_data && node_data[:any_instance_target_class_name]
|
37
|
-
!node_data[:any_instance_target_class_name].result.nil?
|
35
|
+
!runtime_data[subject_node, :any_instance_target_class_name].nil?
|
38
36
|
end
|
39
37
|
|
40
38
|
private
|
@@ -45,7 +43,7 @@ module Transpec
|
|
45
43
|
if any_instance_target_node
|
46
44
|
any_instance_target_node.loc.expression.source
|
47
45
|
else
|
48
|
-
|
46
|
+
runtime_data[subject_node, :any_instance_target_class_name]
|
49
47
|
end
|
50
48
|
end
|
51
49
|
|
@@ -19,11 +19,8 @@ module Transpec
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(node, expectation, source_rewriter = nil, runtime_data = nil, report = nil)
|
22
|
-
|
22
|
+
super(node, source_rewriter, runtime_data, report)
|
23
23
|
@expectation = expectation
|
24
|
-
@source_rewriter = source_rewriter
|
25
|
-
@runtime_data = runtime_data
|
26
|
-
@report = report || Report.new
|
27
24
|
end
|
28
25
|
end
|
29
26
|
end
|
@@ -34,9 +34,7 @@ module Transpec
|
|
34
34
|
return unless runtime_data
|
35
35
|
receiver_node, method_name, *_ = *node
|
36
36
|
target_node = receiver_node ? receiver_node : node
|
37
|
-
|
38
|
-
return unless (eval_data = node_data[source_location_key(method_name)])
|
39
|
-
eval_data.result
|
37
|
+
runtime_data[target_node, source_location_key(method_name)]
|
40
38
|
end
|
41
39
|
|
42
40
|
def source_location_key(method_name)
|
@@ -50,7 +48,7 @@ module Transpec
|
|
50
48
|
target_node = receiver_node
|
51
49
|
target_object_type = :object
|
52
50
|
else
|
53
|
-
target_node =
|
51
|
+
target_node = node
|
54
52
|
target_object_type = :context
|
55
53
|
end
|
56
54
|
|
@@ -61,23 +59,23 @@ module Transpec
|
|
61
59
|
end
|
62
60
|
|
63
61
|
def receiver_node
|
64
|
-
|
62
|
+
node.children[0]
|
65
63
|
end
|
66
64
|
|
67
65
|
def method_name
|
68
|
-
|
66
|
+
node.children[1]
|
69
67
|
end
|
70
68
|
|
71
69
|
def arg_node
|
72
|
-
|
70
|
+
node.children[2]
|
73
71
|
end
|
74
72
|
|
75
73
|
def arg_nodes
|
76
|
-
|
74
|
+
node.children[2..-1]
|
77
75
|
end
|
78
76
|
|
79
77
|
def selector_range
|
80
|
-
|
78
|
+
node.loc.selector
|
81
79
|
end
|
82
80
|
|
83
81
|
def receiver_range
|
@@ -43,7 +43,7 @@ module Transpec
|
|
43
43
|
|
44
44
|
have_matcher.convert_to_standard_expectation!
|
45
45
|
|
46
|
-
|
46
|
+
report.records << OnelinerShouldHaveRecord.new(self, have_matcher)
|
47
47
|
end
|
48
48
|
|
49
49
|
# rubocop:disable LineLength
|
@@ -61,7 +61,7 @@ module Transpec
|
|
61
61
|
@current_syntax_type = :expect
|
62
62
|
have_matcher.convert_to_standard_expectation!
|
63
63
|
|
64
|
-
|
64
|
+
report.records << OnelinerShouldHaveRecord.new(self, have_matcher, negative_form)
|
65
65
|
end
|
66
66
|
|
67
67
|
def example_has_description?
|
@@ -111,7 +111,7 @@ module Transpec
|
|
111
111
|
def example_block_node
|
112
112
|
return @example_block_node if instance_variable_defined?(:@example_block_node)
|
113
113
|
|
114
|
-
@example_block_node =
|
114
|
+
@example_block_node = node.each_ancestor_node.find do |node|
|
115
115
|
next false unless node.block_type?
|
116
116
|
send_node = node.children.first
|
117
117
|
receiver_node, method_name, = *send_node
|
@@ -157,7 +157,7 @@ module Transpec
|
|
157
157
|
syntax << ' ... }'
|
158
158
|
end
|
159
159
|
|
160
|
-
|
160
|
+
report.records << Record.new(original_syntax, converted_syntax)
|
161
161
|
end
|
162
162
|
|
163
163
|
class OnelinerShouldHaveRecord < Have::HaveRecord
|
@@ -19,12 +19,12 @@ module Transpec
|
|
19
19
|
false
|
20
20
|
end
|
21
21
|
|
22
|
-
def self.
|
22
|
+
def self.check_target_node_statically(node)
|
23
23
|
node = node.parent_node if node == BE_NODE
|
24
|
+
return false unless node && node.send_type?
|
24
25
|
receiver_node, method_name, *_ = *node
|
25
26
|
return false if receiver_node.nil?
|
26
|
-
|
27
|
-
check_target_node_dynamically(node, runtime_data)
|
27
|
+
OPERATORS.include?(method_name)
|
28
28
|
end
|
29
29
|
|
30
30
|
define_dynamic_analysis_request do |rewriter|
|
@@ -94,10 +94,12 @@ module Transpec
|
|
94
94
|
|
95
95
|
# Need to register record after all source rewrites are done
|
96
96
|
# to avoid false record when failed with overlapped rewrite.
|
97
|
+
accurate = !arg_is_enumerable?.nil?
|
98
|
+
|
97
99
|
if arg_is_enumerable?
|
98
|
-
register_record('=~ [1, 2]', 'match_array([1, 2])')
|
100
|
+
register_record('=~ [1, 2]', 'match_array([1, 2])', accurate)
|
99
101
|
else
|
100
|
-
register_record('=~ /pattern/', 'match(/pattern/)')
|
102
|
+
register_record('=~ /pattern/', 'match(/pattern/)', accurate)
|
101
103
|
end
|
102
104
|
end
|
103
105
|
|
@@ -110,9 +112,14 @@ module Transpec
|
|
110
112
|
end
|
111
113
|
|
112
114
|
def arg_is_enumerable?
|
113
|
-
|
114
|
-
|
115
|
-
|
115
|
+
case arg_node.type
|
116
|
+
when :array
|
117
|
+
true
|
118
|
+
when :regexp
|
119
|
+
false
|
120
|
+
else
|
121
|
+
runtime_data[arg_node, :arg_is_enumerable?]
|
122
|
+
end
|
116
123
|
end
|
117
124
|
|
118
125
|
def parenthesize_single_line!(always)
|
@@ -128,7 +135,7 @@ module Transpec
|
|
128
135
|
|
129
136
|
def parenthesize_multi_line!(linefeed)
|
130
137
|
insert_before(range_in_between_selector_and_arg, '(')
|
131
|
-
matcher_line_indentation = indentation_of_line(
|
138
|
+
matcher_line_indentation = indentation_of_line(node)
|
132
139
|
right_parenthesis = "#{linefeed}#{matcher_line_indentation})"
|
133
140
|
insert_after(expression_range, right_parenthesis)
|
134
141
|
end
|
@@ -150,9 +157,18 @@ module Transpec
|
|
150
157
|
end
|
151
158
|
end
|
152
159
|
|
153
|
-
def
|
160
|
+
def matcher_range
|
161
|
+
if be_node
|
162
|
+
expression_range
|
163
|
+
else
|
164
|
+
selector_range.join(expression_range.end)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def register_record(original_syntax, converted_syntax, accurate = true)
|
154
169
|
original_syntax ||= "#{method_name} expected"
|
155
|
-
|
170
|
+
annotation = AccuracyAnnotation.new(matcher_range) unless accurate
|
171
|
+
report.records << Record.new(original_syntax, converted_syntax, annotation)
|
156
172
|
end
|
157
173
|
end
|
158
174
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax'
|
4
|
+
require 'transpec/syntax/mixin/send'
|
5
|
+
require 'transpec/util'
|
6
|
+
|
7
|
+
module Transpec
|
8
|
+
class Syntax
|
9
|
+
class Pending < Syntax
|
10
|
+
include Mixin::Send, Util
|
11
|
+
|
12
|
+
def self.conversion_target_node?(node, runtime_data = nil)
|
13
|
+
return false unless target_node?(node, runtime_data)
|
14
|
+
|
15
|
+
# Check whether the context is example group to differenciate
|
16
|
+
# RSpec::Core::ExampleGroup.pending (a relative of #it) and
|
17
|
+
# RSpec::Core::ExampleGroup#pending (marks the example as pending in #it block).
|
18
|
+
if runtime_data && runtime_data.run?(node)
|
19
|
+
# If we have runtime data, check with it.
|
20
|
+
runtime_data[node, :example_context?]
|
21
|
+
else
|
22
|
+
# Otherwise check statically.
|
23
|
+
inspector = StaticContextInspector.new(node)
|
24
|
+
inspector.scopes.last == :example
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.target_method?(receiver_node, method_name)
|
29
|
+
receiver_node.nil? && method_name == :pending
|
30
|
+
end
|
31
|
+
|
32
|
+
define_dynamic_analysis_request do |rewriter|
|
33
|
+
code = 'is_a?(RSpec::Core::ExampleGroup)'
|
34
|
+
rewriter.register_request(node, :example_context?, code, :context)
|
35
|
+
end
|
36
|
+
|
37
|
+
def convert_deprecated_syntax!
|
38
|
+
if block_node
|
39
|
+
unblock!
|
40
|
+
else
|
41
|
+
convert_to_skip!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def convert_to_skip!
|
48
|
+
replace(selector_range, 'skip')
|
49
|
+
register_record('pending', 'skip')
|
50
|
+
end
|
51
|
+
|
52
|
+
def unblock!
|
53
|
+
if block_beginning_line == block_body_line
|
54
|
+
range_between_pending_and_body =
|
55
|
+
expression_range.end.join(block_body_node.loc.expression.begin)
|
56
|
+
replace(range_between_pending_and_body, "\n" + indentation_of_line(node))
|
57
|
+
else
|
58
|
+
remove(expression_range.end.join(block_node.loc.begin))
|
59
|
+
outdent!(block_body_node, node)
|
60
|
+
end
|
61
|
+
|
62
|
+
if block_body_line == block_end_line
|
63
|
+
remove(block_body_node.loc.expression.end.join(block_node.loc.end))
|
64
|
+
else
|
65
|
+
remove(line_range(block_node.loc.end))
|
66
|
+
end
|
67
|
+
|
68
|
+
register_record('pending { do_something_fail }', 'pending; do_something_fail')
|
69
|
+
end
|
70
|
+
|
71
|
+
def outdent!(target_node, base_node)
|
72
|
+
indentation_width = indentation_width(target_node, base_node)
|
73
|
+
|
74
|
+
return unless indentation_width > 0
|
75
|
+
|
76
|
+
each_line_range(target_node) do |line_range|
|
77
|
+
remove(line_range.resize(indentation_width))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def indentation_width(target, base)
|
82
|
+
indentation_of_line(target).size - indentation_of_line(base).size
|
83
|
+
end
|
84
|
+
|
85
|
+
def block_node
|
86
|
+
block_node_taken_by_method(node)
|
87
|
+
end
|
88
|
+
|
89
|
+
def block_body_node
|
90
|
+
block_node.children[2]
|
91
|
+
end
|
92
|
+
|
93
|
+
def block_beginning_line
|
94
|
+
block_node.loc.begin.line
|
95
|
+
end
|
96
|
+
|
97
|
+
def block_body_line
|
98
|
+
block_body_node.loc.expression.line
|
99
|
+
end
|
100
|
+
|
101
|
+
def block_end_line
|
102
|
+
block_node.loc.end.line
|
103
|
+
end
|
104
|
+
|
105
|
+
def register_record(original_syntax, converted_syntax)
|
106
|
+
report.records << Record.new(original_syntax, converted_syntax)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -17,22 +17,22 @@ module Transpec
|
|
17
17
|
def remove_useless_and_return!
|
18
18
|
removed = super
|
19
19
|
return unless removed
|
20
|
-
|
20
|
+
report.records << ReceiveUselessAndReturnRecord.new(self)
|
21
21
|
end
|
22
22
|
|
23
23
|
def add_receiver_arg_to_any_instance_implementation_block!
|
24
24
|
added = super
|
25
25
|
return unless added
|
26
|
-
|
26
|
+
report.records << ReceiveAnyInstanceBlockRecord.new(self)
|
27
27
|
end
|
28
28
|
|
29
29
|
def any_instance?
|
30
|
-
|
30
|
+
expectation.any_instance?
|
31
31
|
end
|
32
32
|
|
33
33
|
def any_instance_block_node
|
34
34
|
return unless any_instance?
|
35
|
-
super ||
|
35
|
+
super || expectation.block_node
|
36
36
|
end
|
37
37
|
|
38
38
|
class ReceiveAnyInstanceBlockRecord < AnyInstanceBlockRecord
|
@@ -9,7 +9,7 @@ module Transpec
|
|
9
9
|
class Framework
|
10
10
|
include Util, ::AST::Sexp
|
11
11
|
|
12
|
-
attr_reader :rspec_configure
|
12
|
+
attr_reader :rspec_configure, :source_rewriter
|
13
13
|
|
14
14
|
def initialize(rspec_configure, source_rewriter)
|
15
15
|
@rspec_configure = rspec_configure
|
@@ -27,7 +27,7 @@ module Transpec
|
|
27
27
|
|
28
28
|
if setter_node
|
29
29
|
arg_node = setter_node.children[2]
|
30
|
-
|
30
|
+
source_rewriter.replace(arg_node.loc.expression, value.to_s)
|
31
31
|
else
|
32
32
|
add_configuration!(config_name, value)
|
33
33
|
end
|
@@ -77,7 +77,7 @@ module Transpec
|
|
77
77
|
lines.map! { |line| line + "\n" }
|
78
78
|
|
79
79
|
insertion_position = beginning_of_line_range(block_node_to_insert_code.loc.end)
|
80
|
-
|
80
|
+
source_rewriter.insert_before(insertion_position, lines.join(''))
|
81
81
|
end
|
82
82
|
|
83
83
|
def config_variable_name
|
@@ -35,7 +35,7 @@ module Transpec
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
|
38
|
-
fail ContextError.new(
|
38
|
+
fail ContextError.new("##{method_name}", '#expect', selector_range) unless expect_available?
|
39
39
|
|
40
40
|
if proc_literal?(subject_node)
|
41
41
|
replace(range_of_subject_method_taking_block, 'expect')
|
@@ -74,7 +74,7 @@ module Transpec
|
|
74
74
|
converted_syntax << negative_form_of_to
|
75
75
|
end
|
76
76
|
|
77
|
-
|
77
|
+
report.records << Record.new(original_syntax, converted_syntax)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -45,7 +45,7 @@ module Transpec
|
|
45
45
|
|
46
46
|
def expectize!(negative_form = 'not_to')
|
47
47
|
unless expect_to_receive_available?
|
48
|
-
fail ContextError.new(
|
48
|
+
fail ContextError.new("##{method_name}", '#expect', selector_range)
|
49
49
|
end
|
50
50
|
|
51
51
|
convert_to_syntax!('expect', negative_form)
|
@@ -56,7 +56,7 @@ module Transpec
|
|
56
56
|
return unless useless_expectation?
|
57
57
|
|
58
58
|
unless allow_to_receive_available?
|
59
|
-
fail ContextError.new(
|
59
|
+
fail ContextError.new("##{method_name}", '#allow', selector_range)
|
60
60
|
end
|
61
61
|
|
62
62
|
convert_to_syntax!('allow', negative_form)
|
@@ -157,7 +157,7 @@ module Transpec
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def register_record(record_class, negative_form_of_to = nil)
|
160
|
-
|
160
|
+
report.records << record_class.new(self, negative_form_of_to)
|
161
161
|
end
|
162
162
|
|
163
163
|
class ExpectBaseRecord < Record
|
data/lib/transpec/util.rb
CHANGED
@@ -12,6 +12,14 @@ module Transpec
|
|
12
12
|
|
13
13
|
module_function
|
14
14
|
|
15
|
+
def node_id(node)
|
16
|
+
source_range = node.loc.expression
|
17
|
+
source_buffer = source_range.source_buffer
|
18
|
+
absolute_path = File.expand_path(source_buffer.name)
|
19
|
+
relative_path = Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
|
20
|
+
[relative_path, source_range.begin_pos, source_range.end_pos].join('_')
|
21
|
+
end
|
22
|
+
|
15
23
|
def proc_literal?(node)
|
16
24
|
return false unless node.block_type?
|
17
25
|
|
@@ -133,16 +141,40 @@ module Transpec
|
|
133
141
|
end
|
134
142
|
|
135
143
|
def beginning_of_line_range(arg)
|
136
|
-
range =
|
137
|
-
when AST::Node then arg.loc.expression
|
138
|
-
when Parser::Source::Range then arg
|
139
|
-
else fail ArgumentError, "Invalid argument #{arg}"
|
140
|
-
end
|
141
|
-
|
144
|
+
range = range_from_arg(arg)
|
142
145
|
begin_pos = range.begin_pos - range.column
|
143
146
|
Parser::Source::Range.new(range.source_buffer, begin_pos, begin_pos)
|
144
147
|
end
|
145
148
|
|
149
|
+
def end_of_line_range(arg)
|
150
|
+
range = range_from_arg(arg)
|
151
|
+
begin_pos = beginning_of_line_range(range).begin_pos + range.source_line.size
|
152
|
+
Parser::Source::Range.new(range.source_buffer, begin_pos, begin_pos)
|
153
|
+
end
|
154
|
+
|
155
|
+
def line_range(arg)
|
156
|
+
range = range_from_arg(arg)
|
157
|
+
beginning_of_line_range(range).resize(range.source_line.size + 1)
|
158
|
+
end
|
159
|
+
|
160
|
+
def each_line_range(arg)
|
161
|
+
multiline_range = range_from_arg(arg)
|
162
|
+
range = line_range(multiline_range)
|
163
|
+
|
164
|
+
while range.line <= multiline_range.end.line
|
165
|
+
yield range
|
166
|
+
range = line_range(range.end)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def range_from_arg(arg)
|
171
|
+
case arg
|
172
|
+
when AST::Node then arg.loc.expression
|
173
|
+
when Parser::Source::Range then arg
|
174
|
+
else fail ArgumentError, "Invalid argument #{arg}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
146
178
|
def literal?(node)
|
147
179
|
case node.type
|
148
180
|
when *LITERAL_TYPES
|