transpec 1.3.1 → 1.4.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.
- 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
|