transpec 1.5.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Guardfile +18 -14
- data/README.md +1 -1
- data/README.md.erb +1 -1
- data/lib/transpec/cli.rb +2 -2
- data/lib/transpec/context_error.rb +23 -0
- data/lib/transpec/converter.rb +5 -5
- data/lib/transpec/dynamic_analyzer/rewriter.rb +1 -2
- data/lib/transpec/report.rb +5 -5
- data/lib/transpec/rspec_version.rb +10 -7
- data/lib/transpec/syntax.rb +76 -49
- data/lib/transpec/syntax/expect.rb +2 -6
- data/lib/transpec/syntax/have.rb +1 -2
- data/lib/transpec/syntax/its.rb +1 -1
- data/lib/transpec/syntax/method_stub.rb +29 -3
- data/lib/transpec/syntax/mixin/any_instance.rb +19 -18
- data/lib/transpec/syntax/mixin/have_matcher_owner.rb +35 -0
- data/lib/transpec/syntax/mixin/send.rb +33 -33
- data/lib/transpec/syntax/mixin/should_base.rb +22 -8
- data/lib/transpec/syntax/oneliner_should.rb +2 -7
- data/lib/transpec/syntax/operator_matcher.rb +4 -3
- data/lib/transpec/syntax/should.rb +4 -9
- data/lib/transpec/syntax/should_receive.rb +3 -5
- data/lib/transpec/version.rb +1 -1
- data/spec/support/shared_context.rb +2 -3
- data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +1 -1
- data/spec/transpec/report_spec.rb +1 -2
- data/spec/transpec/rspec_version_spec.rb +7 -1
- data/spec/transpec/syntax/double_spec.rb +4 -4
- data/spec/transpec/syntax/example_spec.rb +1 -1
- data/spec/transpec/syntax/method_stub_spec.rb +65 -11
- data/spec/transpec/syntax/should_receive_spec.rb +14 -14
- data/spec/transpec/syntax/should_spec.rb +10 -10
- data/transpec.gemspec +2 -2
- metadata +6 -5
- data/lib/transpec/syntax/mixin/have_matcher.rb +0 -23
data/lib/transpec/syntax/its.rb
CHANGED
@@ -13,7 +13,7 @@ module Transpec
|
|
13
13
|
receiver_node.nil? && method_name == :its
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
add_dynamic_analysis_request do |rewriter|
|
17
17
|
key = :project_requires_its?
|
18
18
|
code = 'defined?(RSpec::Its)'
|
19
19
|
rewriter.register_request(@node, key, code, :context)
|
@@ -13,17 +13,43 @@ module Transpec
|
|
13
13
|
class MethodStub < Syntax
|
14
14
|
include Mixin::Send, Mixin::MonkeyPatch, Mixin::AllowNoMessage, Mixin::AnyInstance, Util
|
15
15
|
|
16
|
+
# rubocop:disable LineLength
|
17
|
+
CLASSES_DEFINING_OWN_STUB_METHOD = [
|
18
|
+
'Typhoeus', # https://github.com/typhoeus/typhoeus/blob/6a59c62/lib/typhoeus.rb#L66-L85
|
19
|
+
'Excon', # https://github.com/geemus/excon/blob/6af4f9c/lib/excon.rb#L143-L178
|
20
|
+
'Factory' # https://github.com/thoughtbot/factory_girl/blob/v3.6.2/lib/factory_girl/syntax/vintage.rb#L112
|
21
|
+
]
|
22
|
+
# rubocop:enable LineLength
|
23
|
+
|
24
|
+
def self.dynamic_analysis_target_node?(node)
|
25
|
+
target_node?(node)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.conversion_target_node?(node, runtime_data = nil)
|
29
|
+
return false unless check_target_node_statically(node)
|
30
|
+
|
31
|
+
# Check if the method is RSpec's one or not.
|
32
|
+
if source_location(node, runtime_data)
|
33
|
+
# If we have a source location runtime data, check with it.
|
34
|
+
check_target_node_dynamically(node, runtime_data)
|
35
|
+
else
|
36
|
+
# Otherwise check with a static whitelist.
|
37
|
+
receiver_node = node.children.first
|
38
|
+
const_name = Util.const_name(receiver_node)
|
39
|
+
!CLASSES_DEFINING_OWN_STUB_METHOD.include?(const_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
16
43
|
def self.target_method?(receiver_node, method_name)
|
17
44
|
!receiver_node.nil? && [:stub, :stub!, :stub_chain, :unstub, :unstub!].include?(method_name)
|
18
45
|
end
|
19
46
|
|
20
|
-
|
47
|
+
add_dynamic_analysis_request do |rewriter|
|
21
48
|
register_request_of_syntax_availability_inspection(
|
22
49
|
rewriter,
|
23
50
|
:allow_to_receive_available?,
|
24
51
|
[:allow, :receive]
|
25
52
|
)
|
26
|
-
register_request_of_any_instance_inspection(rewriter)
|
27
53
|
end
|
28
54
|
|
29
55
|
def allow_to_receive_available?
|
@@ -36,7 +62,7 @@ module Transpec
|
|
36
62
|
return if method_name == :stub_chain && !rspec_version.receive_message_chain_available?
|
37
63
|
|
38
64
|
unless allow_to_receive_available?
|
39
|
-
fail
|
65
|
+
fail ContextError.new(selector_range, "##{method_name}", '#allow')
|
40
66
|
end
|
41
67
|
|
42
68
|
source, type = replacement_source_and_conversion_type(rspec_version)
|
@@ -8,29 +8,30 @@ module Transpec
|
|
8
8
|
module AnyInstance
|
9
9
|
include ::AST::Sexp
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
11
|
+
def self.included(syntax)
|
12
|
+
syntax.add_dynamic_analysis_request do |rewriter|
|
13
|
+
key = :any_instance_target_class_name
|
14
|
+
code = <<-END.gsub(/^\s+\|/, '').chomp
|
15
|
+
|if self.class.name == 'RSpec::Mocks::AnyInstance::Recorder'
|
16
|
+
| if respond_to?(:klass)
|
17
|
+
| klass.name
|
18
|
+
| elsif instance_variable_defined?(:@klass)
|
19
|
+
| instance_variable_get(:@klass).name
|
20
|
+
| else
|
21
|
+
| nil
|
22
|
+
| end
|
23
|
+
|else
|
24
|
+
| nil
|
25
|
+
|end
|
26
|
+
END
|
27
|
+
rewriter.register_request(subject_node, key, code)
|
28
|
+
end
|
27
29
|
end
|
28
30
|
|
29
31
|
def any_instance?
|
30
32
|
return true unless any_instance_target_node.nil?
|
31
33
|
node_data = runtime_node_data(subject_node)
|
32
|
-
return false unless node_data
|
33
|
-
return false unless node_data[:any_instance_target_class_name]
|
34
|
+
return false unless node_data && node_data[:any_instance_target_class_name]
|
34
35
|
!node_data[:any_instance_target_class_name].result.nil?
|
35
36
|
end
|
36
37
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'transpec/syntax/have'
|
4
|
+
|
5
|
+
module Transpec
|
6
|
+
class Syntax
|
7
|
+
module Mixin
|
8
|
+
module HaveMatcherOwner
|
9
|
+
def self.included(syntax)
|
10
|
+
syntax.add_dynamic_analysis_request do |rewriter|
|
11
|
+
if Have.dynamic_analysis_target_node?(matcher_node)
|
12
|
+
create_have_matcher.register_request_for_dynamic_analysis(rewriter)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def have_matcher
|
18
|
+
return @have_matcher if instance_variable_defined?(:@have_matcher)
|
19
|
+
|
20
|
+
@have_matcher ||= if Have.conversion_target_node?(matcher_node, @runtime_data)
|
21
|
+
create_have_matcher
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def create_have_matcher
|
30
|
+
Have.new(matcher_node, self, @source_rewriter, @runtime_data, @report)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,58 +1,42 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
3
5
|
module Transpec
|
4
6
|
class Syntax
|
5
7
|
module Mixin
|
6
8
|
module Send
|
7
|
-
|
8
|
-
klass.extend(ClassMethods)
|
9
|
-
end
|
9
|
+
extend ActiveSupport::Concern
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
def register_request_for_dynamic_analysis(node, rewriter)
|
13
|
-
return unless target_node?(node)
|
14
|
-
|
15
|
-
receiver_node, method_name, *_ = *node
|
16
|
-
|
17
|
-
if receiver_node
|
18
|
-
target_node = receiver_node
|
19
|
-
target_object_type = :object
|
20
|
-
else
|
21
|
-
target_node = node
|
22
|
-
target_object_type = :context
|
23
|
-
end
|
24
|
-
|
25
|
-
key = source_location_key(method_name)
|
26
|
-
code = "method(#{method_name.inspect}).source_location"
|
27
|
-
rewriter.register_request(target_node, key, code, target_object_type)
|
28
|
-
end
|
29
|
-
|
30
12
|
def target_node?(node, runtime_data = nil)
|
31
13
|
check_target_node_statically(node) && check_target_node_dynamically(node, runtime_data)
|
32
14
|
end
|
33
15
|
|
34
|
-
def target_method?(receiver_node, method_name)
|
35
|
-
false
|
36
|
-
end
|
37
|
-
|
38
16
|
def check_target_node_statically(node)
|
39
17
|
return false unless node && node.type == :send
|
40
18
|
receiver_node, method_name, *_ = *node
|
41
19
|
target_method?(receiver_node, method_name)
|
42
20
|
end
|
43
21
|
|
22
|
+
def target_method?(receiver_node, method_name)
|
23
|
+
NotImplementedError
|
24
|
+
end
|
25
|
+
|
44
26
|
def check_target_node_dynamically(node, runtime_data)
|
45
|
-
|
27
|
+
source_location = source_location(node, runtime_data)
|
28
|
+
return true unless source_location
|
29
|
+
file_path = source_location.first
|
30
|
+
!file_path.match(%r{/gems/rspec\-[^/]+/lib/rspec/}).nil?
|
31
|
+
end
|
46
32
|
|
33
|
+
def source_location(node, runtime_data)
|
34
|
+
return unless runtime_data
|
47
35
|
receiver_node, method_name, *_ = *node
|
48
36
|
target_node = receiver_node ? receiver_node : node
|
49
|
-
|
50
|
-
return
|
51
|
-
|
52
|
-
return true unless (source_location = eval_data.result)
|
53
|
-
|
54
|
-
file_path = source_location.first
|
55
|
-
!file_path.match(%r{/gems/rspec\-[^/]+/lib/rspec/}).nil?
|
37
|
+
return unless (node_data = runtime_data[target_node])
|
38
|
+
return unless (eval_data = node_data[source_location_key(method_name)])
|
39
|
+
eval_data.result
|
56
40
|
end
|
57
41
|
|
58
42
|
def source_location_key(method_name)
|
@@ -60,6 +44,22 @@ module Transpec
|
|
60
44
|
end
|
61
45
|
end
|
62
46
|
|
47
|
+
def self.included(syntax)
|
48
|
+
syntax.add_dynamic_analysis_request do |rewriter|
|
49
|
+
if receiver_node
|
50
|
+
target_node = receiver_node
|
51
|
+
target_object_type = :object
|
52
|
+
else
|
53
|
+
target_node = @node
|
54
|
+
target_object_type = :context
|
55
|
+
end
|
56
|
+
|
57
|
+
key = self.class.source_location_key(method_name)
|
58
|
+
code = "method(#{method_name.inspect}).source_location"
|
59
|
+
rewriter.register_request(target_node, key, code, target_object_type)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
63
|
def receiver_node
|
64
64
|
@node.children[0]
|
65
65
|
end
|
@@ -6,6 +6,14 @@ module Transpec
|
|
6
6
|
class Syntax
|
7
7
|
module Mixin
|
8
8
|
module ShouldBase
|
9
|
+
def self.included(syntax)
|
10
|
+
syntax.add_dynamic_analysis_request do |rewriter|
|
11
|
+
if OperatorMatcher.dynamic_analysis_target_node?(matcher_node)
|
12
|
+
create_operator_matcher.register_request_for_dynamic_analysis(rewriter)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
def positive?
|
10
18
|
method_name == :should
|
11
19
|
end
|
@@ -14,24 +22,30 @@ module Transpec
|
|
14
22
|
arg_node || parent_node
|
15
23
|
end
|
16
24
|
|
25
|
+
def should_range
|
26
|
+
if arg_node
|
27
|
+
selector_range
|
28
|
+
else
|
29
|
+
selector_range.join(expression_range.end)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
17
33
|
def operator_matcher
|
18
34
|
return @operator_matcher if instance_variable_defined?(:@operator_matcher)
|
19
35
|
|
20
36
|
@operator_matcher ||= begin
|
21
|
-
if OperatorMatcher.
|
22
|
-
|
37
|
+
if OperatorMatcher.conversion_target_node?(matcher_node, @runtime_data)
|
38
|
+
create_operator_matcher
|
23
39
|
else
|
24
40
|
nil
|
25
41
|
end
|
26
42
|
end
|
27
43
|
end
|
28
44
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
selector_range.join(expression_range.end)
|
34
|
-
end
|
45
|
+
private
|
46
|
+
|
47
|
+
def create_operator_matcher
|
48
|
+
OperatorMatcher.new(matcher_node, @source_rewriter, @runtime_data, @report)
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'transpec/syntax'
|
4
4
|
require 'transpec/syntax/mixin/should_base'
|
5
5
|
require 'transpec/syntax/mixin/send'
|
6
|
-
require 'transpec/syntax/mixin/
|
6
|
+
require 'transpec/syntax/mixin/have_matcher_owner'
|
7
7
|
require 'transpec/rspec_dsl'
|
8
8
|
require 'transpec/util'
|
9
9
|
require 'active_support/inflector/methods'
|
@@ -11,7 +11,7 @@ require 'active_support/inflector/methods'
|
|
11
11
|
module Transpec
|
12
12
|
class Syntax
|
13
13
|
class OnelinerShould < Syntax
|
14
|
-
include Mixin::ShouldBase, Mixin::Send, Mixin::
|
14
|
+
include Mixin::ShouldBase, Mixin::Send, Mixin::HaveMatcherOwner, RSpecDSL, Util
|
15
15
|
|
16
16
|
attr_reader :current_syntax_type
|
17
17
|
|
@@ -24,11 +24,6 @@ module Transpec
|
|
24
24
|
@current_syntax_type = :should
|
25
25
|
end
|
26
26
|
|
27
|
-
def register_request_for_dynamic_analysis(rewriter)
|
28
|
-
operator_matcher.register_request_for_dynamic_analysis(rewriter) if operator_matcher
|
29
|
-
have_matcher.register_request_for_dynamic_analysis(rewriter) if have_matcher
|
30
|
-
end
|
31
|
-
|
32
27
|
def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
|
33
28
|
replacement = 'is_expected.'
|
34
29
|
replacement << (positive? ? 'to' : negative_form)
|
@@ -20,9 +20,10 @@ module Transpec
|
|
20
20
|
!receiver_node.nil? && OPERATORS.include?(method_name)
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
add_dynamic_analysis_request do |rewriter|
|
24
|
+
if method_name == :=~
|
25
|
+
rewriter.register_request(arg_node, :arg_is_enumerable?, 'is_a?(Enumerable)')
|
26
|
+
end
|
26
27
|
end
|
27
28
|
|
28
29
|
def convert_operator!(parenthesize_arg = true)
|
@@ -5,14 +5,14 @@ require 'transpec/syntax/mixin/should_base'
|
|
5
5
|
require 'transpec/syntax/mixin/send'
|
6
6
|
require 'transpec/syntax/mixin/monkey_patch'
|
7
7
|
require 'transpec/syntax/mixin/expectizable'
|
8
|
-
require 'transpec/syntax/mixin/
|
8
|
+
require 'transpec/syntax/mixin/have_matcher_owner'
|
9
9
|
require 'transpec/util'
|
10
10
|
|
11
11
|
module Transpec
|
12
12
|
class Syntax
|
13
13
|
class Should < Syntax
|
14
14
|
include Mixin::ShouldBase, Mixin::Send, Mixin::MonkeyPatch, Mixin::Expectizable,
|
15
|
-
Mixin::
|
15
|
+
Mixin::HaveMatcherOwner, Util
|
16
16
|
|
17
17
|
attr_reader :current_syntax_type
|
18
18
|
|
@@ -25,15 +25,12 @@ module Transpec
|
|
25
25
|
@current_syntax_type = :should
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
add_dynamic_analysis_request do |rewriter|
|
29
29
|
register_request_of_syntax_availability_inspection(
|
30
30
|
rewriter,
|
31
31
|
:expect_available?,
|
32
32
|
[:expect]
|
33
33
|
)
|
34
|
-
|
35
|
-
operator_matcher.register_request_for_dynamic_analysis(rewriter) if operator_matcher
|
36
|
-
have_matcher.register_request_for_dynamic_analysis(rewriter) if have_matcher
|
37
34
|
end
|
38
35
|
|
39
36
|
def expect_available?
|
@@ -41,9 +38,7 @@ module Transpec
|
|
41
38
|
end
|
42
39
|
|
43
40
|
def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
|
44
|
-
unless expect_available?
|
45
|
-
fail InvalidContextError.new(selector_range, "##{method_name}", '#expect')
|
46
|
-
end
|
41
|
+
fail ContextError.new(selector_range, "##{method_name}", '#expect') unless expect_available?
|
47
42
|
|
48
43
|
if proc_literal?(subject_node)
|
49
44
|
replace(range_of_subject_method_taking_block, 'expect')
|
@@ -19,7 +19,7 @@ module Transpec
|
|
19
19
|
!receiver_node.nil? && [:should_receive, :should_not_receive].include?(method_name)
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
add_dynamic_analysis_request do |rewriter|
|
23
23
|
register_request_of_syntax_availability_inspection(
|
24
24
|
rewriter,
|
25
25
|
:expect_to_receive_available?,
|
@@ -31,8 +31,6 @@ module Transpec
|
|
31
31
|
:allow_to_receive_available?,
|
32
32
|
[:allow, :receive]
|
33
33
|
)
|
34
|
-
|
35
|
-
register_request_of_any_instance_inspection(rewriter)
|
36
34
|
end
|
37
35
|
|
38
36
|
def expect_to_receive_available?
|
@@ -49,7 +47,7 @@ module Transpec
|
|
49
47
|
|
50
48
|
def expectize!(negative_form = 'not_to')
|
51
49
|
unless expect_to_receive_available?
|
52
|
-
fail
|
50
|
+
fail ContextError.new(selector_range, "##{method_name}", '#expect')
|
53
51
|
end
|
54
52
|
|
55
53
|
convert_to_syntax!('expect', negative_form)
|
@@ -60,7 +58,7 @@ module Transpec
|
|
60
58
|
return unless useless_expectation?
|
61
59
|
|
62
60
|
unless allow_to_receive_available?
|
63
|
-
fail
|
61
|
+
fail ContextError.new(selector_range, "##{method_name}", '#allow')
|
64
62
|
end
|
65
63
|
|
66
64
|
convert_to_syntax!('allow', negative_form)
|
data/lib/transpec/version.rb
CHANGED
@@ -60,7 +60,7 @@ end
|
|
60
60
|
shared_context 'syntax object' do |syntax_class, name|
|
61
61
|
let(name) do
|
62
62
|
ast.each_node do |node|
|
63
|
-
next unless syntax_class.
|
63
|
+
next unless syntax_class.conversion_target_node?(node)
|
64
64
|
return syntax_class.new(node, source_rewriter, runtime_data)
|
65
65
|
end
|
66
66
|
|
@@ -70,6 +70,7 @@ end
|
|
70
70
|
|
71
71
|
shared_context 'isolated environment' do
|
72
72
|
around do |example|
|
73
|
+
require 'tmpdir'
|
73
74
|
Dir.mktmpdir do |tmpdir|
|
74
75
|
Dir.chdir(tmpdir) do
|
75
76
|
example.run
|
@@ -79,8 +80,6 @@ shared_context 'isolated environment' do
|
|
79
80
|
end
|
80
81
|
|
81
82
|
shared_context 'inside of git repository' do
|
82
|
-
require 'tmpdir'
|
83
|
-
|
84
83
|
around do |example|
|
85
84
|
Dir.mkdir('repo')
|
86
85
|
Dir.chdir('repo') do
|