rubocop-rspec 2.16.0 → 2.24.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 +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
|