rubocop-rspec_rails 2.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +58 -0
- data/CODE_OF_CONDUCT.md +17 -0
- data/MIT-LICENSE.md +21 -0
- data/README.md +88 -0
- data/config/default.yml +83 -0
- data/lib/rubocop/cop/rspec_rails/avoid_setup_hook.rb +41 -0
- data/lib/rubocop/cop/rspec_rails/have_http_status.rb +77 -0
- data/lib/rubocop/cop/rspec_rails/http_status.rb +212 -0
- data/lib/rubocop/cop/rspec_rails/inferred_spec_type.rb +143 -0
- data/lib/rubocop/cop/rspec_rails/minitest_assertions.rb +350 -0
- data/lib/rubocop/cop/rspec_rails/negation_be_valid.rb +98 -0
- data/lib/rubocop/cop/rspec_rails/travel_around.rb +90 -0
- data/lib/rubocop/cop/rspec_rails_cops.rb +13 -0
- data/lib/rubocop/rspec_rails/config_formatter.rb +55 -0
- data/lib/rubocop/rspec_rails/description_extractor.rb +72 -0
- data/lib/rubocop/rspec_rails/version.rb +10 -0
- data/lib/rubocop-rspec_rails.rb +14 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 53524a3483350f80d17572e3e221a777dabb1421999fbbcb37ad30cb182358b1
|
4
|
+
data.tar.gz: af8ff0697f66b78c197fc34e8dbd207e75a64473075ccb1b65d00650582d80d5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b42314769301898bd8723c2c8d5e29eee29cb1952b70e3e4abb892f75e8a13c2129933a3a99de8247510030f208925ab70c5a0a1f1ebaf950595a5c7d540152
|
7
|
+
data.tar.gz: 9ad058ef82208bb685249e7ed847da8fc4b06b6718033d9153adc6544d28845e91454524531a72f67f25497325876ed140d543e7173b839f72d12044ef37c429
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## Master (Unreleased)
|
4
|
+
|
5
|
+
## 2.28.0 (2024-03-28)
|
6
|
+
|
7
|
+
- Extracted from `rubocop-rspec` into a separate repository. ([@ydah])
|
8
|
+
|
9
|
+
## Previously (see [rubocop-rspec's changelist](https://github.com/rubocop/rubocop-rspec/blob/v2.27.1/CHANGELOG.md) for details)
|
10
|
+
|
11
|
+
- Add support for `assert_true`, `assert_false`, `assert_not_equal`, `assert_not_nil`, `*_empty`, `*_predicate`, `*_kind_of`, `*_in_delta`, `*_match`, `*_instance_of` and `*_includes` assertions in `RSpec/Rails/MinitestAssertions`. ([@ydah], [@G-Rath])
|
12
|
+
- Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
|
13
|
+
- Add support single quoted string and percent string and heredoc for `RSpec/Rails/HttpStatus`. ([@ydah])
|
14
|
+
- Add support `RSpec/Rails/HttpStatus` when `have_http_status` with string argument. ([@ydah])
|
15
|
+
- Mark to `Safe: false` for `RSpec/Rails/NegationBeValid` cop. ([@ydah])
|
16
|
+
- Add new `RSpec/Rails/NegationBeValid` cop. ([@ydah])
|
17
|
+
- Fix a false negative for `RSpec/ExcessiveDocstringSpacing` when finds description with em space. ([@ydah])
|
18
|
+
- Fix a false positive for `RSpec/EmptyExampleGroup` when example group with examples defined in `if` branch inside iterator. ([@ydah])
|
19
|
+
- Update the message output of `RSpec/ExpectActual` to include the word 'value'. ([@corydiamand])
|
20
|
+
- Fix a false negative for `RSpec/Pending` when `it` without body. ([@ydah])
|
21
|
+
- Add new `RSpec/ReceiveMessages` cop. ([@ydah])
|
22
|
+
- Change default.yml path to use `**/spec/*` instead of `spec/*`. ([@ydah])
|
23
|
+
- Add `AllowedIdentifiers` and `AllowedPatterns` configuration option to `RSpec/IndexedLet`. ([@ydah])
|
24
|
+
- Fix `RSpec/NamedSubject` when block has no body. ([@splattael])
|
25
|
+
- Fix `RSpec/LetBeforeExamples` autocorrect incompatible with `RSpec/ScatteredLet` autocorrect. ([@ydah])
|
26
|
+
- Update `RSpec/Focus` to support `shared_context` and `shared_examples`. ([@tmaier])
|
27
|
+
- Fix an error for `RSpec/Rails/HaveHttpStatus` with comparison with strings containing non-numeric characters. ([@ydah])
|
28
|
+
- Add support `be_status` style for `RSpec/Rails/HttpStatus`. ([@ydah])
|
29
|
+
- Fix order of expected and actual in correction for `RSpec/Rails/MinitestAssertions`. ([@mvz])
|
30
|
+
- Add `RSpec/Rails/TravelAround` cop. ([@r7kamura])
|
31
|
+
- Add new `RSpec/Rails/MinitestAssertions` cop. ([@ydah])
|
32
|
+
- Improved processing speed for `RSpec/Be`, `RSpec/ExpectActual`, `RSpec/ImplicitExpect`, `RSpec/MessageSpies`, `RSpec/PredicateMatcher` and `RSpec/Rails/HaveHttpStatus`. ([@ydah])
|
33
|
+
- Fix an error for `RSpec/Rails/InferredSpecType` with redundant type before other Hash metadata. ([@ydah])
|
34
|
+
- Add `RSpec/Rails/InferredSpecType` cop. ([@r7kamura])
|
35
|
+
- Add new `RSpec/Rails/HaveHttpStatus` cop. ([@akiomik])
|
36
|
+
- Exclude unrelated Rails directories from `RSpec/DescribeClass`. ([@MothOnMars])
|
37
|
+
- Add `RSpec/Rails/AvoidSetupHook` cop. ([@paydaylight])
|
38
|
+
- Change namespace of several cops (`Capybara/*` -> `RSpec/Capybara/*`, `FactoryBot/*` -> `RSpec/FactoryBot/*`, `Rails/*` -> `RSpec/Rails/*`). ([@pirj], [@bquorning])
|
39
|
+
- The `Rails/HttpStatus` cop is unavailable if the `rack` gem cannot be loaded. ([@bquorning])
|
40
|
+
- Fix `Rails/HttpStatus` not working with custom HTTP status codes. ([@bquorning])
|
41
|
+
- Add `RSpec/Rails/HttpStatus` cop to enforce consistent usage of the status format (numeric or symbolic). ([@anthony-robin], [@jojos003])
|
42
|
+
|
43
|
+
<!-- Contributors (alphabetically) -->
|
44
|
+
|
45
|
+
[@akiomik]: https://github.com/akiomik
|
46
|
+
[@anthony-robin]: https://github.com/anthony-robin
|
47
|
+
[@bquorning]: https://github.com/bquorning
|
48
|
+
[@corydiamand]: https://github.com/corydiamand
|
49
|
+
[@g-rath]: https://github.com/G-Rath
|
50
|
+
[@jojos003]: https://github.com/jojos003
|
51
|
+
[@mothonmars]: https://github.com/MothOnMars
|
52
|
+
[@mvz]: https://github.com/mvz
|
53
|
+
[@paydaylight]: https://github.com/paydaylight
|
54
|
+
[@pirj]: https://github.com/pirj
|
55
|
+
[@r7kamura]: https://github.com/r7kamura
|
56
|
+
[@splattael]: https://github.com/splattael
|
57
|
+
[@tmaier]: https://github.com/tmaier
|
58
|
+
[@ydah]: https://github.com/ydah
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# The RuboCop Community Code of Conduct
|
2
|
+
|
3
|
+
**Note:** We have picked the following code of conduct based on [Ruby's own
|
4
|
+
code of conduct](https://www.ruby-lang.org/en/conduct/).
|
5
|
+
|
6
|
+
This document provides a few simple community guidelines for a safe, respectful,
|
7
|
+
productive, and collaborative place for any person who is willing to contribute
|
8
|
+
to the RuboCop community. It applies to all "collaborative spaces", which are
|
9
|
+
defined as community communications channels (such as mailing lists, submitted
|
10
|
+
patches, commit comments, etc.).
|
11
|
+
|
12
|
+
- Participants will be tolerant of opposing views.
|
13
|
+
- Participants must ensure that their language and actions are free of personal
|
14
|
+
attacks and disparaging personal remarks.
|
15
|
+
- When interpreting the words and actions of others, participants should always
|
16
|
+
assume good intentions.
|
17
|
+
- Behaviour which can be reasonably considered harassment will not be tolerated.
|
data/MIT-LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Ian MacLeod <ian@nevir.net>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# RuboCop RSpec Rails
|
2
|
+
|
3
|
+
[![Join the chat at https://gitter.im/rubocop-rspec/Lobby](https://badges.gitter.im/rubocop-rspec/Lobby.svg)](https://gitter.im/rubocop-rspec/Lobby)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/rubocop-rspec_rails.svg)](https://rubygems.org/gems/rubocop-rspec_rails)
|
5
|
+
![CI](https://github.com/rubocop/rubocop-rspec_rails/workflows/CI/badge.svg)
|
6
|
+
|
7
|
+
[RSpec Rails](https://rspec.info/)-specific analysis for your projects, as an extension to
|
8
|
+
[RuboCop](https://github.com/rubocop/rubocop).
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Just install the `rubocop-rspec_rails` gem
|
13
|
+
|
14
|
+
```bash
|
15
|
+
gem install rubocop-rspec_rails
|
16
|
+
```
|
17
|
+
|
18
|
+
or if you use bundler put this in your `Gemfile`
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'rubocop-rspec_rails', require: false
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
You need to tell RuboCop to load the RSpec Rails extension. There are three
|
27
|
+
ways to do this:
|
28
|
+
|
29
|
+
### RuboCop configuration file
|
30
|
+
|
31
|
+
Put this into your `.rubocop.yml`.
|
32
|
+
|
33
|
+
```yaml
|
34
|
+
require: rubocop-rspec_rails
|
35
|
+
```
|
36
|
+
|
37
|
+
Alternatively, use the following array notation when specifying multiple extensions.
|
38
|
+
|
39
|
+
```yaml
|
40
|
+
require:
|
41
|
+
- rubocop-rspec
|
42
|
+
- rubocop-rspec_rails
|
43
|
+
```
|
44
|
+
|
45
|
+
Now you can run `rubocop` and it will automatically load the RuboCop RSpec Rails
|
46
|
+
cops together with the standard cops.
|
47
|
+
|
48
|
+
### Command line
|
49
|
+
|
50
|
+
```bash
|
51
|
+
rubocop --require rubocop-rspec_rails
|
52
|
+
```
|
53
|
+
|
54
|
+
### Rake task
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
RuboCop::RakeTask.new do |task|
|
58
|
+
task.requires << 'rubocop-rspec_rails'
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
## Documentation
|
63
|
+
|
64
|
+
You can read more about RuboCop RSpec Rails in its [official manual](https://docs.rubocop.org/rubocop-rspec_rails).
|
65
|
+
|
66
|
+
## The Cops
|
67
|
+
|
68
|
+
All cops are located under
|
69
|
+
[`lib/rubocop/cop/rspec_rails`](lib/rubocop/cop/rspec_rails), and contain
|
70
|
+
examples/documentation.
|
71
|
+
|
72
|
+
In your `.rubocop.yml`, you may treat the RSpec Rails cops just like any other
|
73
|
+
cop. For example:
|
74
|
+
|
75
|
+
```yaml
|
76
|
+
RSpecRails/AvoidSetupHook:
|
77
|
+
Exclude:
|
78
|
+
- spec/my_poorly_named_spec_file.rb
|
79
|
+
```
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
Checkout the [contribution guidelines](.github/CONTRIBUTING.md).
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
`rubocop-rspec_rails` is MIT licensed. [See the accompanying file](MIT-LICENSE.md) for
|
88
|
+
the full text.
|
data/config/default.yml
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
---
|
2
|
+
RSpecRails:
|
3
|
+
Enabled: true
|
4
|
+
DocumentationBaseURL: https://docs.rubocop.org/rubocop-rspec_rails
|
5
|
+
Include:
|
6
|
+
- "**/*_spec.rb"
|
7
|
+
- "**/spec/**/*"
|
8
|
+
|
9
|
+
RSpecRails/AvoidSetupHook:
|
10
|
+
Description: Checks that tests use RSpec `before` hook over Rails `setup` method.
|
11
|
+
Enabled: pending
|
12
|
+
VersionAdded: '2.4'
|
13
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/AvoidSetupHook
|
14
|
+
|
15
|
+
RSpecRails/HaveHttpStatus:
|
16
|
+
Description: Checks that tests use `have_http_status` instead of equality matchers.
|
17
|
+
Enabled: pending
|
18
|
+
ResponseMethods:
|
19
|
+
- response
|
20
|
+
- last_response
|
21
|
+
SafeAutoCorrect: false
|
22
|
+
VersionAdded: '2.12'
|
23
|
+
VersionChanged: '2.27'
|
24
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/HaveHttpStatus
|
25
|
+
|
26
|
+
RSpecRails/HttpStatus:
|
27
|
+
Description: Enforces use of symbolic or numeric value to describe HTTP status.
|
28
|
+
Enabled: true
|
29
|
+
EnforcedStyle: symbolic
|
30
|
+
SupportedStyles:
|
31
|
+
- numeric
|
32
|
+
- symbolic
|
33
|
+
- be_status
|
34
|
+
VersionAdded: '1.23'
|
35
|
+
VersionChanged: '2.20'
|
36
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/HttpStatus
|
37
|
+
|
38
|
+
RSpecRails/InferredSpecType:
|
39
|
+
Description: Identifies redundant spec type.
|
40
|
+
Enabled: pending
|
41
|
+
Safe: false
|
42
|
+
VersionAdded: '2.14'
|
43
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/InferredSpecType
|
44
|
+
Inferences:
|
45
|
+
channels: channel
|
46
|
+
controllers: controller
|
47
|
+
features: feature
|
48
|
+
generator: generator
|
49
|
+
helpers: helper
|
50
|
+
jobs: job
|
51
|
+
mailboxes: mailbox
|
52
|
+
mailers: mailer
|
53
|
+
models: model
|
54
|
+
requests: request
|
55
|
+
integration: request
|
56
|
+
api: request
|
57
|
+
routing: routing
|
58
|
+
system: system
|
59
|
+
views: view
|
60
|
+
|
61
|
+
RSpecRails/MinitestAssertions:
|
62
|
+
Description: Check if using Minitest-like matchers.
|
63
|
+
Enabled: pending
|
64
|
+
VersionAdded: '2.17'
|
65
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/MinitestAssertions
|
66
|
+
|
67
|
+
RSpecRails/NegationBeValid:
|
68
|
+
Description: Enforces use of `be_invalid` or `not_to` for negated be_valid.
|
69
|
+
Safe: false
|
70
|
+
EnforcedStyle: not_to
|
71
|
+
SupportedStyles:
|
72
|
+
- not_to
|
73
|
+
- be_invalid
|
74
|
+
Enabled: pending
|
75
|
+
VersionAdded: '2.23'
|
76
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/NegationBeValid
|
77
|
+
|
78
|
+
RSpecRails/TravelAround:
|
79
|
+
Description: Prefer to travel in `before` rather than `around`.
|
80
|
+
Enabled: pending
|
81
|
+
Safe: false
|
82
|
+
VersionAdded: '2.19'
|
83
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec_rails/RuboCop/Cop/RSpecRails/TravelAround
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpecRails
|
6
|
+
# Checks that tests use RSpec `before` hook over Rails `setup` method.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# setup do
|
11
|
+
# allow(foo).to receive(:bar)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# before do
|
16
|
+
# allow(foo).to receive(:bar)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
class AvoidSetupHook < ::RuboCop::Cop::RSpec::Base
|
20
|
+
extend AutoCorrector
|
21
|
+
|
22
|
+
MSG = 'Use `before` instead of `setup`.'
|
23
|
+
|
24
|
+
# @!method setup_call(node)
|
25
|
+
def_node_matcher :setup_call, <<~PATTERN
|
26
|
+
(block
|
27
|
+
$(send nil? :setup)
|
28
|
+
(args) _)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
32
|
+
setup_call(node) do |setup|
|
33
|
+
add_offense(node) do |corrector|
|
34
|
+
corrector.replace setup, 'before'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpecRails
|
6
|
+
# Checks that tests use `have_http_status` instead of equality matchers.
|
7
|
+
#
|
8
|
+
# @example ResponseMethods: ['response', 'last_response'] (default)
|
9
|
+
# # bad
|
10
|
+
# expect(response.status).to be(200)
|
11
|
+
# expect(last_response.code).to eq("200")
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# expect(response).to have_http_status(200)
|
15
|
+
# expect(last_response).to have_http_status(200)
|
16
|
+
#
|
17
|
+
# @example ResponseMethods: ['foo_response']
|
18
|
+
# # bad
|
19
|
+
# expect(foo_response.status).to be(200)
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# expect(foo_response).to have_http_status(200)
|
23
|
+
#
|
24
|
+
# # also good
|
25
|
+
# expect(response).to have_http_status(200)
|
26
|
+
# expect(last_response).to have_http_status(200)
|
27
|
+
#
|
28
|
+
class HaveHttpStatus < ::RuboCop::Cop::Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
MSG =
|
32
|
+
'Prefer `expect(%<response>s).%<to>s ' \
|
33
|
+
'have_http_status(%<status>s)` over `%<bad_code>s`.'
|
34
|
+
|
35
|
+
RUNNERS = %i[to to_not not_to].to_set
|
36
|
+
RESTRICT_ON_SEND = RUNNERS
|
37
|
+
|
38
|
+
# @!method match_status(node)
|
39
|
+
def_node_matcher :match_status, <<~PATTERN
|
40
|
+
(send
|
41
|
+
(send nil? :expect
|
42
|
+
$(send $(send nil? #response_methods?) {:status :code})
|
43
|
+
)
|
44
|
+
$RUNNERS
|
45
|
+
$(send nil? {:be :eq :eql :equal} ({int str} $_))
|
46
|
+
)
|
47
|
+
PATTERN
|
48
|
+
|
49
|
+
def on_send(node) # rubocop:disable Metrics/MethodLength
|
50
|
+
match_status(node) do
|
51
|
+
|response_status, response_method, to, match, status|
|
52
|
+
return unless status.to_s.match?(/\A\d+\z/)
|
53
|
+
|
54
|
+
message = format(MSG, response: response_method.method_name,
|
55
|
+
to: to, status: status,
|
56
|
+
bad_code: node.source)
|
57
|
+
add_offense(node, message: message) do |corrector|
|
58
|
+
corrector.replace(response_status, response_method.method_name)
|
59
|
+
corrector.replace(match.loc.selector, 'have_http_status')
|
60
|
+
corrector.replace(match.first_argument, status.to_s)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def response_methods?(name)
|
68
|
+
response_methods.include?(name.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
def response_methods
|
72
|
+
cop_config.fetch('ResponseMethods', [])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/utils'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module RSpecRails
|
8
|
+
# Enforces use of symbolic or numeric value to describe HTTP status.
|
9
|
+
#
|
10
|
+
# This cop inspects only `have_http_status` calls.
|
11
|
+
# So, this cop does not check if a method starting with `be_*` is used
|
12
|
+
# when setting for `EnforcedStyle: symbolic` or
|
13
|
+
# `EnforcedStyle: numeric`.
|
14
|
+
#
|
15
|
+
# @example `EnforcedStyle: symbolic` (default)
|
16
|
+
# # bad
|
17
|
+
# it { is_expected.to have_http_status 200 }
|
18
|
+
# it { is_expected.to have_http_status 404 }
|
19
|
+
# it { is_expected.to have_http_status "403" }
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# it { is_expected.to have_http_status :ok }
|
23
|
+
# it { is_expected.to have_http_status :not_found }
|
24
|
+
# it { is_expected.to have_http_status :forbidden }
|
25
|
+
# it { is_expected.to have_http_status :success }
|
26
|
+
# it { is_expected.to have_http_status :error }
|
27
|
+
#
|
28
|
+
# @example `EnforcedStyle: numeric`
|
29
|
+
# # bad
|
30
|
+
# it { is_expected.to have_http_status :ok }
|
31
|
+
# it { is_expected.to have_http_status :not_found }
|
32
|
+
# it { is_expected.to have_http_status "forbidden" }
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# it { is_expected.to have_http_status 200 }
|
36
|
+
# it { is_expected.to have_http_status 404 }
|
37
|
+
# it { is_expected.to have_http_status 403 }
|
38
|
+
# it { is_expected.to have_http_status :success }
|
39
|
+
# it { is_expected.to have_http_status :error }
|
40
|
+
#
|
41
|
+
# @example `EnforcedStyle: be_status`
|
42
|
+
# # bad
|
43
|
+
# it { is_expected.to have_http_status :ok }
|
44
|
+
# it { is_expected.to have_http_status :not_found }
|
45
|
+
# it { is_expected.to have_http_status "forbidden" }
|
46
|
+
# it { is_expected.to have_http_status 200 }
|
47
|
+
# it { is_expected.to have_http_status 404 }
|
48
|
+
# it { is_expected.to have_http_status "403" }
|
49
|
+
#
|
50
|
+
# # good
|
51
|
+
# it { is_expected.to be_ok }
|
52
|
+
# it { is_expected.to be_not_found }
|
53
|
+
# it { is_expected.to have_http_status :success }
|
54
|
+
# it { is_expected.to have_http_status :error }
|
55
|
+
#
|
56
|
+
class HttpStatus < ::RuboCop::Cop::RSpec::Base
|
57
|
+
extend AutoCorrector
|
58
|
+
include ConfigurableEnforcedStyle
|
59
|
+
RESTRICT_ON_SEND = %i[have_http_status].freeze
|
60
|
+
|
61
|
+
# @!method http_status(node)
|
62
|
+
def_node_matcher :http_status, <<~PATTERN
|
63
|
+
(send nil? :have_http_status ${int sym str})
|
64
|
+
PATTERN
|
65
|
+
|
66
|
+
def on_send(node)
|
67
|
+
http_status(node) do |arg|
|
68
|
+
return if arg.str_type? && arg.heredoc?
|
69
|
+
|
70
|
+
checker = checker_class.new(arg)
|
71
|
+
return unless checker.offensive?
|
72
|
+
|
73
|
+
add_offense(checker.offense_range,
|
74
|
+
message: checker.message) do |corrector|
|
75
|
+
corrector.replace(checker.offense_range, checker.prefer)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def checker_class
|
83
|
+
case style
|
84
|
+
when :symbolic
|
85
|
+
SymbolicStyleChecker
|
86
|
+
when :numeric
|
87
|
+
NumericStyleChecker
|
88
|
+
when :be_status
|
89
|
+
BeStatusStyleChecker
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# :nodoc:
|
94
|
+
class StyleCheckerBase
|
95
|
+
MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
|
96
|
+
'to describe HTTP status code.'
|
97
|
+
ALLOWED_STATUSES = %i[error success missing redirect].freeze
|
98
|
+
|
99
|
+
attr_reader :node
|
100
|
+
|
101
|
+
def initialize(node)
|
102
|
+
@node = node
|
103
|
+
end
|
104
|
+
|
105
|
+
def message
|
106
|
+
format(MSG, prefer: prefer, current: current)
|
107
|
+
end
|
108
|
+
|
109
|
+
def current
|
110
|
+
offense_range.source
|
111
|
+
end
|
112
|
+
|
113
|
+
def offense_range
|
114
|
+
node
|
115
|
+
end
|
116
|
+
|
117
|
+
def allowed_symbol?
|
118
|
+
node.sym_type? && ALLOWED_STATUSES.include?(node.value)
|
119
|
+
end
|
120
|
+
|
121
|
+
def custom_http_status_code?
|
122
|
+
node.int_type? &&
|
123
|
+
!::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# :nodoc:
|
128
|
+
class SymbolicStyleChecker < StyleCheckerBase
|
129
|
+
def offensive?
|
130
|
+
!node.sym_type? && !custom_http_status_code?
|
131
|
+
end
|
132
|
+
|
133
|
+
def prefer
|
134
|
+
symbol.inspect
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def symbol
|
140
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
141
|
+
end
|
142
|
+
|
143
|
+
def number
|
144
|
+
node.value.to_i
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# :nodoc:
|
149
|
+
class NumericStyleChecker < StyleCheckerBase
|
150
|
+
def offensive?
|
151
|
+
!node.int_type? && !allowed_symbol?
|
152
|
+
end
|
153
|
+
|
154
|
+
def prefer
|
155
|
+
number.to_s
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def symbol
|
161
|
+
node.value
|
162
|
+
end
|
163
|
+
|
164
|
+
def number
|
165
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol.to_sym]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# :nodoc:
|
170
|
+
class BeStatusStyleChecker < StyleCheckerBase
|
171
|
+
def offensive?
|
172
|
+
(!node.sym_type? && !custom_http_status_code?) ||
|
173
|
+
(!node.int_type? && !allowed_symbol?)
|
174
|
+
end
|
175
|
+
|
176
|
+
def offense_range
|
177
|
+
node.parent
|
178
|
+
end
|
179
|
+
|
180
|
+
def prefer
|
181
|
+
if node.sym_type?
|
182
|
+
"be_#{node.value}"
|
183
|
+
elsif node.int_type?
|
184
|
+
"be_#{symbol}"
|
185
|
+
elsif node.str_type?
|
186
|
+
"be_#{normalize_str}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def symbol
|
193
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
194
|
+
end
|
195
|
+
|
196
|
+
def number
|
197
|
+
node.value.to_i
|
198
|
+
end
|
199
|
+
|
200
|
+
def normalize_str
|
201
|
+
str = node.value.to_s
|
202
|
+
if str.match?(/\A\d+\z/)
|
203
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(str.to_i)
|
204
|
+
else
|
205
|
+
str
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|