rubocop-rspec_rails 2.32.0 → 2.33.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/CHANGELOG.md +21 -0
- data/config/default.yml +16 -3
- data/lib/rubocop/cop/rspec_rails/avoid_setup_hook.rb +6 -3
- data/lib/rubocop/cop/rspec_rails/file_fixture.rb +45 -0
- data/lib/rubocop/cop/rspec_rails/have_http_status.rb +7 -3
- data/lib/rubocop/cop/rspec_rails/http_status.rb +48 -33
- data/lib/rubocop/cop/rspec_rails/http_status_name_consistency.rb +2 -2
- data/lib/rubocop/cop/rspec_rails/inferred_spec_type.rb +3 -8
- data/lib/rubocop/cop/rspec_rails/minitest_assertions.rb +94 -140
- data/lib/rubocop/cop/rspec_rails/negation_be_valid.rb +4 -4
- data/lib/rubocop/cop/rspec_rails/receive_perform_later.rb +107 -0
- data/lib/rubocop/cop/rspec_rails/travel_around.rb +44 -15
- data/lib/rubocop/cop/rspec_rails.rb +22 -0
- data/lib/rubocop/cop/rspec_rails_cops.rb +4 -8
- data/lib/rubocop/rspec_rails/version.rb +1 -1
- data/lib/rubocop-rspec_rails.rb +1 -1
- metadata +7 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a42fb50c1cc6fac2f7c5a5c81dcf9573d4cbc27390157cc0063b907c880b12ae
|
|
4
|
+
data.tar.gz: b4e95f46c045360b80c76177b975ba2932c247974e63d56cd7e376b176831288
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 594c53b8d30e097e94063476fd9d42d014ae3f4913a029956b66a9c25be1064d8136bedeea8247e7845bfd6278b614a8a7ea7da2a71e1677739500c44ce24782
|
|
7
|
+
data.tar.gz: 7318471ee0e4062142c4a64ca3ad5a718d7d00c6653d6210d7345bbf60208fe51f8b4c815849e369daecf3810d59d351c69b330ddb7130c028b89cbeb21af152
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Master (Unreleased)
|
|
4
4
|
|
|
5
|
+
## 2.33.0 (2026-10-07)
|
|
6
|
+
|
|
7
|
+
- Add `RSpecRails/FileFixture` cop to prefer `file_fixture` over hardcoded fixture paths. ([@ydah])
|
|
8
|
+
- Fix `RSpecRails/HttpStatus` to detect unknown symbolic HTTP statuses and handle custom numeric string statuses consistently. ([@ydah])
|
|
9
|
+
- Speed up loading rubocop-rspec_rails by lazily loading only the cops needed for a run. This requires RuboCop 1.89.0+. ([@koic])
|
|
10
|
+
- Fix offense message for `RSpecRails/HttpStatusNameConsistency` cop. ([@fatkodima])
|
|
11
|
+
- Fix `RSpecRails/MinitestAssertions` autocorrection for quoted predicate symbols. ([@ydah])
|
|
12
|
+
- Fix `RSpecRails/AvoidSetupHook` to only check setup hooks inside example groups. ([@ydah])
|
|
13
|
+
- Fix a false positive for `RspecRails/NegationBeValid` when use `to_not`. ([@ydah])
|
|
14
|
+
- Fix false positives for `RSpecRails/TravelAround` when travel does not directly wrap the around example. ([@ydah])
|
|
15
|
+
- Supporting correcting `assest_redirected_to` in `RSpec/Rails/MinitestAssertions`. ([@nzlaura])
|
|
16
|
+
- Add new `RSpecRails/ReceivePerformLater` cop. ([@ydah])
|
|
17
|
+
- Fix `RSpecRails/ReceivePerformLater` offense range to exclude chains after the expectation. ([@ydah])
|
|
18
|
+
- Fix a false positive for `RSpecRails/ReceivePerformLater` when `receive(:perform_later)` is used inside another matcher. ([@ydah])
|
|
19
|
+
- Support `described_class` for `RSpecRails/ReceivePerformLater`. ([@ydah])
|
|
20
|
+
- Fix `RSpecRails/TravelAround` autocorrection to keep the indentation of the surrounding `around` block. ([@eugeneius])
|
|
21
|
+
- Fix `RSpecRails/ReceivePerformLater` offense messages for negative expectations. ([@ydah])
|
|
22
|
+
|
|
5
23
|
## 2.32.0 (2025-11-12)
|
|
6
24
|
|
|
7
25
|
- Add `RSpecRails/HttpStatusNameConsistency` cop. ([@taketo1113])
|
|
@@ -83,8 +101,11 @@
|
|
|
83
101
|
[@anthony-robin]: https://github.com/anthony-robin
|
|
84
102
|
[@bquorning]: https://github.com/bquorning
|
|
85
103
|
[@corydiamand]: https://github.com/corydiamand
|
|
104
|
+
[@eugeneius]: https://github.com/eugeneius
|
|
105
|
+
[@fatkodima]: https://github.com/fatkodima
|
|
86
106
|
[@g-rath]: https://github.com/G-Rath
|
|
87
107
|
[@jojos003]: https://github.com/jojos003
|
|
108
|
+
[@koic]: https://github.com/koic
|
|
88
109
|
[@mothonmars]: https://github.com/MothOnMars
|
|
89
110
|
[@mvz]: https://github.com/mvz
|
|
90
111
|
[@nzlaura]: https://github.com/nzlaura
|
data/config/default.yml
CHANGED
|
@@ -7,11 +7,18 @@ RSpecRails:
|
|
|
7
7
|
- "**/spec/**/*"
|
|
8
8
|
|
|
9
9
|
RSpecRails/AvoidSetupHook:
|
|
10
|
-
Description: Checks that tests use RSpec `before`
|
|
10
|
+
Description: Checks that tests use RSpec `before` instead of Rails `setup`.
|
|
11
11
|
Enabled: pending
|
|
12
12
|
VersionAdded: '2.4'
|
|
13
13
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/AvoidSetupHook
|
|
14
14
|
|
|
15
|
+
RSpecRails/FileFixture:
|
|
16
|
+
Description: Prefer `file_fixture` over hardcoded paths into `spec/fixtures`.
|
|
17
|
+
Enabled: pending
|
|
18
|
+
Safe: false
|
|
19
|
+
VersionAdded: '2.33'
|
|
20
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/FileFixture
|
|
21
|
+
|
|
15
22
|
RSpecRails/HaveHttpStatus:
|
|
16
23
|
Description: Checks that tests use `have_http_status` instead of equality matchers.
|
|
17
24
|
Enabled: pending
|
|
@@ -65,7 +72,7 @@ RSpecRails/InferredSpecType:
|
|
|
65
72
|
views: view
|
|
66
73
|
|
|
67
74
|
RSpecRails/MinitestAssertions:
|
|
68
|
-
Description:
|
|
75
|
+
Description: Detects Minitest-like matchers.
|
|
69
76
|
Enabled: pending
|
|
70
77
|
VersionAdded: '2.17'
|
|
71
78
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/MinitestAssertions
|
|
@@ -83,8 +90,14 @@ RSpecRails/NegationBeValid:
|
|
|
83
90
|
VersionChanged: '2.29'
|
|
84
91
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/NegationBeValid
|
|
85
92
|
|
|
93
|
+
RSpecRails/ReceivePerformLater:
|
|
94
|
+
Description: Prefer `have_enqueued_job` over `receive(:perform_later)`.
|
|
95
|
+
Enabled: pending
|
|
96
|
+
VersionAdded: '2.33'
|
|
97
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/ReceivePerformLater
|
|
98
|
+
|
|
86
99
|
RSpecRails/TravelAround:
|
|
87
|
-
Description:
|
|
100
|
+
Description: Checks for time travel helpers in `around` hooks.
|
|
88
101
|
Enabled: pending
|
|
89
102
|
Safe: false
|
|
90
103
|
VersionAdded: '2.19'
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module RSpecRails
|
|
6
|
-
# Checks that tests use RSpec `before`
|
|
6
|
+
# Checks that tests use RSpec `before` instead of Rails `setup`.
|
|
7
7
|
#
|
|
8
8
|
# @example
|
|
9
9
|
# # bad
|
|
@@ -16,8 +16,9 @@ module RuboCop
|
|
|
16
16
|
# allow(foo).to receive(:bar)
|
|
17
17
|
# end
|
|
18
18
|
#
|
|
19
|
-
class AvoidSetupHook <
|
|
19
|
+
class AvoidSetupHook < RuboCop::Cop::RSpec::Base
|
|
20
20
|
extend AutoCorrector
|
|
21
|
+
include RuboCop::Cop::RSpec::InsideExampleGroup
|
|
21
22
|
|
|
22
23
|
MSG = 'Use `before` instead of `setup`.'
|
|
23
24
|
|
|
@@ -28,7 +29,9 @@ module RuboCop
|
|
|
28
29
|
(args) _)
|
|
29
30
|
PATTERN
|
|
30
31
|
|
|
31
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
32
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler, InternalAffairs/NumblockHandler
|
|
33
|
+
return unless inside_example_group?(node)
|
|
34
|
+
|
|
32
35
|
setup_call(node) do |setup|
|
|
33
36
|
add_offense(node) do |corrector|
|
|
34
37
|
corrector.replace setup, 'before'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpecRails
|
|
6
|
+
# Prefer `file_fixture` over hardcoded paths into `spec/fixtures`.
|
|
7
|
+
#
|
|
8
|
+
# @safety
|
|
9
|
+
# `file_fixture` uses the configured `file_fixture_path`, which defaults
|
|
10
|
+
# to `spec/fixtures/files`. A file under `spec/fixtures` may need to be
|
|
11
|
+
# moved or `file_fixture_path` configured before replacing the path.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# # bad
|
|
15
|
+
# Rails.root.join('spec', 'fixtures', 'files', 'example.pdf')
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# file_fixture('example.pdf')
|
|
19
|
+
class FileFixture < RuboCop::Cop::Base
|
|
20
|
+
MSG = 'Prefer `file_fixture` for files under `spec/fixtures`.'
|
|
21
|
+
RESTRICT_ON_SEND = %i[join].freeze
|
|
22
|
+
|
|
23
|
+
# @!method fixture_path?(node)
|
|
24
|
+
def_node_matcher :fixture_path?, <<~PATTERN
|
|
25
|
+
(send
|
|
26
|
+
(send (const {nil? cbase} :Rails) :root)
|
|
27
|
+
:join (str "spec") (str "fixtures") _ ...)
|
|
28
|
+
PATTERN
|
|
29
|
+
|
|
30
|
+
# @!method combined_fixture_path?(node)
|
|
31
|
+
def_node_matcher :combined_fixture_path?, <<~PATTERN
|
|
32
|
+
(send
|
|
33
|
+
(send (const {nil? cbase} :Rails) :root)
|
|
34
|
+
:join (str "spec/fixtures") _ ...)
|
|
35
|
+
PATTERN
|
|
36
|
+
|
|
37
|
+
def on_send(node)
|
|
38
|
+
return unless fixture_path?(node) || combined_fixture_path?(node)
|
|
39
|
+
|
|
40
|
+
add_offense(node.loc.selector)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -5,7 +5,11 @@ module RuboCop
|
|
|
5
5
|
module RSpecRails
|
|
6
6
|
# Checks that tests use `have_http_status` instead of equality matchers.
|
|
7
7
|
#
|
|
8
|
-
# @
|
|
8
|
+
# @safety
|
|
9
|
+
# The autocorrection is marked as unsafe because
|
|
10
|
+
# `response.status` response is not always an HTTP response status.
|
|
11
|
+
#
|
|
12
|
+
# @example `ResponseMethods: ['response', 'last_response']` (default)
|
|
9
13
|
# # bad
|
|
10
14
|
# expect(response.status).to be(200)
|
|
11
15
|
# expect(last_response.code).to eq("200")
|
|
@@ -14,7 +18,7 @@ module RuboCop
|
|
|
14
18
|
# expect(response).to have_http_status(200)
|
|
15
19
|
# expect(last_response).to have_http_status(200)
|
|
16
20
|
#
|
|
17
|
-
# @example ResponseMethods: ['foo_response']
|
|
21
|
+
# @example `ResponseMethods: ['foo_response']`
|
|
18
22
|
# # bad
|
|
19
23
|
# expect(foo_response.status).to be(200)
|
|
20
24
|
#
|
|
@@ -25,7 +29,7 @@ module RuboCop
|
|
|
25
29
|
# expect(response).to have_http_status(200)
|
|
26
30
|
# expect(last_response).to have_http_status(200)
|
|
27
31
|
#
|
|
28
|
-
class HaveHttpStatus <
|
|
32
|
+
class HaveHttpStatus < RuboCop::Cop::Base
|
|
29
33
|
extend AutoCorrector
|
|
30
34
|
|
|
31
35
|
MSG =
|
|
@@ -64,9 +64,10 @@ module RuboCop
|
|
|
64
64
|
#
|
|
65
65
|
# # good
|
|
66
66
|
# it { is_expected.to have_http_status :ok }
|
|
67
|
-
class HttpStatus <
|
|
67
|
+
class HttpStatus < RuboCop::Cop::Base
|
|
68
68
|
extend AutoCorrector
|
|
69
69
|
include ConfigurableEnforcedStyle
|
|
70
|
+
|
|
70
71
|
RESTRICT_ON_SEND = %i[have_http_status].freeze
|
|
71
72
|
|
|
72
73
|
# @!method http_status(node)
|
|
@@ -142,16 +143,44 @@ module RuboCop
|
|
|
142
143
|
node.sym_type? && ALLOWED_STATUSES.include?(node.value)
|
|
143
144
|
end
|
|
144
145
|
|
|
146
|
+
def known_status_symbol?
|
|
147
|
+
node.sym_type? &&
|
|
148
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(node.value)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def known_status_code?
|
|
152
|
+
numeric_status_code? &&
|
|
153
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(status_code_number)
|
|
154
|
+
end
|
|
155
|
+
|
|
145
156
|
def custom_http_status_code?
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
numeric_status_code? && !known_status_code?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def numeric_status_code?
|
|
161
|
+
node.int_type? || numeric_string?
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def numeric_string?
|
|
165
|
+
node.str_type? && node.value.match?(/\A\d+\z/)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def status_code_number
|
|
169
|
+
node.value.to_i
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def status_code_symbol
|
|
173
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(status_code_number)
|
|
148
174
|
end
|
|
149
175
|
end
|
|
150
176
|
|
|
151
177
|
# :nodoc:
|
|
152
178
|
class SymbolicStyleChecker < StyleCheckerBase
|
|
153
179
|
def offensive?
|
|
154
|
-
|
|
180
|
+
return false if allowed_symbol? || known_status_symbol?
|
|
181
|
+
return false if custom_http_status_code?
|
|
182
|
+
|
|
183
|
+
true
|
|
155
184
|
end
|
|
156
185
|
|
|
157
186
|
def autocorrectable?
|
|
@@ -165,18 +194,17 @@ module RuboCop
|
|
|
165
194
|
private
|
|
166
195
|
|
|
167
196
|
def symbol
|
|
168
|
-
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def number
|
|
172
|
-
node.value.to_i
|
|
197
|
+
status_code_symbol if known_status_code?
|
|
173
198
|
end
|
|
174
199
|
end
|
|
175
200
|
|
|
176
201
|
# :nodoc:
|
|
177
202
|
class NumericStyleChecker < StyleCheckerBase
|
|
178
203
|
def offensive?
|
|
179
|
-
|
|
204
|
+
return false if node.int_type? || allowed_symbol?
|
|
205
|
+
return false if custom_http_status_code?
|
|
206
|
+
|
|
207
|
+
true
|
|
180
208
|
end
|
|
181
209
|
|
|
182
210
|
def autocorrectable?
|
|
@@ -189,20 +217,19 @@ module RuboCop
|
|
|
189
217
|
|
|
190
218
|
private
|
|
191
219
|
|
|
192
|
-
def symbol
|
|
193
|
-
node.value
|
|
194
|
-
end
|
|
195
|
-
|
|
196
220
|
def number
|
|
197
|
-
|
|
221
|
+
return status_code_number if known_status_code?
|
|
222
|
+
|
|
223
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE[node.value.to_sym]
|
|
198
224
|
end
|
|
199
225
|
end
|
|
200
226
|
|
|
201
227
|
# :nodoc:
|
|
202
228
|
class BeStatusStyleChecker < StyleCheckerBase
|
|
203
229
|
def offensive?
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
return false if allowed_symbol? || custom_http_status_code?
|
|
231
|
+
|
|
232
|
+
true
|
|
206
233
|
end
|
|
207
234
|
|
|
208
235
|
def autocorrectable?
|
|
@@ -220,30 +247,18 @@ module RuboCop
|
|
|
220
247
|
private
|
|
221
248
|
|
|
222
249
|
def status_code
|
|
223
|
-
if
|
|
250
|
+
if known_status_symbol?
|
|
224
251
|
node.value
|
|
225
|
-
elsif
|
|
226
|
-
|
|
252
|
+
elsif known_status_code?
|
|
253
|
+
status_code_symbol
|
|
227
254
|
else
|
|
228
255
|
normalize_str
|
|
229
256
|
end
|
|
230
257
|
end
|
|
231
258
|
|
|
232
|
-
def symbol
|
|
233
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
def number
|
|
237
|
-
node.value.to_i
|
|
238
|
-
end
|
|
239
|
-
|
|
240
259
|
def normalize_str
|
|
241
260
|
str = node.value.to_s
|
|
242
|
-
if
|
|
243
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(str.to_i)
|
|
244
|
-
elsif ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(str.to_sym)
|
|
245
|
-
str
|
|
246
|
-
end
|
|
261
|
+
str if ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(str.to_sym)
|
|
247
262
|
end
|
|
248
263
|
end
|
|
249
264
|
end
|
|
@@ -13,12 +13,12 @@ module RuboCop
|
|
|
13
13
|
# # good
|
|
14
14
|
# it { is_expected.to have_http_status :unprocessable_content }
|
|
15
15
|
#
|
|
16
|
-
class HttpStatusNameConsistency <
|
|
16
|
+
class HttpStatusNameConsistency < RuboCop::Cop::Base
|
|
17
17
|
extend AutoCorrector
|
|
18
18
|
|
|
19
19
|
requires_gem 'rack', '>= 3.1.0'
|
|
20
20
|
|
|
21
|
-
MSG = '
|
|
21
|
+
MSG = 'Prefer `:%<preferred>s` over `:%<current>s`.'
|
|
22
22
|
|
|
23
23
|
RESTRICT_ON_SEND = %i[have_http_status].freeze
|
|
24
24
|
|
|
@@ -30,12 +30,7 @@ module RuboCop
|
|
|
30
30
|
# RSpec.describe User, type: :common do
|
|
31
31
|
# end
|
|
32
32
|
#
|
|
33
|
-
# @example `Inferences`
|
|
34
|
-
# # .rubocop.yml
|
|
35
|
-
# # RSpecRails/InferredSpecType:
|
|
36
|
-
# # Inferences:
|
|
37
|
-
# # services: service
|
|
38
|
-
#
|
|
33
|
+
# @example `Inferences: {services: service}`
|
|
39
34
|
# # bad
|
|
40
35
|
# # spec/services/user_spec.rb
|
|
41
36
|
# RSpec.describe User, type: :service do
|
|
@@ -50,13 +45,13 @@ module RuboCop
|
|
|
50
45
|
# # spec/services/user_spec.rb
|
|
51
46
|
# RSpec.describe User, type: :common do
|
|
52
47
|
# end
|
|
53
|
-
class InferredSpecType <
|
|
48
|
+
class InferredSpecType < RuboCop::Cop::RSpec::Base
|
|
54
49
|
extend AutoCorrector
|
|
55
50
|
|
|
56
51
|
MSG = 'Remove redundant spec type.'
|
|
57
52
|
|
|
58
53
|
# @param [RuboCop::AST::BlockNode] node
|
|
59
|
-
def on_block(node)
|
|
54
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
|
|
60
55
|
return unless example_group?(node)
|
|
61
56
|
|
|
62
57
|
pair_node = describe_with_type(node)
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module RSpecRails
|
|
6
|
-
#
|
|
6
|
+
# Detects Minitest-like matchers.
|
|
7
7
|
#
|
|
8
|
-
#
|
|
9
|
-
# starting with `assert_` or `refute_
|
|
8
|
+
# Detects the use of Minitest-like matchers
|
|
9
|
+
# (those starting with `assert_` or `refute_`).
|
|
10
10
|
#
|
|
11
11
|
# @example
|
|
12
12
|
# # bad
|
|
@@ -19,6 +19,7 @@ module RuboCop
|
|
|
19
19
|
# assert_true(a)
|
|
20
20
|
# assert_false(a)
|
|
21
21
|
# assert_response :ok
|
|
22
|
+
# assert_redirected_to '/users'
|
|
22
23
|
#
|
|
23
24
|
# # good
|
|
24
25
|
# expect(b).to eq(a)
|
|
@@ -30,8 +31,9 @@ module RuboCop
|
|
|
30
31
|
# expect(a).to be(true)
|
|
31
32
|
# expect(a).to be(false)
|
|
32
33
|
# expect(response).to have_http_status(:ok)
|
|
34
|
+
# expect(response).to redirect_to('/users')
|
|
33
35
|
#
|
|
34
|
-
class MinitestAssertions <
|
|
36
|
+
class MinitestAssertions < RuboCop::Cop::Base
|
|
35
37
|
extend AutoCorrector
|
|
36
38
|
|
|
37
39
|
# :nodoc:
|
|
@@ -40,12 +42,25 @@ module RuboCop
|
|
|
40
42
|
|
|
41
43
|
attr_reader :expected, :actual, :failure_message
|
|
42
44
|
|
|
45
|
+
def self.match_assertions(*matchers, captures:)
|
|
46
|
+
const_set(:MATCHERS, matchers.freeze)
|
|
47
|
+
|
|
48
|
+
matcher_pattern = matchers.map { |matcher| ":#{matcher}" }.join(' ')
|
|
49
|
+
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
50
|
+
(send nil? {#{matcher_pattern}} #{captures})
|
|
51
|
+
PATTERN
|
|
52
|
+
end
|
|
53
|
+
|
|
43
54
|
def self.minitest_assertion
|
|
44
55
|
# :nocov:
|
|
45
56
|
raise NotImplementedError
|
|
46
57
|
# :nocov:
|
|
47
58
|
end
|
|
48
59
|
|
|
60
|
+
def self.match(expected, actual, failure_message)
|
|
61
|
+
new(expected, actual, failure_message.first)
|
|
62
|
+
end
|
|
63
|
+
|
|
49
64
|
def initialize(expected, actual, failure_message)
|
|
50
65
|
@expected = expected&.source
|
|
51
66
|
@actual = actual.source
|
|
@@ -74,20 +89,10 @@ module RuboCop
|
|
|
74
89
|
|
|
75
90
|
# :nodoc:
|
|
76
91
|
class EqualAssertion < BasicAssertion
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
].freeze
|
|
82
|
-
|
|
83
|
-
# @!method self.minitest_assertion(node)
|
|
84
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
85
|
-
(send nil? {:assert_equal :assert_not_equal :refute_equal} $_ $_ $_?)
|
|
86
|
-
PATTERN
|
|
87
|
-
|
|
88
|
-
def self.match(expected, actual, failure_message)
|
|
89
|
-
new(expected, actual, failure_message.first)
|
|
90
|
-
end
|
|
92
|
+
match_assertions :assert_equal,
|
|
93
|
+
:assert_not_equal,
|
|
94
|
+
:refute_equal,
|
|
95
|
+
captures: '$_ $_ $_?'
|
|
91
96
|
|
|
92
97
|
def assertion
|
|
93
98
|
"eq(#{expected})"
|
|
@@ -96,20 +101,10 @@ module RuboCop
|
|
|
96
101
|
|
|
97
102
|
# :nodoc:
|
|
98
103
|
class KindOfAssertion < BasicAssertion
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
].freeze
|
|
104
|
-
|
|
105
|
-
# @!method self.minitest_assertion(node)
|
|
106
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
107
|
-
(send nil? {:assert_kind_of :assert_not_kind_of :refute_kind_of} $_ $_ $_?)
|
|
108
|
-
PATTERN
|
|
109
|
-
|
|
110
|
-
def self.match(expected, actual, failure_message)
|
|
111
|
-
new(expected, actual, failure_message.first)
|
|
112
|
-
end
|
|
104
|
+
match_assertions :assert_kind_of,
|
|
105
|
+
:assert_not_kind_of,
|
|
106
|
+
:refute_kind_of,
|
|
107
|
+
captures: '$_ $_ $_?'
|
|
113
108
|
|
|
114
109
|
def assertion
|
|
115
110
|
"be_a_kind_of(#{expected})"
|
|
@@ -118,20 +113,10 @@ module RuboCop
|
|
|
118
113
|
|
|
119
114
|
# :nodoc:
|
|
120
115
|
class InstanceOfAssertion < BasicAssertion
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
].freeze
|
|
126
|
-
|
|
127
|
-
# @!method self.minitest_assertion(node)
|
|
128
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
129
|
-
(send nil? {:assert_instance_of :assert_not_instance_of :refute_instance_of} $_ $_ $_?)
|
|
130
|
-
PATTERN
|
|
131
|
-
|
|
132
|
-
def self.match(expected, actual, failure_message)
|
|
133
|
-
new(expected, actual, failure_message.first)
|
|
134
|
-
end
|
|
116
|
+
match_assertions :assert_instance_of,
|
|
117
|
+
:assert_not_instance_of,
|
|
118
|
+
:refute_instance_of,
|
|
119
|
+
captures: '$_ $_ $_?'
|
|
135
120
|
|
|
136
121
|
def assertion
|
|
137
122
|
"be_an_instance_of(#{expected})"
|
|
@@ -140,16 +125,10 @@ module RuboCop
|
|
|
140
125
|
|
|
141
126
|
# :nodoc:
|
|
142
127
|
class IncludesAssertion < BasicAssertion
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
].freeze
|
|
148
|
-
|
|
149
|
-
# @!method self.minitest_assertion(node)
|
|
150
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
151
|
-
(send nil? {:assert_includes :assert_not_includes :refute_includes} $_ $_ $_?)
|
|
152
|
-
PATTERN
|
|
128
|
+
match_assertions :assert_includes,
|
|
129
|
+
:assert_not_includes,
|
|
130
|
+
:refute_includes,
|
|
131
|
+
captures: '$_ $_ $_?'
|
|
153
132
|
|
|
154
133
|
def self.match(collection, expected, failure_message)
|
|
155
134
|
new(expected, collection, failure_message.first)
|
|
@@ -162,16 +141,10 @@ module RuboCop
|
|
|
162
141
|
|
|
163
142
|
# :nodoc:
|
|
164
143
|
class InDeltaAssertion < BasicAssertion
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
].freeze
|
|
170
|
-
|
|
171
|
-
# @!method self.minitest_assertion(node)
|
|
172
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
173
|
-
(send nil? {:assert_in_delta :assert_not_in_delta :refute_in_delta} $_ $_ $_? $_?)
|
|
174
|
-
PATTERN
|
|
144
|
+
match_assertions :assert_in_delta,
|
|
145
|
+
:assert_not_in_delta,
|
|
146
|
+
:refute_in_delta,
|
|
147
|
+
captures: '$_ $_ $_? $_?'
|
|
175
148
|
|
|
176
149
|
def self.match(expected, actual, delta, failure_message)
|
|
177
150
|
new(expected, actual, delta.first, failure_message.first)
|
|
@@ -190,43 +163,41 @@ module RuboCop
|
|
|
190
163
|
|
|
191
164
|
# :nodoc:
|
|
192
165
|
class PredicateAssertion < BasicAssertion
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
].freeze
|
|
198
|
-
|
|
199
|
-
# @!method self.minitest_assertion(node)
|
|
200
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
201
|
-
(send nil? {:assert_predicate :assert_not_predicate :refute_predicate} $_ ${sym} $_?)
|
|
202
|
-
PATTERN
|
|
166
|
+
match_assertions :assert_predicate,
|
|
167
|
+
:assert_not_predicate,
|
|
168
|
+
:refute_predicate,
|
|
169
|
+
captures: '$_ ${sym} $_?'
|
|
203
170
|
|
|
204
171
|
def self.match(subject, predicate, failure_message)
|
|
205
|
-
|
|
172
|
+
predicate_name = predicate.value.to_s
|
|
173
|
+
return nil unless predicate_name.end_with?('?')
|
|
174
|
+
|
|
175
|
+
matcher_name = predicate_name.delete_suffix('?')
|
|
176
|
+
return nil unless valid_matcher_name?(matcher_name)
|
|
206
177
|
|
|
207
|
-
new(predicate, subject, failure_message.first)
|
|
178
|
+
new(predicate, subject, failure_message.first, matcher_name)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def self.valid_matcher_name?(matcher_name)
|
|
182
|
+
matcher_name.match?(/\A\w+\z/)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def initialize(expected, actual, failure_message, matcher_name)
|
|
186
|
+
super(expected, actual, failure_message)
|
|
187
|
+
|
|
188
|
+
@matcher_name = matcher_name
|
|
208
189
|
end
|
|
209
190
|
|
|
210
191
|
def assertion
|
|
211
|
-
"be_#{
|
|
192
|
+
"be_#{@matcher_name}"
|
|
212
193
|
end
|
|
213
194
|
end
|
|
214
195
|
|
|
215
196
|
# :nodoc:
|
|
216
197
|
class MatchAssertion < BasicAssertion
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
].freeze
|
|
221
|
-
|
|
222
|
-
# @!method self.minitest_assertion(node)
|
|
223
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
224
|
-
(send nil? {:assert_match :refute_match} $_ $_ $_?)
|
|
225
|
-
PATTERN
|
|
226
|
-
|
|
227
|
-
def self.match(matcher, actual, failure_message)
|
|
228
|
-
new(matcher, actual, failure_message.first)
|
|
229
|
-
end
|
|
198
|
+
match_assertions :assert_match,
|
|
199
|
+
:refute_match,
|
|
200
|
+
captures: '$_ $_ $_?'
|
|
230
201
|
|
|
231
202
|
def assertion
|
|
232
203
|
"match(#{expected})"
|
|
@@ -235,16 +206,10 @@ module RuboCop
|
|
|
235
206
|
|
|
236
207
|
# :nodoc:
|
|
237
208
|
class NilAssertion < BasicAssertion
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
].freeze
|
|
243
|
-
|
|
244
|
-
# @!method self.minitest_assertion(node)
|
|
245
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
246
|
-
(send nil? {:assert_nil :assert_not_nil :refute_nil} $_ $_?)
|
|
247
|
-
PATTERN
|
|
209
|
+
match_assertions :assert_nil,
|
|
210
|
+
:assert_not_nil,
|
|
211
|
+
:refute_nil,
|
|
212
|
+
captures: '$_ $_?'
|
|
248
213
|
|
|
249
214
|
def self.match(actual, failure_message)
|
|
250
215
|
new(nil, actual, failure_message.first)
|
|
@@ -257,16 +222,10 @@ module RuboCop
|
|
|
257
222
|
|
|
258
223
|
# :nodoc:
|
|
259
224
|
class EmptyAssertion < BasicAssertion
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
].freeze
|
|
265
|
-
|
|
266
|
-
# @!method self.minitest_assertion(node)
|
|
267
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
268
|
-
(send nil? {:assert_empty :assert_not_empty :refute_empty} $_ $_?)
|
|
269
|
-
PATTERN
|
|
225
|
+
match_assertions :assert_empty,
|
|
226
|
+
:assert_not_empty,
|
|
227
|
+
:refute_empty,
|
|
228
|
+
captures: '$_ $_?'
|
|
270
229
|
|
|
271
230
|
def self.match(actual, failure_message)
|
|
272
231
|
new(nil, actual, failure_message.first)
|
|
@@ -279,14 +238,7 @@ module RuboCop
|
|
|
279
238
|
|
|
280
239
|
# :nodoc:
|
|
281
240
|
class TrueAssertion < BasicAssertion
|
|
282
|
-
|
|
283
|
-
assert_true
|
|
284
|
-
].freeze
|
|
285
|
-
|
|
286
|
-
# @!method self.minitest_assertion(node)
|
|
287
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
288
|
-
(send nil? {:assert_true} $_ $_?)
|
|
289
|
-
PATTERN
|
|
241
|
+
match_assertions :assert_true, captures: '$_ $_?'
|
|
290
242
|
|
|
291
243
|
def self.match(actual, failure_message)
|
|
292
244
|
new(nil, actual, failure_message.first)
|
|
@@ -299,14 +251,7 @@ module RuboCop
|
|
|
299
251
|
|
|
300
252
|
# :nodoc:
|
|
301
253
|
class FalseAssertion < BasicAssertion
|
|
302
|
-
|
|
303
|
-
assert_false
|
|
304
|
-
].freeze
|
|
305
|
-
|
|
306
|
-
# @!method self.minitest_assertion(node)
|
|
307
|
-
def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
|
|
308
|
-
(send nil? {:assert_false} $_ $_?)
|
|
309
|
-
PATTERN
|
|
254
|
+
match_assertions :assert_false, captures: '$_ $_?'
|
|
310
255
|
|
|
311
256
|
def self.match(actual, failure_message)
|
|
312
257
|
new(nil, actual, failure_message.first)
|
|
@@ -318,27 +263,36 @@ module RuboCop
|
|
|
318
263
|
end
|
|
319
264
|
|
|
320
265
|
# :nodoc:
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
assert_response
|
|
324
|
-
].freeze
|
|
266
|
+
module ResponseAssertionActual
|
|
267
|
+
RESPONSE_NODE = Struct.new(:source).new('response')
|
|
325
268
|
|
|
326
|
-
|
|
269
|
+
def match(expected, failure_message)
|
|
270
|
+
new(expected, RESPONSE_NODE, failure_message.first)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
327
273
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
PATTERN
|
|
274
|
+
# :nodoc:
|
|
275
|
+
class ResponseAssertion < BasicAssertion
|
|
276
|
+
extend ResponseAssertionActual
|
|
332
277
|
|
|
333
|
-
|
|
334
|
-
new(expected, ASSERT_ACTUAL_NODE, failure_message.first)
|
|
335
|
-
end
|
|
278
|
+
match_assertions :assert_response, captures: '$_ $_?'
|
|
336
279
|
|
|
337
280
|
def assertion
|
|
338
281
|
"have_http_status(#{expected})"
|
|
339
282
|
end
|
|
340
283
|
end
|
|
341
284
|
|
|
285
|
+
# :nodoc:
|
|
286
|
+
class RedirectAssertion < BasicAssertion
|
|
287
|
+
extend ResponseAssertionActual
|
|
288
|
+
|
|
289
|
+
match_assertions :assert_redirected_to, captures: '$_ $_?'
|
|
290
|
+
|
|
291
|
+
def assertion
|
|
292
|
+
"redirect_to(#{expected})"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
342
296
|
MSG = 'Use `%<prefer>s`.'
|
|
343
297
|
|
|
344
298
|
# TODO: replace with `BasicAssertion.subclasses` in Ruby 3.1+
|
|
@@ -9,7 +9,7 @@ module RuboCop
|
|
|
9
9
|
# This cop is unsafe because it cannot guarantee that
|
|
10
10
|
# the test target is an instance of `ActiveModel::Validations``.
|
|
11
11
|
#
|
|
12
|
-
# @example EnforcedStyle: not_to (default)
|
|
12
|
+
# @example `EnforcedStyle: not_to` (default)
|
|
13
13
|
# # bad
|
|
14
14
|
# expect(foo).to be_invalid
|
|
15
15
|
#
|
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
|
19
19
|
# # good (with method chain)
|
|
20
20
|
# expect(foo).to be_invalid.and be_odd
|
|
21
21
|
#
|
|
22
|
-
# @example EnforcedStyle: be_invalid
|
|
22
|
+
# @example `EnforcedStyle: be_invalid`
|
|
23
23
|
# # bad
|
|
24
24
|
# expect(foo).not_to be_valid
|
|
25
25
|
#
|
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
|
29
29
|
# # good (with method chain)
|
|
30
30
|
# expect(foo).to be_invalid.or be_even
|
|
31
31
|
#
|
|
32
|
-
class NegationBeValid <
|
|
32
|
+
class NegationBeValid < RuboCop::Cop::Base
|
|
33
33
|
extend AutoCorrector
|
|
34
34
|
include ConfigurableEnforcedStyle
|
|
35
35
|
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
|
|
39
39
|
# @!method not_to?(node)
|
|
40
40
|
def_node_matcher :not_to?, <<~PATTERN
|
|
41
|
-
(send ... :not_to (send nil? :be_valid ...))
|
|
41
|
+
(send ... {:not_to :to_not} (send nil? :be_valid ...))
|
|
42
42
|
PATTERN
|
|
43
43
|
|
|
44
44
|
# @!method be_invalid?(node)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpecRails
|
|
6
|
+
# Prefer `have_enqueued_job` over `receive(:perform_later)`.
|
|
7
|
+
#
|
|
8
|
+
# The `have_enqueued_job` matcher is preferred for testing ActiveJob
|
|
9
|
+
# enqueuing. It is more explicit and provides better clarity than
|
|
10
|
+
# using `receive(:perform_later)`.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# expect(MyJob).to receive(:perform_later)
|
|
15
|
+
# do_something
|
|
16
|
+
#
|
|
17
|
+
# # bad
|
|
18
|
+
# allow(MyJob).to receive(:perform_later)
|
|
19
|
+
# do_something
|
|
20
|
+
# expect(MyJob).to have_received(:perform_later)
|
|
21
|
+
#
|
|
22
|
+
# # bad
|
|
23
|
+
# expect(MyJob).to receive(:perform_later).with(user, order)
|
|
24
|
+
#
|
|
25
|
+
# # good
|
|
26
|
+
# expect { do_something }.to have_enqueued_job(MyJob)
|
|
27
|
+
#
|
|
28
|
+
# # good
|
|
29
|
+
# expect { do_something }.to have_enqueued_job(MyJob).with(user, order)
|
|
30
|
+
#
|
|
31
|
+
# # good
|
|
32
|
+
# expect { do_something }
|
|
33
|
+
# .to have_enqueued_job(MyJob)
|
|
34
|
+
# .on_queue('mailers')
|
|
35
|
+
# .at(Date.tomorrow.noon)
|
|
36
|
+
#
|
|
37
|
+
class ReceivePerformLater < RuboCop::Cop::Base
|
|
38
|
+
MSG = 'Prefer `expect { ... }.%<runner>s ' \
|
|
39
|
+
'have_enqueued_job(%<job_class>s)` ' \
|
|
40
|
+
'over `%<receiver>s(%<job_class>s).%<to>s ' \
|
|
41
|
+
'%<matcher>s(:perform_later)`.'
|
|
42
|
+
|
|
43
|
+
RESTRICT_ON_SEND = %i[receive have_received].to_set
|
|
44
|
+
RUNNERS = %i[to to_not not_to].freeze
|
|
45
|
+
|
|
46
|
+
# @!method receive_perform_later?(node)
|
|
47
|
+
def_node_matcher :receive_perform_later?, <<~PATTERN
|
|
48
|
+
(send nil? {:receive :have_received}
|
|
49
|
+
(sym :perform_later))
|
|
50
|
+
PATTERN
|
|
51
|
+
|
|
52
|
+
# @!method expect_or_allow?(node)
|
|
53
|
+
def_node_matcher :expect_or_allow?, <<~PATTERN
|
|
54
|
+
(send nil? {:expect :allow} {const_type? (send nil? :described_class)})
|
|
55
|
+
PATTERN
|
|
56
|
+
|
|
57
|
+
def on_send(node)
|
|
58
|
+
return unless receive_perform_later?(node)
|
|
59
|
+
return unless (runner_node = find_runner_node(node))
|
|
60
|
+
return unless runner_matcher?(node, runner_node)
|
|
61
|
+
|
|
62
|
+
register_offense(node, runner_node)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def register_offense(matcher_node, runner_node)
|
|
68
|
+
expect_node = runner_node.receiver
|
|
69
|
+
return unless expect_or_allow?(expect_node)
|
|
70
|
+
return if allow_receive_combination?(expect_node, matcher_node)
|
|
71
|
+
|
|
72
|
+
job_class = expect_node.first_argument
|
|
73
|
+
add_offense(runner_node,
|
|
74
|
+
message: offense_message(expect_node, job_class,
|
|
75
|
+
runner_node, matcher_node))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def allow_receive_combination?(expect_node, matcher_node)
|
|
79
|
+
expect_node.method?(:allow) && matcher_node.method?(:receive)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def offense_message(expect_node, job_class, runner_node, matcher_node)
|
|
83
|
+
format(MSG,
|
|
84
|
+
receiver: expect_node.method_name,
|
|
85
|
+
job_class: job_class.source,
|
|
86
|
+
runner: runner_node.method_name,
|
|
87
|
+
to: runner_node.method_name,
|
|
88
|
+
matcher: matcher_node.method_name)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def find_runner_node(node)
|
|
92
|
+
node.each_ancestor(:send).find { |ancestor| runner?(ancestor) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def runner_matcher?(matcher_node, runner_node)
|
|
96
|
+
node = runner_node.first_argument
|
|
97
|
+
node = node.receiver while node&.send_type? && node != matcher_node
|
|
98
|
+
node == matcher_node
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def runner?(node)
|
|
102
|
+
RUNNERS.include?(node.method_name)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -3,15 +3,26 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module RSpecRails
|
|
6
|
-
#
|
|
6
|
+
# Checks for time travel helpers in `around` hooks.
|
|
7
|
+
#
|
|
8
|
+
# In Rails example groups, `freeze_time` in a `before` hook does not need
|
|
9
|
+
# a block: Rails' `after_teardown` calls `travel_back` after each example.
|
|
10
|
+
# This also allows an example to use block-form time travel, which Rails
|
|
11
|
+
# does not allow inside another time travel block.
|
|
12
|
+
# See https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
|
|
7
13
|
#
|
|
8
14
|
# @safety
|
|
9
|
-
#
|
|
10
|
-
#
|
|
15
|
+
# The automatic `travel_back` requires both
|
|
16
|
+
# `ActiveSupport::Testing::TimeHelpers` and RSpec Rails' lifecycle
|
|
17
|
+
# hooks. RSpec Rails applies those hooks to example groups with Rails
|
|
18
|
+
# `type:` metadata. File paths set that metadata only when
|
|
19
|
+
# `infer_spec_type_from_file_location!` is enabled. In a plain RSpec
|
|
20
|
+
# group that only includes TimeHelpers, use a block or call
|
|
21
|
+
# `travel_back` explicitly.
|
|
22
|
+
# See https://rspec.info/features/7-1/rspec-rails/directory-structure/
|
|
11
23
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# `around`.
|
|
24
|
+
# Autocorrection can change execution order: `around` starts before
|
|
25
|
+
# all `before` hooks, while `before` runs in normal hook order.
|
|
15
26
|
#
|
|
16
27
|
# @example
|
|
17
28
|
# # bad
|
|
@@ -29,7 +40,7 @@ module RuboCop
|
|
|
29
40
|
# # good
|
|
30
41
|
# before { freeze_time }
|
|
31
42
|
#
|
|
32
|
-
class TravelAround <
|
|
43
|
+
class TravelAround < RuboCop::Cop::Base
|
|
33
44
|
extend AutoCorrector
|
|
34
45
|
|
|
35
46
|
MSG = 'Prefer to travel in `before` rather than `around`.'
|
|
@@ -45,7 +56,7 @@ module RuboCop
|
|
|
45
56
|
(block
|
|
46
57
|
$(send nil? TRAVEL_METHOD_NAMES ...)
|
|
47
58
|
(args ...)
|
|
48
|
-
(send _ :run)
|
|
59
|
+
(send (lvar _) :run)
|
|
49
60
|
)
|
|
50
61
|
PATTERN
|
|
51
62
|
|
|
@@ -64,7 +75,7 @@ module RuboCop
|
|
|
64
75
|
)
|
|
65
76
|
PATTERN
|
|
66
77
|
|
|
67
|
-
def on_block(node)
|
|
78
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
|
|
68
79
|
extract_run_in_travel(node) do |run_node|
|
|
69
80
|
run_in_travel(node, run_node)
|
|
70
81
|
end
|
|
@@ -76,26 +87,30 @@ module RuboCop
|
|
|
76
87
|
|
|
77
88
|
private
|
|
78
89
|
|
|
79
|
-
def run_in_travel(node,
|
|
80
|
-
around_node = extract_surrounding_around_block(
|
|
90
|
+
def run_in_travel(node, travel_node)
|
|
91
|
+
around_node = extract_surrounding_around_block(node)
|
|
81
92
|
return unless around_node
|
|
93
|
+
return unless matching_run_receiver?(node.body, around_node)
|
|
82
94
|
|
|
83
95
|
add_offense(node) do |corrector|
|
|
84
96
|
corrector.replace(node, node.body.source)
|
|
85
|
-
corrector.insert_before(
|
|
86
|
-
|
|
97
|
+
corrector.insert_before(
|
|
98
|
+
around_node,
|
|
99
|
+
"before { #{travel_node.source} }\n\n#{indent(around_node)}"
|
|
100
|
+
)
|
|
87
101
|
end
|
|
88
102
|
end
|
|
89
103
|
|
|
90
104
|
def travel_with_block_pass(node, lvar)
|
|
91
105
|
around_node = extract_surrounding_around_block(node)
|
|
92
106
|
return unless around_node
|
|
107
|
+
return unless lvar.name == around_argument_name(around_node)
|
|
93
108
|
|
|
94
109
|
add_offense(node) do |corrector|
|
|
95
110
|
corrector.replace(node, "#{lvar.name}.run")
|
|
96
111
|
corrector.insert_before(
|
|
97
112
|
around_node,
|
|
98
|
-
"before { #{node.method_name} }\n\n"
|
|
113
|
+
"before { #{node.method_name} }\n\n#{indent(around_node)}"
|
|
99
114
|
)
|
|
100
115
|
end
|
|
101
116
|
end
|
|
@@ -104,9 +119,23 @@ module RuboCop
|
|
|
104
119
|
# @return [RuboCop::AST::BlockNode, nil]
|
|
105
120
|
def extract_surrounding_around_block(node)
|
|
106
121
|
node.each_ancestor(:block).find do |ancestor|
|
|
107
|
-
match_around_each?(ancestor)
|
|
122
|
+
match_around_each?(ancestor) &&
|
|
123
|
+
direct_child_of_around_body?(node, ancestor)
|
|
108
124
|
end
|
|
109
125
|
end
|
|
126
|
+
|
|
127
|
+
def direct_child_of_around_body?(node, around_node)
|
|
128
|
+
node.parent == around_node ||
|
|
129
|
+
(node.parent.begin_type? && node.parent.parent == around_node)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def matching_run_receiver?(run_node, around_node)
|
|
133
|
+
run_node.receiver.children.first == around_argument_name(around_node)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def around_argument_name(around_node)
|
|
137
|
+
around_node.first_argument&.name
|
|
138
|
+
end
|
|
110
139
|
end
|
|
111
140
|
end
|
|
112
141
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `RSpecRails` department. The department's cops are
|
|
6
|
+
# registered for lazy loading and their files are loaded on demand.
|
|
7
|
+
module RSpecRails
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :AvoidSetupHook, "#{__dir__}/rspec_rails/avoid_setup_hook"
|
|
11
|
+
register_cop :FileFixture, "#{__dir__}/rspec_rails/file_fixture"
|
|
12
|
+
register_cop :HaveHttpStatus, "#{__dir__}/rspec_rails/have_http_status"
|
|
13
|
+
register_cop :HttpStatus, "#{__dir__}/rspec_rails/http_status"
|
|
14
|
+
register_cop :HttpStatusNameConsistency, "#{__dir__}/rspec_rails/http_status_name_consistency"
|
|
15
|
+
register_cop :InferredSpecType, "#{__dir__}/rspec_rails/inferred_spec_type"
|
|
16
|
+
register_cop :MinitestAssertions, "#{__dir__}/rspec_rails/minitest_assertions"
|
|
17
|
+
register_cop :NegationBeValid, "#{__dir__}/rspec_rails/negation_be_valid"
|
|
18
|
+
register_cop :ReceivePerformLater, "#{__dir__}/rspec_rails/receive_perform_later"
|
|
19
|
+
register_cop :TravelAround, "#{__dir__}/rspec_rails/travel_around"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require_relative 'rspec_rails
|
|
7
|
-
require_relative 'rspec_rails/inferred_spec_type'
|
|
8
|
-
require_relative 'rspec_rails/minitest_assertions'
|
|
9
|
-
require_relative 'rspec_rails/negation_be_valid'
|
|
10
|
-
require_relative 'rspec_rails/travel_around'
|
|
3
|
+
# @deprecated This file is deprecated. Cops are registered for lazy loading in
|
|
4
|
+
# `rubocop/cop/rspec_rails`; this file is kept for compatibility with code
|
|
5
|
+
# that requires it directly.
|
|
6
|
+
require_relative 'rspec_rails'
|
data/lib/rubocop-rspec_rails.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-rspec_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Benjamin Quorning
|
|
@@ -32,20 +32,14 @@ dependencies:
|
|
|
32
32
|
requirements:
|
|
33
33
|
- - "~>"
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
|
-
version: '1.
|
|
36
|
-
- - ">="
|
|
37
|
-
- !ruby/object:Gem::Version
|
|
38
|
-
version: 1.72.1
|
|
35
|
+
version: '1.89'
|
|
39
36
|
type: :runtime
|
|
40
37
|
prerelease: false
|
|
41
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
39
|
requirements:
|
|
43
40
|
- - "~>"
|
|
44
41
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '1.
|
|
46
|
-
- - ">="
|
|
47
|
-
- !ruby/object:Gem::Version
|
|
48
|
-
version: 1.72.1
|
|
42
|
+
version: '1.89'
|
|
49
43
|
- !ruby/object:Gem::Dependency
|
|
50
44
|
name: rubocop-rspec
|
|
51
45
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -75,13 +69,16 @@ files:
|
|
|
75
69
|
- README.md
|
|
76
70
|
- config/default.yml
|
|
77
71
|
- lib/rubocop-rspec_rails.rb
|
|
72
|
+
- lib/rubocop/cop/rspec_rails.rb
|
|
78
73
|
- lib/rubocop/cop/rspec_rails/avoid_setup_hook.rb
|
|
74
|
+
- lib/rubocop/cop/rspec_rails/file_fixture.rb
|
|
79
75
|
- lib/rubocop/cop/rspec_rails/have_http_status.rb
|
|
80
76
|
- lib/rubocop/cop/rspec_rails/http_status.rb
|
|
81
77
|
- lib/rubocop/cop/rspec_rails/http_status_name_consistency.rb
|
|
82
78
|
- lib/rubocop/cop/rspec_rails/inferred_spec_type.rb
|
|
83
79
|
- lib/rubocop/cop/rspec_rails/minitest_assertions.rb
|
|
84
80
|
- lib/rubocop/cop/rspec_rails/negation_be_valid.rb
|
|
81
|
+
- lib/rubocop/cop/rspec_rails/receive_perform_later.rb
|
|
85
82
|
- lib/rubocop/cop/rspec_rails/travel_around.rb
|
|
86
83
|
- lib/rubocop/cop/rspec_rails_cops.rb
|
|
87
84
|
- lib/rubocop/rspec_rails/config_formatter.rb
|
|
@@ -111,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
111
108
|
- !ruby/object:Gem::Version
|
|
112
109
|
version: '0'
|
|
113
110
|
requirements: []
|
|
114
|
-
rubygems_version:
|
|
111
|
+
rubygems_version: 4.0.20
|
|
115
112
|
specification_version: 4
|
|
116
113
|
summary: Code style checking for RSpec Rails files
|
|
117
114
|
test_files: []
|