rubocop-rspec 2.16.0 → 2.24.1
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/CHANGELOG.md +124 -9
- data/README.md +3 -3
- data/config/default.yml +145 -18
- data/config/obsoletion.yml +15 -0
- data/lib/rubocop/cop/rspec/be_empty.rb +44 -0
- data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +29 -115
- data/lib/rubocop/cop/rspec/capybara/match_style.rb +38 -0
- data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +23 -96
- data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +19 -75
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +14 -83
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +25 -69
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +26 -63
- data/lib/rubocop/cop/rspec/change_by_zero.rb +33 -23
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_method.rb +5 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +13 -6
- data/lib/rubocop/cop/rspec/describe_method.rb +16 -8
- data/lib/rubocop/cop/rspec/described_class.rb +2 -1
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +7 -5
- data/lib/rubocop/cop/rspec/dialect.rb +1 -1
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_example_group.rb +10 -7
- data/lib/rubocop/cop/rspec/empty_hook.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
- data/lib/rubocop/cop/rspec/eq.rb +47 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
- data/lib/rubocop/cop/rspec/expect_actual.rb +4 -4
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +25 -118
- data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +40 -107
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +30 -250
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +19 -46
- data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +23 -64
- data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +45 -79
- data/lib/rubocop/cop/rspec/file_path.rb +8 -2
- data/lib/rubocop/cop/rspec/focus.rb +19 -5
- data/lib/rubocop/cop/rspec/hook_argument.rb +12 -9
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +5 -3
- data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +8 -4
- data/lib/rubocop/cop/rspec/let_setup.rb +6 -8
- data/lib/rubocop/cop/rspec/match_array.rb +59 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +197 -0
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +1 -2
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +20 -4
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +7 -5
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +2 -5
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +3 -1
- data/lib/rubocop/cop/rspec/pending.rb +23 -13
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +72 -36
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +49 -40
- data/lib/rubocop/cop/rspec/rails/have_http_status.rb +11 -6
- data/lib/rubocop/cop/rspec/rails/http_status.rb +107 -34
- data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +4 -4
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +60 -0
- data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
- data/lib/rubocop/cop/rspec/rails/travel_around.rb +92 -0
- data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
- data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -4
- data/lib/rubocop/cop/rspec/scattered_setup.rb +23 -6
- data/lib/rubocop/cop/rspec/shared_context.rb +12 -13
- data/lib/rubocop/cop/rspec/shared_examples.rb +6 -4
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
- data/lib/rubocop/cop/rspec/sort_metadata.rb +4 -3
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
- data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +0 -1
- data/lib/rubocop/cop/rspec/variable_definition.rb +5 -2
- data/lib/rubocop/cop/rspec/variable_name.rb +4 -1
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +7 -7
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
- data/lib/rubocop/cop/rspec_cops.rb +16 -0
- data/lib/rubocop/rspec/config_formatter.rb +16 -0
- data/lib/rubocop/rspec/example_group.rb +6 -8
- data/lib/rubocop/rspec/language/node_pattern.rb +26 -0
- data/lib/rubocop/rspec/language.rb +25 -16
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +4 -5
- metadata +50 -8
- data/lib/rubocop/cop/rspec/mixin/capybara_help.rb +0 -80
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +0 -146
- data/lib/rubocop/rspec/factory_bot/language.rb +0 -37
- data/lib/rubocop/rspec/factory_bot.rb +0 -64
@@ -74,7 +74,7 @@ module RuboCop
|
|
74
74
|
name[0..-2]
|
75
75
|
when 'exist?', 'exists?'
|
76
76
|
'exist'
|
77
|
-
when
|
77
|
+
when /\Ahas_/
|
78
78
|
name.sub('has_', 'have_')[0..-2]
|
79
79
|
else
|
80
80
|
"be_#{name[0..-2]}"
|
@@ -84,22 +84,22 @@ module RuboCop
|
|
84
84
|
|
85
85
|
def remove_predicate(corrector, predicate)
|
86
86
|
range = predicate.loc.dot.with(
|
87
|
-
end_pos: predicate.
|
87
|
+
end_pos: predicate.source_range.end_pos
|
88
88
|
)
|
89
89
|
|
90
90
|
corrector.remove(range)
|
91
91
|
|
92
|
-
block_range =
|
92
|
+
block_range = LocationHelp.block_with_whitespace(predicate)
|
93
93
|
corrector.remove(block_range) if block_range
|
94
94
|
end
|
95
95
|
|
96
96
|
def rewrite_matcher(corrector, predicate, matcher)
|
97
|
-
args =
|
98
|
-
block_loc =
|
97
|
+
args = LocationHelp.arguments_with_whitespace(predicate).source
|
98
|
+
block_loc = LocationHelp.block_with_whitespace(predicate)
|
99
99
|
block = block_loc ? block_loc.source : ''
|
100
100
|
|
101
101
|
corrector.replace(
|
102
|
-
matcher
|
102
|
+
matcher,
|
103
103
|
to_predicate_matcher(predicate.method_name) + args + block
|
104
104
|
)
|
105
105
|
end
|
@@ -118,7 +118,7 @@ module RuboCop
|
|
118
118
|
end
|
119
119
|
|
120
120
|
# A helper for `explicit` style
|
121
|
-
module ExplicitHelper
|
121
|
+
module ExplicitHelper # rubocop:disable Metrics/ModuleLength
|
122
122
|
include RuboCop::RSpec::Language
|
123
123
|
extend NodePattern::Macros
|
124
124
|
|
@@ -149,12 +149,35 @@ module RuboCop
|
|
149
149
|
return if part_of_ignored_node?(node)
|
150
150
|
|
151
151
|
predicate_matcher?(node) do |actual, matcher|
|
152
|
+
next unless replaceable_matcher?(matcher)
|
153
|
+
|
152
154
|
add_offense(node, message: message_explicit(matcher)) do |corrector|
|
155
|
+
next if uncorrectable_matcher?(node, matcher)
|
156
|
+
|
153
157
|
corrector_explicit(corrector, node, actual, matcher, matcher)
|
154
158
|
end
|
155
159
|
end
|
156
160
|
end
|
157
161
|
|
162
|
+
def replaceable_matcher?(matcher)
|
163
|
+
case matcher.method_name.to_s
|
164
|
+
when 'include'
|
165
|
+
matcher.arguments.one?
|
166
|
+
else
|
167
|
+
true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def uncorrectable_matcher?(node, matcher)
|
172
|
+
heredoc_argument?(matcher) && !same_line?(node, matcher)
|
173
|
+
end
|
174
|
+
|
175
|
+
def heredoc_argument?(matcher)
|
176
|
+
matcher.arguments.select do |arg|
|
177
|
+
%i[str dstr xstr].include?(arg.type)
|
178
|
+
end.any?(&:heredoc?)
|
179
|
+
end
|
180
|
+
|
158
181
|
# @!method predicate_matcher?(node)
|
159
182
|
def_node_matcher :predicate_matcher?, <<-PATTERN
|
160
183
|
(send
|
@@ -179,7 +202,8 @@ module RuboCop
|
|
179
202
|
|
180
203
|
return false if allowed_explicit_matchers.include?(name)
|
181
204
|
|
182
|
-
name.start_with?('be_', 'have_') && !name.end_with?('?')
|
205
|
+
name.start_with?('be_', 'have_') && !name.end_with?('?') ||
|
206
|
+
%w[include respond_to].include?(name)
|
183
207
|
end
|
184
208
|
|
185
209
|
def message_explicit(matcher)
|
@@ -190,20 +214,19 @@ module RuboCop
|
|
190
214
|
|
191
215
|
def corrector_explicit(corrector, to_node, actual, matcher, block_child)
|
192
216
|
replacement_matcher = replacement_matcher(to_node)
|
193
|
-
corrector.replace(matcher
|
217
|
+
corrector.replace(matcher, replacement_matcher)
|
194
218
|
move_predicate(corrector, actual, matcher, block_child)
|
195
219
|
corrector.replace(to_node.loc.selector, 'to')
|
196
220
|
end
|
197
221
|
|
198
222
|
def move_predicate(corrector, actual, matcher, block_child)
|
199
223
|
predicate = to_predicate_method(matcher.method_name)
|
200
|
-
args =
|
201
|
-
block_loc =
|
224
|
+
args = LocationHelp.arguments_with_whitespace(matcher).source
|
225
|
+
block_loc = LocationHelp.block_with_whitespace(block_child)
|
202
226
|
block = block_loc ? block_loc.source : ''
|
203
227
|
|
204
228
|
corrector.remove(block_loc) if block_loc
|
205
|
-
corrector.insert_after(actual
|
206
|
-
".#{predicate}" + args + block)
|
229
|
+
corrector.insert_after(actual, ".#{predicate}" + args + block)
|
207
230
|
end
|
208
231
|
|
209
232
|
# rubocop:disable Metrics/MethodLength
|
@@ -217,10 +240,10 @@ module RuboCop
|
|
217
240
|
'include?'
|
218
241
|
when 'respond_to'
|
219
242
|
'respond_to?'
|
220
|
-
when
|
243
|
+
when /\Ahave_(.+)/
|
221
244
|
"has_#{Regexp.last_match(1)}?"
|
222
245
|
else
|
223
|
-
"#{matcher[
|
246
|
+
"#{matcher[/\Abe_(.+)/, 1]}?"
|
224
247
|
end
|
225
248
|
end
|
226
249
|
# rubocop:enable Metrics/MethodLength
|
@@ -270,6 +293,17 @@ module RuboCop
|
|
270
293
|
# # good - the above code is rewritten to it by this cop
|
271
294
|
# expect(foo.something?).to be(true)
|
272
295
|
#
|
296
|
+
# # bad - no autocorrect
|
297
|
+
# expect(foo)
|
298
|
+
# .to be_something(<<~TEXT)
|
299
|
+
# bar
|
300
|
+
# TEXT
|
301
|
+
#
|
302
|
+
# # good
|
303
|
+
# expect(foo.something?(<<~TEXT)).to be(true)
|
304
|
+
# bar
|
305
|
+
# TEXT
|
306
|
+
#
|
273
307
|
# @example Strict: false, EnforcedStyle: explicit
|
274
308
|
# # bad
|
275
309
|
# expect(foo).to be_something
|
@@ -297,31 +331,6 @@ module RuboCop
|
|
297
331
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
298
332
|
check_explicit(node) if style == :explicit
|
299
333
|
end
|
300
|
-
|
301
|
-
private
|
302
|
-
|
303
|
-
# returns args location with whitespace
|
304
|
-
# @example
|
305
|
-
# foo 1, 2
|
306
|
-
# ^^^^^
|
307
|
-
def args_loc(send_node)
|
308
|
-
send_node.loc.selector.end.with(
|
309
|
-
end_pos: send_node.loc.expression.end_pos
|
310
|
-
)
|
311
|
-
end
|
312
|
-
|
313
|
-
# returns block location with whitespace
|
314
|
-
# @example
|
315
|
-
# foo { bar }
|
316
|
-
# ^^^^^^^^
|
317
|
-
def block_loc(send_node)
|
318
|
-
parent = send_node.parent
|
319
|
-
return unless parent.block_type?
|
320
|
-
|
321
|
-
send_node.loc.expression.end.with(
|
322
|
-
end_pos: parent.loc.expression.end_pos
|
323
|
-
)
|
324
|
-
end
|
325
334
|
end
|
326
335
|
end
|
327
336
|
end
|
@@ -9,6 +9,7 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# expect(response.status).to be(200)
|
12
|
+
# expect(response.code).to eq("200")
|
12
13
|
#
|
13
14
|
# # good
|
14
15
|
# expect(response).to have_http_status(200)
|
@@ -17,8 +18,8 @@ module RuboCop
|
|
17
18
|
extend AutoCorrector
|
18
19
|
|
19
20
|
MSG =
|
20
|
-
'Prefer `expect(response).%<to>s have_http_status(%<status>
|
21
|
-
'over
|
21
|
+
'Prefer `expect(response).%<to>s have_http_status(%<status>s)` ' \
|
22
|
+
'over `%<bad_code>s`.'
|
22
23
|
|
23
24
|
RUNNERS = %i[to to_not not_to].to_set
|
24
25
|
RESTRICT_ON_SEND = RUNNERS
|
@@ -27,19 +28,23 @@ module RuboCop
|
|
27
28
|
def_node_matcher :match_status, <<-PATTERN
|
28
29
|
(send
|
29
30
|
(send nil? :expect
|
30
|
-
$(send (send nil? :response) :status)
|
31
|
+
$(send (send nil? :response) {:status :code})
|
31
32
|
)
|
32
33
|
$RUNNERS
|
33
|
-
$(send nil? {:be :eq :eql :equal} (int $_))
|
34
|
+
$(send nil? {:be :eq :eql :equal} ({int str} $_))
|
34
35
|
)
|
35
36
|
PATTERN
|
36
37
|
|
37
38
|
def on_send(node)
|
38
39
|
match_status(node) do |response_status, to, match, status|
|
39
|
-
|
40
|
+
return unless status.to_s.match?(/\A\d+\z/)
|
41
|
+
|
42
|
+
message = format(MSG, to: to, status: status,
|
43
|
+
bad_code: node.source)
|
40
44
|
add_offense(node, message: message) do |corrector|
|
41
|
-
corrector.replace(response_status
|
45
|
+
corrector.replace(response_status, 'response')
|
42
46
|
corrector.replace(match.loc.selector, 'have_http_status')
|
47
|
+
corrector.replace(match.first_argument, status.to_s)
|
43
48
|
end
|
44
49
|
end
|
45
50
|
end
|
@@ -8,14 +8,21 @@ module RuboCop
|
|
8
8
|
module Rails
|
9
9
|
# Enforces use of symbolic or numeric value to describe HTTP status.
|
10
10
|
#
|
11
|
+
# This cop inspects only `have_http_status` calls.
|
12
|
+
# So, this cop does not check if a method starting with `be_*` is used
|
13
|
+
# when setting for `EnforcedStyle: symbolic` or
|
14
|
+
# `EnforcedStyle: numeric`.
|
15
|
+
#
|
11
16
|
# @example `EnforcedStyle: symbolic` (default)
|
12
17
|
# # bad
|
13
18
|
# it { is_expected.to have_http_status 200 }
|
14
19
|
# it { is_expected.to have_http_status 404 }
|
20
|
+
# it { is_expected.to have_http_status "403" }
|
15
21
|
#
|
16
22
|
# # good
|
17
23
|
# it { is_expected.to have_http_status :ok }
|
18
24
|
# it { is_expected.to have_http_status :not_found }
|
25
|
+
# it { is_expected.to have_http_status :forbidden }
|
19
26
|
# it { is_expected.to have_http_status :success }
|
20
27
|
# it { is_expected.to have_http_status :error }
|
21
28
|
#
|
@@ -23,10 +30,27 @@ module RuboCop
|
|
23
30
|
# # bad
|
24
31
|
# it { is_expected.to have_http_status :ok }
|
25
32
|
# it { is_expected.to have_http_status :not_found }
|
33
|
+
# it { is_expected.to have_http_status "forbidden" }
|
26
34
|
#
|
27
35
|
# # good
|
28
36
|
# it { is_expected.to have_http_status 200 }
|
29
37
|
# it { is_expected.to have_http_status 404 }
|
38
|
+
# it { is_expected.to have_http_status 403 }
|
39
|
+
# it { is_expected.to have_http_status :success }
|
40
|
+
# it { is_expected.to have_http_status :error }
|
41
|
+
#
|
42
|
+
# @example `EnforcedStyle: be_status`
|
43
|
+
# # bad
|
44
|
+
# it { is_expected.to have_http_status :ok }
|
45
|
+
# it { is_expected.to have_http_status :not_found }
|
46
|
+
# it { is_expected.to have_http_status "forbidden" }
|
47
|
+
# it { is_expected.to have_http_status 200 }
|
48
|
+
# it { is_expected.to have_http_status 404 }
|
49
|
+
# it { is_expected.to have_http_status "403" }
|
50
|
+
#
|
51
|
+
# # good
|
52
|
+
# it { is_expected.to be_ok }
|
53
|
+
# it { is_expected.to be_not_found }
|
30
54
|
# it { is_expected.to have_http_status :success }
|
31
55
|
# it { is_expected.to have_http_status :error }
|
32
56
|
#
|
@@ -37,16 +61,17 @@ module RuboCop
|
|
37
61
|
|
38
62
|
# @!method http_status(node)
|
39
63
|
def_node_matcher :http_status, <<-PATTERN
|
40
|
-
(send nil? :have_http_status ${int sym})
|
64
|
+
(send nil? :have_http_status ${int sym str})
|
41
65
|
PATTERN
|
42
66
|
|
43
67
|
def on_send(node)
|
44
|
-
http_status(node) do |
|
45
|
-
checker = checker_class.new(
|
68
|
+
http_status(node) do |arg|
|
69
|
+
checker = checker_class.new(arg)
|
46
70
|
return unless checker.offensive?
|
47
71
|
|
48
|
-
add_offense(checker.
|
49
|
-
|
72
|
+
add_offense(checker.offense_range,
|
73
|
+
message: checker.message) do |corrector|
|
74
|
+
corrector.replace(checker.offense_range, checker.prefer)
|
50
75
|
end
|
51
76
|
end
|
52
77
|
end
|
@@ -59,13 +84,16 @@ module RuboCop
|
|
59
84
|
SymbolicStyleChecker
|
60
85
|
when :numeric
|
61
86
|
NumericStyleChecker
|
87
|
+
when :be_status
|
88
|
+
BeStatusStyleChecker
|
62
89
|
end
|
63
90
|
end
|
64
91
|
|
65
92
|
# :nodoc:
|
66
|
-
class
|
93
|
+
class StyleCheckerBase
|
67
94
|
MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
|
68
95
|
'to describe HTTP status code.'
|
96
|
+
ALLOWED_STATUSES = %i[error success missing redirect].freeze
|
69
97
|
|
70
98
|
attr_reader :node
|
71
99
|
|
@@ -73,16 +101,36 @@ module RuboCop
|
|
73
101
|
@node = node
|
74
102
|
end
|
75
103
|
|
104
|
+
def message
|
105
|
+
format(MSG, prefer: prefer, current: current)
|
106
|
+
end
|
107
|
+
|
108
|
+
def offense_range
|
109
|
+
node
|
110
|
+
end
|
111
|
+
|
112
|
+
def allowed_symbol?
|
113
|
+
node.sym_type? && ALLOWED_STATUSES.include?(node.value)
|
114
|
+
end
|
115
|
+
|
116
|
+
def custom_http_status_code?
|
117
|
+
node.int_type? &&
|
118
|
+
!::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# :nodoc:
|
123
|
+
class SymbolicStyleChecker < StyleCheckerBase
|
76
124
|
def offensive?
|
77
125
|
!node.sym_type? && !custom_http_status_code?
|
78
126
|
end
|
79
127
|
|
80
|
-
def
|
81
|
-
|
128
|
+
def prefer
|
129
|
+
symbol.inspect
|
82
130
|
end
|
83
131
|
|
84
|
-
def
|
85
|
-
|
132
|
+
def current
|
133
|
+
node.value.inspect
|
86
134
|
end
|
87
135
|
|
88
136
|
private
|
@@ -92,52 +140,77 @@ module RuboCop
|
|
92
140
|
end
|
93
141
|
|
94
142
|
def number
|
95
|
-
node.source.to_i
|
96
|
-
end
|
97
|
-
|
98
|
-
def custom_http_status_code?
|
99
|
-
node.int_type? &&
|
100
|
-
!::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
|
143
|
+
node.source.delete('"').to_i
|
101
144
|
end
|
102
145
|
end
|
103
146
|
|
104
147
|
# :nodoc:
|
105
|
-
class NumericStyleChecker
|
106
|
-
|
107
|
-
|
148
|
+
class NumericStyleChecker < StyleCheckerBase
|
149
|
+
def offensive?
|
150
|
+
!node.int_type? && !allowed_symbol?
|
151
|
+
end
|
108
152
|
|
109
|
-
|
153
|
+
def prefer
|
154
|
+
number.to_s
|
155
|
+
end
|
110
156
|
|
111
|
-
|
157
|
+
def current
|
158
|
+
symbol.inspect
|
159
|
+
end
|
112
160
|
|
113
|
-
|
114
|
-
|
161
|
+
private
|
162
|
+
|
163
|
+
def symbol
|
164
|
+
node.value
|
115
165
|
end
|
116
166
|
|
167
|
+
def number
|
168
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol.to_sym]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# :nodoc:
|
173
|
+
class BeStatusStyleChecker < StyleCheckerBase
|
117
174
|
def offensive?
|
118
|
-
!node.
|
175
|
+
(!node.sym_type? && !custom_http_status_code?) ||
|
176
|
+
(!node.int_type? && !allowed_symbol?)
|
119
177
|
end
|
120
178
|
|
121
|
-
def
|
122
|
-
|
179
|
+
def offense_range
|
180
|
+
node.parent
|
123
181
|
end
|
124
182
|
|
125
|
-
def
|
126
|
-
|
183
|
+
def prefer
|
184
|
+
if node.sym_type?
|
185
|
+
"be_#{node.value}"
|
186
|
+
elsif node.int_type?
|
187
|
+
"be_#{symbol}"
|
188
|
+
elsif node.str_type?
|
189
|
+
"be_#{normalize_str}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def current
|
194
|
+
offense_range.source
|
127
195
|
end
|
128
196
|
|
129
197
|
private
|
130
198
|
|
131
|
-
def
|
132
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE
|
199
|
+
def symbol
|
200
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
133
201
|
end
|
134
202
|
|
135
|
-
def
|
136
|
-
node.
|
203
|
+
def number
|
204
|
+
node.source.to_i
|
137
205
|
end
|
138
206
|
|
139
|
-
def
|
140
|
-
|
207
|
+
def normalize_str
|
208
|
+
normalized = node.source.delete('"')
|
209
|
+
if normalized.match?(/\A\d+\z/)
|
210
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(normalized.to_i)
|
211
|
+
else
|
212
|
+
normalized
|
213
|
+
end
|
141
214
|
end
|
142
215
|
end
|
143
216
|
end
|
@@ -96,12 +96,12 @@ module RuboCop
|
|
96
96
|
# @return [Parser::Source::Range]
|
97
97
|
def remove_range(node)
|
98
98
|
if node.left_sibling
|
99
|
-
node.
|
100
|
-
begin_pos: node.left_sibling.
|
99
|
+
node.source_range.with(
|
100
|
+
begin_pos: node.left_sibling.source_range.end_pos
|
101
101
|
)
|
102
102
|
elsif node.right_sibling
|
103
|
-
node.
|
104
|
-
end_pos: node.right_sibling.
|
103
|
+
node.source_range.with(
|
104
|
+
end_pos: node.right_sibling.source_range.begin_pos
|
105
105
|
)
|
106
106
|
end
|
107
107
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
module Rails
|
7
|
+
# Check if using Minitest matchers.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# assert_equal(a, b)
|
12
|
+
# assert_equal a, b, "must be equal"
|
13
|
+
# refute_equal(a, b)
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# expect(b).to eq(a)
|
17
|
+
# expect(b).to(eq(a), "must be equal")
|
18
|
+
# expect(b).not_to eq(a)
|
19
|
+
#
|
20
|
+
class MinitestAssertions < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
23
|
+
MSG = 'Use `%<prefer>s`.'
|
24
|
+
RESTRICT_ON_SEND = %i[assert_equal refute_equal].freeze
|
25
|
+
|
26
|
+
# @!method minitest_assertion(node)
|
27
|
+
def_node_matcher :minitest_assertion, <<-PATTERN
|
28
|
+
(send nil? {:assert_equal :refute_equal} $_ $_ $_?)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
minitest_assertion(node) do |expected, actual, failure_message|
|
33
|
+
prefer = replacement(node, expected, actual,
|
34
|
+
failure_message.first)
|
35
|
+
add_offense(node, message: message(prefer)) do |corrector|
|
36
|
+
corrector.replace(node, prefer)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def replacement(node, expected, actual, failure_message)
|
44
|
+
runner = node.method?(:assert_equal) ? 'to' : 'not_to'
|
45
|
+
if failure_message.nil?
|
46
|
+
"expect(#{actual.source}).#{runner} eq(#{expected.source})"
|
47
|
+
else
|
48
|
+
"expect(#{actual.source}).#{runner}(eq(#{expected.source}), " \
|
49
|
+
"#{failure_message.source})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def message(prefer)
|
54
|
+
format(MSG, prefer: prefer)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
module Rails
|
7
|
+
# Enforces use of `be_invalid` or `not_to` for negated be_valid.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop is unsafe because it cannot guarantee that
|
11
|
+
# the test target is an instance of `ActiveModel::Validations``.
|
12
|
+
#
|
13
|
+
# @example EnforcedStyle: not_to (default)
|
14
|
+
# # bad
|
15
|
+
# expect(foo).to be_invalid
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# expect(foo).not_to be_valid
|
19
|
+
#
|
20
|
+
# # good (with method chain)
|
21
|
+
# expect(foo).to be_invalid.and be_odd
|
22
|
+
#
|
23
|
+
# @example EnforcedStyle: be_invalid
|
24
|
+
# # bad
|
25
|
+
# expect(foo).not_to be_valid
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# expect(foo).to be_invalid
|
29
|
+
#
|
30
|
+
# # good (with method chain)
|
31
|
+
# expect(foo).to be_invalid.or be_even
|
32
|
+
#
|
33
|
+
class NegationBeValid < Base
|
34
|
+
extend AutoCorrector
|
35
|
+
include ConfigurableEnforcedStyle
|
36
|
+
|
37
|
+
MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
|
38
|
+
RESTRICT_ON_SEND = %i[be_valid be_invalid].freeze
|
39
|
+
|
40
|
+
# @!method not_to?(node)
|
41
|
+
def_node_matcher :not_to?, <<~PATTERN
|
42
|
+
(send ... :not_to (send nil? :be_valid ...))
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
# @!method be_invalid?(node)
|
46
|
+
def_node_matcher :be_invalid?, <<~PATTERN
|
47
|
+
(send ... :to (send nil? :be_invalid ...))
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def on_send(node)
|
51
|
+
return unless offense?(node.parent)
|
52
|
+
|
53
|
+
add_offense(offense_range(node),
|
54
|
+
message: message(node.method_name)) do |corrector|
|
55
|
+
corrector.replace(node.parent.loc.selector, replaced_runner)
|
56
|
+
corrector.replace(node.loc.selector, replaced_matcher)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def offense?(node)
|
63
|
+
case style
|
64
|
+
when :not_to
|
65
|
+
be_invalid?(node)
|
66
|
+
when :be_invalid
|
67
|
+
not_to?(node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def offense_range(node)
|
72
|
+
node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
|
73
|
+
end
|
74
|
+
|
75
|
+
def message(_matcher)
|
76
|
+
format(MSG,
|
77
|
+
runner: replaced_runner,
|
78
|
+
matcher: replaced_matcher)
|
79
|
+
end
|
80
|
+
|
81
|
+
def replaced_runner
|
82
|
+
case style
|
83
|
+
when :not_to
|
84
|
+
'not_to'
|
85
|
+
when :be_invalid
|
86
|
+
'to'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def replaced_matcher
|
91
|
+
case style
|
92
|
+
when :not_to
|
93
|
+
'be_valid'
|
94
|
+
when :be_invalid
|
95
|
+
'be_invalid'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|