transpec 1.3.1 → 1.4.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 -3
- data/CHANGELOG.md +9 -0
- data/README.md +141 -24
- data/README.md.erb +136 -24
- data/lib/transpec/ast/node.rb +7 -3
- data/lib/transpec/cli.rb +1 -1
- data/lib/transpec/configuration.rb +1 -0
- data/lib/transpec/converter.rb +31 -2
- data/lib/transpec/dynamic_analyzer.rb +1 -1
- data/lib/transpec/option_parser.rb +12 -9
- data/lib/transpec/project.rb +23 -12
- data/lib/transpec/rspec_version.rb +18 -4
- data/lib/transpec/static_context_inspector.rb +0 -15
- data/lib/transpec/syntax/example.rb +83 -0
- data/lib/transpec/syntax/expect.rb +5 -0
- data/lib/transpec/syntax/have.rb +111 -54
- data/lib/transpec/syntax/method_stub.rb +58 -37
- data/lib/transpec/syntax/mixin/allow_no_message.rb +2 -0
- data/lib/transpec/syntax/mixin/any_instance.rb +2 -0
- data/lib/transpec/syntax/mixin/should_base.rb +39 -0
- data/lib/transpec/syntax/oneliner_should.rb +218 -0
- data/lib/transpec/syntax/operator_matcher.rb +1 -0
- data/lib/transpec/syntax/should.rb +3 -30
- data/lib/transpec/util.rb +54 -0
- data/lib/transpec/version.rb +2 -2
- data/spec/support/shared_context.rb +21 -29
- data/spec/transpec/ast/node_spec.rb +1 -1
- data/spec/transpec/commit_message_spec.rb +29 -23
- data/spec/transpec/configuration_spec.rb +1 -0
- data/spec/transpec/converter_spec.rb +208 -5
- data/spec/transpec/dynamic_analyzer_spec.rb +2 -2
- data/spec/transpec/option_parser_spec.rb +1 -0
- data/spec/transpec/project_spec.rb +10 -0
- data/spec/transpec/rspec_version_spec.rb +52 -28
- data/spec/transpec/static_context_inspector_spec.rb +2 -2
- data/spec/transpec/syntax/be_boolean_spec.rb +6 -13
- data/spec/transpec/syntax/be_close_spec.rb +2 -9
- data/spec/transpec/syntax/double_spec.rb +2 -9
- data/spec/transpec/syntax/example_spec.rb +249 -0
- data/spec/transpec/syntax/expect_spec.rb +1 -1
- data/spec/transpec/syntax/have_spec.rb +127 -22
- data/spec/transpec/syntax/its_spec.rb +9 -18
- data/spec/transpec/syntax/method_stub_spec.rb +193 -158
- data/spec/transpec/syntax/oneliner_should_spec.rb +653 -0
- data/spec/transpec/syntax/operator_matcher_spec.rb +7 -8
- data/spec/transpec/syntax/raise_error_spec.rb +6 -13
- data/spec/transpec/syntax/rspec_configure_spec.rb +1 -8
- data/spec/transpec/syntax/should_receive_spec.rb +19 -28
- data/spec/transpec/syntax/should_spec.rb +18 -16
- data/spec/transpec/util_spec.rb +30 -0
- data/transpec.gemspec +8 -7
- metadata +49 -28
data/lib/transpec/ast/node.rb
CHANGED
@@ -5,9 +5,13 @@ require 'parser'
|
|
5
5
|
module Transpec
|
6
6
|
module AST
|
7
7
|
class Node < Parser::AST::Node
|
8
|
+
attr_reader :metadata
|
9
|
+
|
8
10
|
def initialize(type, children = [], properties = {})
|
9
|
-
@
|
11
|
+
@metadata = {}
|
12
|
+
@mutable_attributes = {}
|
10
13
|
|
14
|
+
# ::AST::Node#initialize freezes itself.
|
11
15
|
super
|
12
16
|
|
13
17
|
each_child_node do |child_node|
|
@@ -16,11 +20,11 @@ module Transpec
|
|
16
20
|
end
|
17
21
|
|
18
22
|
def parent_node
|
19
|
-
@
|
23
|
+
@mutable_attributes[:parent_node]
|
20
24
|
end
|
21
25
|
|
22
26
|
def parent_node=(node)
|
23
|
-
@
|
27
|
+
@mutable_attributes[:parent_node] = node
|
24
28
|
end
|
25
29
|
|
26
30
|
protected :parent_node=
|
data/lib/transpec/cli.rb
CHANGED
@@ -46,7 +46,7 @@ module Transpec
|
|
46
46
|
runtime_data = nil
|
47
47
|
|
48
48
|
unless @configuration.skip_dynamic_analysis?
|
49
|
-
puts 'Copying project for dynamic analysis...'
|
49
|
+
puts 'Copying the project for dynamic analysis...'
|
50
50
|
DynamicAnalyzer.new(rspec_command: @configuration.rspec_command) do |analyzer|
|
51
51
|
puts "Running dynamic analysis with command \"#{analyzer.rspec_command}\"..."
|
52
52
|
runtime_data = analyzer.analyze(paths)
|
data/lib/transpec/converter.rb
CHANGED
@@ -8,9 +8,11 @@ require 'transpec/syntax'
|
|
8
8
|
require 'transpec/syntax/be_boolean'
|
9
9
|
require 'transpec/syntax/be_close'
|
10
10
|
require 'transpec/syntax/double'
|
11
|
+
require 'transpec/syntax/example'
|
11
12
|
require 'transpec/syntax/expect'
|
12
13
|
require 'transpec/syntax/its'
|
13
14
|
require 'transpec/syntax/method_stub'
|
15
|
+
require 'transpec/syntax/oneliner_should'
|
14
16
|
require 'transpec/syntax/raise_error'
|
15
17
|
require 'transpec/syntax/rspec_configure'
|
16
18
|
require 'transpec/syntax/should'
|
@@ -68,9 +70,31 @@ module Transpec
|
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
73
|
+
def process_oneliner_should(oneliner_should)
|
74
|
+
negative_form = @configuration.negative_form_of_to
|
75
|
+
parenthesize = @configuration.parenthesize_matcher_arg?
|
76
|
+
|
77
|
+
# TODO: Referencing oneliner_should.have_matcher.project_requires_collection_matcher?
|
78
|
+
# from this converter is considered bad design.
|
79
|
+
should_convert_have_items = @configuration.convert_have_items? &&
|
80
|
+
oneliner_should.have_matcher &&
|
81
|
+
!oneliner_should.have_matcher.project_requires_collection_matcher?
|
82
|
+
|
83
|
+
if should_convert_have_items
|
84
|
+
if @configuration.convert_should?
|
85
|
+
oneliner_should.convert_have_items_to_standard_expect!(negative_form, parenthesize)
|
86
|
+
else
|
87
|
+
oneliner_should.convert_have_items_to_standard_should!
|
88
|
+
end
|
89
|
+
elsif @configuration.convert_oneliner? && @rspec_version.one_liner_is_expected_available?
|
90
|
+
oneliner_should.expectize!(negative_form, parenthesize)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
71
94
|
def process_expect(expect)
|
72
95
|
if expect.have_matcher && @configuration.convert_have_items?
|
73
|
-
|
96
|
+
parenthesize_matcher_arg = @configuration.parenthesize_matcher_arg
|
97
|
+
expect.have_matcher.convert_to_standard_expectation!(parenthesize_matcher_arg)
|
74
98
|
end
|
75
99
|
end
|
76
100
|
|
@@ -96,7 +120,7 @@ module Transpec
|
|
96
120
|
|
97
121
|
def process_method_stub(method_stub)
|
98
122
|
if @configuration.convert_stub?
|
99
|
-
method_stub.allowize!(@rspec_version
|
123
|
+
method_stub.allowize!(@rspec_version)
|
100
124
|
elsif @configuration.convert_deprecated_method?
|
101
125
|
method_stub.convert_deprecated_method!
|
102
126
|
end
|
@@ -130,6 +154,11 @@ module Transpec
|
|
130
154
|
its.convert_to_describe_subject_it! if @configuration.convert_its?
|
131
155
|
end
|
132
156
|
|
157
|
+
def process_example(example)
|
158
|
+
return unless @rspec_version.yielded_example_available?
|
159
|
+
example.convert! if @configuration.convert_deprecated_method?
|
160
|
+
end
|
161
|
+
|
133
162
|
def process_rspec_configure(rspec_configure)
|
134
163
|
if need_to_modify_expectation_syntax_configuration?(rspec_configure)
|
135
164
|
rspec_configure.modify_expectation_syntaxes!(:expect)
|
@@ -19,7 +19,6 @@ module Transpec
|
|
19
19
|
RESULT_FILE = 'transpec_analysis_result.json'
|
20
20
|
HELPER_SOURCE = <<-END
|
21
21
|
require 'pathname'
|
22
|
-
require 'json'
|
23
22
|
|
24
23
|
module TranspecAnalysis
|
25
24
|
@base_path = Dir.pwd
|
@@ -44,6 +43,7 @@ module Transpec
|
|
44
43
|
# (Such objects are stored as a string.)
|
45
44
|
# * Singleton method information won't be serialized.
|
46
45
|
# (With Marshal.load, `singleton can't be dumped (TypeError)` will be raised.)
|
46
|
+
require 'json'
|
47
47
|
path = File.join(@base_path, '#{RESULT_FILE}')
|
48
48
|
File.open(path, 'w') do |file|
|
49
49
|
JSON.dump(data, file)
|
@@ -10,6 +10,7 @@ module Transpec
|
|
10
10
|
class OptionParser
|
11
11
|
CONFIG_ATTRS_FOR_KEEP_TYPES = {
|
12
12
|
should: :convert_should=,
|
13
|
+
oneliner: :convert_oneliner=,
|
13
14
|
should_receive: :convert_should_receive=,
|
14
15
|
stub: :convert_stub=,
|
15
16
|
have_items: :convert_have_items=,
|
@@ -140,6 +141,7 @@ module Transpec
|
|
140
141
|
'conversions.',
|
141
142
|
'Available syntax types:',
|
142
143
|
" #{'should'.bright} (to #{'expect(obj).to'.underline})",
|
144
|
+
" #{'oneliner'.bright} (from #{'should'.underline} to #{'is_expected.to'.underline})",
|
143
145
|
" #{'should_receive'.bright} (to #{'expect(obj).to receive'.underline})",
|
144
146
|
" #{'stub'.bright} (to #{'allow(obj).to receive'.underline})",
|
145
147
|
" #{'have_items'.bright} (to #{'expect(obj.size).to eq(x)'.underline})",
|
@@ -154,7 +156,7 @@ module Transpec
|
|
154
156
|
"Default: #{'not_to'.bright}"
|
155
157
|
],
|
156
158
|
'-b' => [
|
157
|
-
"Specify matcher type that #{'be_true'.underline} and",
|
159
|
+
"Specify a matcher type that #{'be_true'.underline} and",
|
158
160
|
"#{'be_false'.underline} will be converted to.",
|
159
161
|
" #{'truthy,falsey'.bright} (conditional semantics)",
|
160
162
|
" #{'truthy,falsy'.bright} (alias of #{'falsey'.underline})",
|
@@ -162,14 +164,15 @@ module Transpec
|
|
162
164
|
"Default: #{'truthy,falsey'.bright}"
|
163
165
|
],
|
164
166
|
'-p' => [
|
165
|
-
'Suppress parenthesizing
|
166
|
-
|
167
|
-
"
|
168
|
-
'parenthesized even if this
|
169
|
-
'specified when parentheses are
|
170
|
-
'keep the meaning of the
|
171
|
-
'By default, arguments of the
|
172
|
-
'operator matchers will be
|
167
|
+
'Suppress parenthesizing arguments of matchers',
|
168
|
+
"when converting #{'should'.underline} with operator matcher",
|
169
|
+
"to #{'expect'.underline} with non-operator matcher. Note",
|
170
|
+
'that it will be parenthesized even if this',
|
171
|
+
'option is specified when parentheses are',
|
172
|
+
'necessary to keep the meaning of the',
|
173
|
+
'expression. By default, arguments of the',
|
174
|
+
'following operator matchers will be',
|
175
|
+
'parenthesized.',
|
173
176
|
" #{'== 10'.underline} to #{'eq(10)'.underline}",
|
174
177
|
" #{'=~ /pattern/'.underline} to #{'match(/pattern/)'.underline}",
|
175
178
|
" #{'=~ [1, 2]'.underline} to #{'match_array([1, 2])'.underline}"
|
data/lib/transpec/project.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
require 'transpec/rspec_version'
|
4
|
+
require 'English'
|
4
5
|
|
5
6
|
module Transpec
|
6
7
|
class Project
|
@@ -20,18 +21,7 @@ module Transpec
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def rspec_version
|
23
|
-
@rspec_version ||=
|
24
|
-
command = 'rspec --version'
|
25
|
-
command = 'bundle exec ' + command if require_bundler?
|
26
|
-
|
27
|
-
version_string = nil
|
28
|
-
|
29
|
-
Dir.chdir(@path) do
|
30
|
-
with_bundler_clean_env { version_string = `#{command}`.chomp }
|
31
|
-
end
|
32
|
-
|
33
|
-
RSpecVersion.new(version_string)
|
34
|
-
end
|
24
|
+
@rspec_version ||= RSpecVersion.new(fetch_rspec_version)
|
35
25
|
end
|
36
26
|
|
37
27
|
def with_bundler_clean_env
|
@@ -45,5 +35,26 @@ module Transpec
|
|
45
35
|
yield
|
46
36
|
end
|
47
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def fetch_rspec_version
|
42
|
+
command = 'rspec --version'
|
43
|
+
command = 'bundle exec ' + command if require_bundler?
|
44
|
+
|
45
|
+
output = nil
|
46
|
+
|
47
|
+
Dir.chdir(@path) do
|
48
|
+
with_bundler_clean_env do
|
49
|
+
IO.popen(command) do |io|
|
50
|
+
output = io.read
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
fail 'Failed checking RSpec version.' if output.nil? || $CHILD_STATUS.exitstatus != 0
|
56
|
+
|
57
|
+
output.chomp
|
58
|
+
end
|
48
59
|
end
|
49
60
|
end
|
@@ -15,8 +15,10 @@ module Transpec
|
|
15
15
|
# Prerelease parts are sorted alphabetically using the normal Ruby string sorting rules.
|
16
16
|
# If a prerelease part contains both letters and numbers, it will be broken into multiple parts
|
17
17
|
# to provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is greater than 1.0.a9).
|
18
|
-
|
19
|
-
|
18
|
+
GEM_VERSION_2_99_0_BETA1 = Gem::Version.new('2.99.0.beta1')
|
19
|
+
GEM_VERSION_2_99_0_BETA2 = Gem::Version.new('2.99.0.beta2')
|
20
|
+
GEM_VERSION_3_0_0_BETA1 = Gem::Version.new('3.0.0.beta1')
|
21
|
+
GEM_VERSION_3_0_0_BETA2 = Gem::Version.new('3.0.0.beta2')
|
20
22
|
|
21
23
|
attr_reader :gem_version
|
22
24
|
|
@@ -29,11 +31,23 @@ module Transpec
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def be_truthy_available?
|
32
|
-
@gem_version >=
|
34
|
+
@gem_version >= GEM_VERSION_2_99_0_BETA1
|
35
|
+
end
|
36
|
+
|
37
|
+
def yielded_example_available?
|
38
|
+
@gem_version >= GEM_VERSION_2_99_0_BETA1
|
39
|
+
end
|
40
|
+
|
41
|
+
def one_liner_is_expected_available?
|
42
|
+
@gem_version >= GEM_VERSION_2_99_0_BETA2
|
33
43
|
end
|
34
44
|
|
35
45
|
def receive_messages_available?
|
36
|
-
@gem_version >=
|
46
|
+
@gem_version >= GEM_VERSION_3_0_0_BETA1
|
47
|
+
end
|
48
|
+
|
49
|
+
def receive_message_chain_available?
|
50
|
+
@gem_version >= GEM_VERSION_3_0_0_BETA2
|
37
51
|
end
|
38
52
|
|
39
53
|
def <=>(other)
|
@@ -8,21 +8,6 @@ module Transpec
|
|
8
8
|
|
9
9
|
SCOPE_TYPES = [:module, :class, :sclass, :def, :defs, :block].freeze
|
10
10
|
|
11
|
-
EXAMPLE_GROUP_METHODS = [
|
12
|
-
:describe, :context,
|
13
|
-
:shared_examples, :shared_context, :share_examples_for, :shared_examples_for
|
14
|
-
].freeze
|
15
|
-
|
16
|
-
EXAMPLE_METHODS = [
|
17
|
-
:example, :it, :specify,
|
18
|
-
:focus, :focused, :fit,
|
19
|
-
:pending, :xexample, :xit, :xspecify
|
20
|
-
].freeze
|
21
|
-
|
22
|
-
HOOK_METHODS = [:before, :after, :around].freeze
|
23
|
-
|
24
|
-
HELPER_METHODS = [:subject, :subject!, :let, :let!]
|
25
|
-
|
26
11
|
NON_MONKEY_PATCH_EXPECTATION_AVAILABLE_CONTEXT = [
|
27
12
|
[:example_group, :example],
|
28
13
|
[:example_group, :each_before_after],
|
@@ -0,0 +1,83 @@
|
|
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 Example < Syntax
|
10
|
+
include Mixin::Send, Util
|
11
|
+
|
12
|
+
METHODS_YIELD_EXAMPLE = (EXAMPLE_METHODS + HOOK_METHODS + HELPER_METHODS).freeze
|
13
|
+
|
14
|
+
def self.target_method?(receiver_node, method_name)
|
15
|
+
receiver_node.nil? && [:example, :running_example].include?(method_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def convert!
|
19
|
+
if block_node
|
20
|
+
insert_after(block_node.loc.begin, " |#{block_arg_name}|") unless block_has_argument?
|
21
|
+
replace(selector_range, block_arg_name.to_s) unless method_name == block_arg_name
|
22
|
+
block_node.metadata[:added_example_block_arg] = true
|
23
|
+
else
|
24
|
+
replace(selector_range, 'RSpec.current_example')
|
25
|
+
end
|
26
|
+
|
27
|
+
register_record
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def block_has_argument?
|
33
|
+
block_arg_node || block_node.metadata[:added_example_block_arg]
|
34
|
+
end
|
35
|
+
|
36
|
+
def block_node
|
37
|
+
return @block_node if instance_variable_defined?(:@block_node)
|
38
|
+
|
39
|
+
@block_node ||= @node.each_ancestor_node.find do |ancestor_node|
|
40
|
+
next false unless ancestor_node.type == :block
|
41
|
+
method_name = method_name_of_block_node(ancestor_node)
|
42
|
+
METHODS_YIELD_EXAMPLE.include?(method_name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def block_method_name
|
47
|
+
method_name_of_block_node(block_node)
|
48
|
+
end
|
49
|
+
|
50
|
+
def block_arg_node
|
51
|
+
args_node = block_node.children[1]
|
52
|
+
args_node.children.first
|
53
|
+
end
|
54
|
+
|
55
|
+
def block_arg_name
|
56
|
+
if block_arg_node
|
57
|
+
block_arg_node.children.first
|
58
|
+
else
|
59
|
+
:example
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_name_of_block_node(block_node)
|
64
|
+
send_node = block_node.children.first
|
65
|
+
send_node.children[1]
|
66
|
+
end
|
67
|
+
|
68
|
+
def register_record
|
69
|
+
if block_node
|
70
|
+
prefix = "#{block_method_name}"
|
71
|
+
prefix << '(:name)' if HELPER_METHODS.include?(block_method_name)
|
72
|
+
original_syntax = "#{prefix} { example }"
|
73
|
+
converted_syntax = "#{prefix} { |example| example }"
|
74
|
+
else
|
75
|
+
original_syntax = 'def helper_method example; end'
|
76
|
+
converted_syntax = 'def helper_method RSpec.current_example; end'
|
77
|
+
end
|
78
|
+
|
79
|
+
@report.records << Record.new(original_syntax, converted_syntax)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/transpec/syntax/have.rb
CHANGED
@@ -44,9 +44,13 @@ module Transpec
|
|
44
44
|
|
45
45
|
def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
|
46
46
|
return if project_requires_collection_matcher?
|
47
|
-
replace(@expectation.subject_range, replacement_subject_source)
|
48
|
-
replace(expression_range, replacement_matcher_source(
|
49
|
-
register_record
|
47
|
+
replace(@expectation.subject_range, replacement_subject_source) if explicit_subject?
|
48
|
+
replace(expression_range, replacement_matcher_source(parenthesize_matcher_arg))
|
49
|
+
register_record if explicit_subject?
|
50
|
+
end
|
51
|
+
|
52
|
+
def explicit_subject?
|
53
|
+
@expectation.respond_to?(:subject_node)
|
50
54
|
end
|
51
55
|
|
52
56
|
def have_node
|
@@ -105,24 +109,12 @@ module Transpec
|
|
105
109
|
QUERY_METHOD_PRIORITIES.first
|
106
110
|
end
|
107
111
|
|
108
|
-
def
|
109
|
-
|
110
|
-
when :should
|
111
|
-
replacement_matcher_source_for_should(size_source)
|
112
|
-
when :expect
|
113
|
-
replacement_matcher_source_for_expect(size_source, parenthesize_arg)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def runtime_subject_data
|
120
|
-
return @runtime_subject_data if instance_variable_defined?(:@runtime_subject_data)
|
121
|
-
@runtime_subject_data = runtime_node_data(@expectation.subject_node)
|
112
|
+
def replacement_subject_source
|
113
|
+
build_replacement_subject_source(@expectation.subject_range.source)
|
122
114
|
end
|
123
115
|
|
124
|
-
def
|
125
|
-
source =
|
116
|
+
def build_replacement_subject_source(original_subject_source)
|
117
|
+
source = original_subject_source.dup
|
126
118
|
if subject_is_owner_of_collection?
|
127
119
|
if collection_accessor_is_private?
|
128
120
|
source << ".send(#{collection_accessor.inspect}"
|
@@ -135,7 +127,33 @@ module Transpec
|
|
135
127
|
source << ".#{query_method}"
|
136
128
|
end
|
137
129
|
|
138
|
-
def
|
130
|
+
def replacement_matcher_source(parenthesize_arg)
|
131
|
+
size_source = size_node.loc.expression.source
|
132
|
+
build_replacement_matcher_source(size_source, parenthesize_arg)
|
133
|
+
end
|
134
|
+
|
135
|
+
def build_replacement_matcher_source(size_source, parenthesize_arg = true)
|
136
|
+
case @expectation.current_syntax_type
|
137
|
+
when :should
|
138
|
+
build_replacement_matcher_source_for_should(size_source)
|
139
|
+
when :expect
|
140
|
+
build_replacement_matcher_source_for_expect(size_source, parenthesize_arg)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def size_source
|
145
|
+
size_node.loc.expression.source
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def runtime_subject_data
|
151
|
+
return @runtime_subject_data if instance_variable_defined?(:@runtime_subject_data)
|
152
|
+
node = explicit_subject? ? @expectation.subject_node : @expectation.node
|
153
|
+
@runtime_subject_data = runtime_node_data(node)
|
154
|
+
end
|
155
|
+
|
156
|
+
def build_replacement_matcher_source_for_should(size_source)
|
139
157
|
case have_method_name
|
140
158
|
when :have, :have_exactly then "== #{size_source}"
|
141
159
|
when :have_at_least then ">= #{size_source}"
|
@@ -143,7 +161,7 @@ module Transpec
|
|
143
161
|
end
|
144
162
|
end
|
145
163
|
|
146
|
-
def
|
164
|
+
def build_replacement_matcher_source_for_expect(size_source, parenthesize_arg)
|
147
165
|
case have_method_name
|
148
166
|
when :have, :have_exactly
|
149
167
|
if parenthesize_arg
|
@@ -158,10 +176,6 @@ module Transpec
|
|
158
176
|
end
|
159
177
|
end
|
160
178
|
|
161
|
-
def size_source
|
162
|
-
size_node.loc.expression.source
|
163
|
-
end
|
164
|
-
|
165
179
|
def register_record
|
166
180
|
@report.records << HaveRecord.new(self)
|
167
181
|
end
|
@@ -177,33 +191,47 @@ module Transpec
|
|
177
191
|
end
|
178
192
|
|
179
193
|
def target_node
|
180
|
-
@have.
|
194
|
+
if @have.explicit_subject?
|
195
|
+
@have.expectation.subject_node
|
196
|
+
else
|
197
|
+
@have.expectation.node
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def target_type
|
202
|
+
if @have.explicit_subject?
|
203
|
+
:object
|
204
|
+
else
|
205
|
+
:context
|
206
|
+
end
|
181
207
|
end
|
182
208
|
|
183
209
|
def register_request
|
184
210
|
key = :collection_accessor
|
185
211
|
code = collection_accessor_inspection_code
|
186
|
-
@rewriter.register_request(target_node, key, code)
|
212
|
+
@rewriter.register_request(target_node, key, code, target_type)
|
187
213
|
|
188
214
|
# Give up inspecting query methods of collection accessor with arguments
|
189
215
|
# (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
|
190
216
|
unless @have.items_method_has_arguments?
|
191
217
|
key = :available_query_methods
|
192
|
-
code =
|
193
|
-
|
194
|
-
"target.methods & #{QUERY_METHOD_PRIORITIES.inspect}"
|
195
|
-
@rewriter.register_request(target_node, key, code)
|
218
|
+
code = available_query_methods_inspection_code
|
219
|
+
@rewriter.register_request(target_node, key, code, target_type)
|
196
220
|
end
|
197
221
|
|
198
222
|
key = :collection_accessor_is_private?
|
199
|
-
code = "private_methods.include?(#{@have.items_name.inspect})"
|
200
|
-
@rewriter.register_request(target_node, key, code)
|
223
|
+
code = "#{subject_code}.private_methods.include?(#{@have.items_name.inspect})"
|
224
|
+
@rewriter.register_request(target_node, key, code, target_type)
|
201
225
|
|
202
226
|
key = :project_requires_collection_matcher?
|
203
227
|
code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
|
204
228
|
@rewriter.register_request(target_node, key, code, :context)
|
205
229
|
end
|
206
230
|
|
231
|
+
def subject_code
|
232
|
+
@have.explicit_subject? ? 'self' : 'subject'
|
233
|
+
end
|
234
|
+
|
207
235
|
# rubocop:disable MethodLength
|
208
236
|
def collection_accessor_inspection_code
|
209
237
|
# `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
|
@@ -212,7 +240,7 @@ module Transpec
|
|
212
240
|
# rubocop:disable LineLength
|
213
241
|
# https://github.com/rspec/rspec-expectations/blob/v2.14.3/lib/rspec/matchers/built_in/have.rb#L48-L58
|
214
242
|
# rubocop:enable LineLength
|
215
|
-
<<-END.gsub(/^\s+\|/, '').chomp
|
243
|
+
@collection_accessor_inspection_code ||= <<-END.gsub(/^\s+\|/, '').chomp
|
216
244
|
|begin
|
217
245
|
| exact_name = #{@have.items_name.inspect}
|
218
246
|
|
|
@@ -227,12 +255,13 @@ module Transpec
|
|
227
255
|
|
|
228
256
|
| if inflector
|
229
257
|
| pluralized_name = inflector.pluralize(exact_name).to_sym
|
230
|
-
| respond_to_pluralized_name = respond_to?(pluralized_name)
|
258
|
+
| respond_to_pluralized_name = #{subject_code}.respond_to?(pluralized_name)
|
231
259
|
| end
|
232
260
|
|
|
233
|
-
| respond_to_query_methods =
|
261
|
+
| respond_to_query_methods =
|
262
|
+
| !(#{subject_code}.methods & #{QUERY_METHOD_PRIORITIES.inspect}).empty?
|
234
263
|
|
|
235
|
-
| if respond_to?(exact_name)
|
264
|
+
| if #{subject_code}.respond_to?(exact_name)
|
236
265
|
| exact_name
|
237
266
|
| elsif respond_to_pluralized_name
|
238
267
|
| pluralized_name
|
@@ -245,6 +274,18 @@ module Transpec
|
|
245
274
|
END
|
246
275
|
end
|
247
276
|
# rubocop:enable MethodLength
|
277
|
+
|
278
|
+
def available_query_methods_inspection_code
|
279
|
+
<<-END.gsub(/^\s+\|/, '').chomp
|
280
|
+
|collection_accessor = #{collection_accessor_inspection_code}
|
281
|
+
|target = if collection_accessor
|
282
|
+
| #{subject_code}.__send__(collection_accessor)
|
283
|
+
| else
|
284
|
+
| #{subject_code}
|
285
|
+
| end
|
286
|
+
|target.methods & #{QUERY_METHOD_PRIORITIES.inspect}
|
287
|
+
END
|
288
|
+
end
|
248
289
|
end
|
249
290
|
|
250
291
|
class HaveRecord < Record
|
@@ -254,28 +295,35 @@ module Transpec
|
|
254
295
|
|
255
296
|
def original_syntax
|
256
297
|
@original_syntax ||= begin
|
257
|
-
|
258
|
-
|
259
|
-
"#{original_subject}.should"
|
260
|
-
when Expect
|
261
|
-
"expect(#{original_subject}).to"
|
262
|
-
end
|
263
|
-
|
298
|
+
type = @have.expectation.class.snake_case_name.to_sym
|
299
|
+
syntax = build_expectation(original_subject, type)
|
264
300
|
syntax << " #{@have.have_method_name}(n).#{original_items}"
|
265
301
|
end
|
266
302
|
end
|
267
303
|
|
268
304
|
def converted_syntax
|
269
305
|
@converted_syntax ||= begin
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
306
|
+
type = @have.expectation.current_syntax_type
|
307
|
+
syntax = build_expectation(converted_subject, type)
|
308
|
+
syntax << " #{@have.build_replacement_matcher_source('n')}"
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def build_expectation(subject, type)
|
313
|
+
case type
|
314
|
+
when :should
|
315
|
+
syntax = "#{subject}.should"
|
316
|
+
syntax << '_not' unless positive?
|
317
|
+
when :expect
|
318
|
+
syntax = "expect(#{subject})."
|
319
|
+
syntax << (positive? ? 'to' : 'not_to')
|
278
320
|
end
|
321
|
+
|
322
|
+
syntax
|
323
|
+
end
|
324
|
+
|
325
|
+
def positive?
|
326
|
+
@have.expectation.positive?
|
279
327
|
end
|
280
328
|
|
281
329
|
def original_subject
|
@@ -300,7 +348,16 @@ module Transpec
|
|
300
348
|
|
301
349
|
def converted_subject
|
302
350
|
if @have.subject_is_owner_of_collection?
|
303
|
-
|
351
|
+
build_converted_subject('obj')
|
352
|
+
else
|
353
|
+
build_converted_subject('collection')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def build_converted_subject(subject)
|
358
|
+
subject << '.'
|
359
|
+
|
360
|
+
if @have.subject_is_owner_of_collection?
|
304
361
|
if @have.collection_accessor_is_private?
|
305
362
|
subject << "send(#{@have.collection_accessor.inspect}"
|
306
363
|
subject << ', ...' if @have.items_method_has_arguments?
|
@@ -311,7 +368,7 @@ module Transpec
|
|
311
368
|
end
|
312
369
|
subject << ".#{@have.query_method}"
|
313
370
|
else
|
314
|
-
"
|
371
|
+
subject << "#{@have.default_query_method}"
|
315
372
|
end
|
316
373
|
end
|
317
374
|
end
|