rubocop-rspec 2.23.2 → 2.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/config/default.yml +49 -2
- data/config/obsoletion.yml +6 -0
- data/lib/rubocop/cop/rspec/duplicated_metadata.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/excessive_docstring_spacing.rb +3 -1
- data/lib/rubocop/cop/rspec/focus.rb +2 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +197 -0
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
- data/lib/rubocop/cop/rspec/rails/http_status.rb +22 -5
- data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +6 -0
- data/lib/rubocop/cop/rspec/receive_messages.rb +1 -1
- data/lib/rubocop/cop/rspec/sort_metadata.rb +2 -1
- 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/variable_definition.rb +2 -2
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +5 -5
- data/lib/rubocop/cop/rspec_cops.rb +5 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +1 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cd039667fb1c9cab8a794b4d4d028a1acdef5cc9033aa37fd37b8ddc6fa00c0
|
4
|
+
data.tar.gz: c80d4491b4e9d124346195d38f73a3b13d251a4259fa459783d6dbb471ed9925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eccda68a017df50004a38d6e55c6fbabeef13fa20aa37941aec18042adcace438c5cdd6ef821daed30cd17cb2884af79bf2a882f7da875e7190ed437c2e061b9
|
7
|
+
data.tar.gz: '0832c87dd57fba0540c7d624b6bf5e38046b8a9bdc7ee82ee1c1a065a7d4bd6ba3f7949da6a9592d01de3ad8406bd8cc8433d309717cd5993314eaeecac076bb'
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
## Master (Unreleased)
|
4
4
|
|
5
|
+
## 2.24.0 (2023-09-08)
|
6
|
+
|
7
|
+
- Split `RSpec/FilePath` into `RSpec/SpecFilePathSuffix` and `RSpec/SpecFilePathFormat`. `RSpec/FilePath` cop is enabled by default, the two new cops are pending and need to be enabled explicitly. ([@ydah])
|
8
|
+
- Add new `RSpec/Eq` cop. ([@ydah])
|
9
|
+
- Add `RSpec/MetadataStyle` and `RSpec/EmptyMetadata` cops. ([@r7kamura])
|
10
|
+
- Add support `RSpec/Rails/HttpStatus` when `have_http_status` with string argument. ([@ydah])
|
11
|
+
- Fix an infinite loop error when `RSpec/ExcessiveDocstringSpacing` finds a description with non-ASCII leading/trailing whitespace. ([@bcgraham])
|
12
|
+
- Fix an incorrect autocorrect for `RSpec/ReceiveMessages` when return values declared between stubs. ([@marocchino])
|
13
|
+
- Fix a false positive `RSpec/Focus` when chained method call and inside define method. ([@ydah])
|
14
|
+
|
5
15
|
## 2.23.2 (2023-08-09)
|
6
16
|
|
7
17
|
- Fix an incorrect autocorrect for `RSpec/ReceiveMessages` when method is only non-word character. ([@marocchino])
|
@@ -797,6 +807,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
797
807
|
[@aried3r]: https://github.com/aried3r
|
798
808
|
[@baberthal]: https://github.com/baberthal
|
799
809
|
[@backus]: https://github.com/backus
|
810
|
+
[@bcgraham]: https://github.com/bcgraham
|
800
811
|
[@biinari]: https://github.com/biinari
|
801
812
|
[@bmorrall]: https://github.com/bmorrall
|
802
813
|
[@bquorning]: https://github.com/bquorning
|
data/config/default.yml
CHANGED
@@ -359,6 +359,18 @@ RSpec/EmptyLineAfterSubject:
|
|
359
359
|
StyleGuide: https://rspec.rubystyle.guide/#empty-line-after-let
|
360
360
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterSubject
|
361
361
|
|
362
|
+
RSpec/EmptyMetadata:
|
363
|
+
Description: Avoid empty metadata hash.
|
364
|
+
Enabled: pending
|
365
|
+
VersionAdded: '2.24'
|
366
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyMetadata
|
367
|
+
|
368
|
+
RSpec/Eq:
|
369
|
+
Description: Use `eq` instead of `be ==` to compare objects.
|
370
|
+
Enabled: pending
|
371
|
+
VersionAdded: '2.24'
|
372
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Eq
|
373
|
+
|
362
374
|
RSpec/ExampleLength:
|
363
375
|
Description: Checks for long examples.
|
364
376
|
Enabled: true
|
@@ -436,7 +448,7 @@ RSpec/ExpectOutput:
|
|
436
448
|
|
437
449
|
RSpec/FilePath:
|
438
450
|
Description: Checks that spec file paths are consistent and well-formed.
|
439
|
-
Enabled:
|
451
|
+
Enabled: false
|
440
452
|
Include:
|
441
453
|
- "**/*_spec*rb*"
|
442
454
|
- "**/spec/**/*"
|
@@ -446,7 +458,7 @@ RSpec/FilePath:
|
|
446
458
|
IgnoreMethods: false
|
447
459
|
SpecSuffixOnly: false
|
448
460
|
VersionAdded: '1.2'
|
449
|
-
VersionChanged: '
|
461
|
+
VersionChanged: '2.24'
|
450
462
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FilePath
|
451
463
|
|
452
464
|
RSpec/Focus:
|
@@ -613,6 +625,16 @@ RSpec/MessageSpies:
|
|
613
625
|
VersionAdded: '1.9'
|
614
626
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MessageSpies
|
615
627
|
|
628
|
+
RSpec/MetadataStyle:
|
629
|
+
Description: Use consistent metadata style.
|
630
|
+
Enabled: pending
|
631
|
+
EnforcedStyle: symbol
|
632
|
+
SupportedStyles:
|
633
|
+
- hash
|
634
|
+
- symbol
|
635
|
+
VersionAdded: '2.24'
|
636
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MetadataStyle
|
637
|
+
|
616
638
|
RSpec/MissingExampleGroupArgument:
|
617
639
|
Description: Checks that the first argument to an example group is not empty.
|
618
640
|
Enabled: true
|
@@ -834,6 +856,31 @@ RSpec/SortMetadata:
|
|
834
856
|
VersionAdded: '2.14'
|
835
857
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SortMetadata
|
836
858
|
|
859
|
+
RSpec/SpecFilePathFormat:
|
860
|
+
Description: Checks that spec file paths are consistent and well-formed.
|
861
|
+
Enabled: pending
|
862
|
+
Include:
|
863
|
+
- "**/*_spec.rb"
|
864
|
+
Exclude:
|
865
|
+
- "**/spec/routing/**/*"
|
866
|
+
CustomTransform:
|
867
|
+
RuboCop: rubocop
|
868
|
+
RSpec: rspec
|
869
|
+
IgnoreMethods: false
|
870
|
+
IgnoreMetadata:
|
871
|
+
type: routing
|
872
|
+
VersionAdded: '2.24'
|
873
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat
|
874
|
+
|
875
|
+
RSpec/SpecFilePathSuffix:
|
876
|
+
Description: Checks that spec file paths suffix are consistent and well-formed.
|
877
|
+
Enabled: pending
|
878
|
+
VersionAdded: '2.24'
|
879
|
+
Include:
|
880
|
+
- "**/*_spec*rb*"
|
881
|
+
- "**/spec/**/*"
|
882
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathSuffix
|
883
|
+
|
837
884
|
RSpec/StubbedMock:
|
838
885
|
Description: Checks that message expectations do not have a configured response.
|
839
886
|
Enabled: true
|
data/config/obsoletion.yml
CHANGED
@@ -27,3 +27,9 @@ renamed:
|
|
27
27
|
RSpec/FactoryBot/FactoryClassName: FactoryBot/FactoryClassName
|
28
28
|
RSpec/FactoryBot/FactoryNameStyle: FactoryBot/FactoryNameStyle
|
29
29
|
RSpec/FactoryBot/SyntaxMethods: FactoryBot/SyntaxMethods
|
30
|
+
|
31
|
+
split:
|
32
|
+
RSpec/FilePath:
|
33
|
+
alternatives:
|
34
|
+
- RSpec/SpecFilePathFormat
|
35
|
+
- RSpec/SpecFilePathSuffix
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Avoid empty metadata hash.
|
7
|
+
#
|
8
|
+
# @example EnforcedStyle: symbol (default)
|
9
|
+
# # bad
|
10
|
+
# describe 'Something', {}
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# describe 'Something'
|
14
|
+
class EmptyMetadata < Base
|
15
|
+
extend AutoCorrector
|
16
|
+
|
17
|
+
include Metadata
|
18
|
+
include RangeHelp
|
19
|
+
|
20
|
+
MSG = 'Avoid empty metadata hash.'
|
21
|
+
|
22
|
+
def on_metadata(_symbols, hash)
|
23
|
+
return unless hash&.pairs&.empty?
|
24
|
+
|
25
|
+
add_offense(hash) do |corrector|
|
26
|
+
remove_empty_metadata(corrector, hash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def remove_empty_metadata(corrector, node)
|
33
|
+
corrector.remove(
|
34
|
+
range_with_surrounding_comma(
|
35
|
+
range_with_surrounding_space(
|
36
|
+
node.source_range,
|
37
|
+
side: :left
|
38
|
+
),
|
39
|
+
:left
|
40
|
+
)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Use `eq` instead of `be ==` to compare objects.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# expect(foo).to be == 42
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# expect(foo).to eq 42
|
14
|
+
#
|
15
|
+
class Eq < Base
|
16
|
+
extend AutoCorrector
|
17
|
+
include RangeHelp
|
18
|
+
|
19
|
+
MSG = 'Use `eq` instead of `be ==` to compare objects.'
|
20
|
+
RESTRICT_ON_SEND = Runners.all
|
21
|
+
|
22
|
+
# @!method be_equals(node)
|
23
|
+
def_node_matcher :be_equals, <<~PATTERN
|
24
|
+
(send _ #Runners.all $(send (send nil? :be) :== _))
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
be_equals(node) do |matcher|
|
29
|
+
range = offense_range(matcher)
|
30
|
+
add_offense(range) do |corrector|
|
31
|
+
corrector.replace(range, 'eq')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def offense_range(matcher)
|
39
|
+
range_between(
|
40
|
+
matcher.source_range.begin_pos,
|
41
|
+
matcher.loc.selector.end_pos
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -66,7 +66,9 @@ module RuboCop
|
|
66
66
|
|
67
67
|
# @param text [String]
|
68
68
|
def strip_excessive_whitespace(text)
|
69
|
-
text
|
69
|
+
text
|
70
|
+
.gsub(/[[:blank:]]{2,}/, ' ')
|
71
|
+
.gsub(/\A[[:blank:]]|[[:blank:]]\z/, '')
|
70
72
|
end
|
71
73
|
|
72
74
|
# @param node [RuboCop::AST::Node]
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Use consistent metadata style.
|
7
|
+
#
|
8
|
+
# This cop does not support autocorrection in the case of
|
9
|
+
# `EnforcedStyle: hash` where the trailing metadata type is ambiguous.
|
10
|
+
# (e.g. `describe 'Something', :a, b`)
|
11
|
+
#
|
12
|
+
# @example EnforcedStyle: symbol (default)
|
13
|
+
# # bad
|
14
|
+
# describe 'Something', a: true
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# describe 'Something', :a
|
18
|
+
#
|
19
|
+
# @example EnforcedStyle: hash
|
20
|
+
# # bad
|
21
|
+
# describe 'Something', :a
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# describe 'Something', a: true
|
25
|
+
class MetadataStyle < Base # rubocop:disable Metrics/ClassLength
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
include ConfigurableEnforcedStyle
|
29
|
+
include Metadata
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
# @!method extract_metadata_hash(node)
|
33
|
+
def_node_matcher :extract_metadata_hash, <<~PATTERN
|
34
|
+
(send _ _ _ ... $hash)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
# @!method match_boolean_metadata_pair?(node)
|
38
|
+
def_node_matcher :match_boolean_metadata_pair?, <<~PATTERN
|
39
|
+
(pair sym true)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
# @!method match_ambiguous_trailing_metadata?(node)
|
43
|
+
def_node_matcher :match_ambiguous_trailing_metadata?, <<~PATTERN
|
44
|
+
(send _ _ _ ... !{hash sym})
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
def on_metadata(symbols, hash)
|
48
|
+
symbols.each do |symbol|
|
49
|
+
on_metadata_symbol(symbol)
|
50
|
+
end
|
51
|
+
|
52
|
+
return unless hash
|
53
|
+
|
54
|
+
hash.pairs.each do |pair|
|
55
|
+
on_metadata_pair(pair)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def autocorrect_pair(corrector, node)
|
62
|
+
remove_pair(corrector, node)
|
63
|
+
insert_symbol(corrector, node)
|
64
|
+
end
|
65
|
+
|
66
|
+
def autocorrect_symbol(corrector, node)
|
67
|
+
return if match_ambiguous_trailing_metadata?(node.parent)
|
68
|
+
|
69
|
+
remove_symbol(corrector, node)
|
70
|
+
insert_pair(corrector, node)
|
71
|
+
end
|
72
|
+
|
73
|
+
def bad_metadata_pair?(node)
|
74
|
+
style == :symbol && match_boolean_metadata_pair?(node)
|
75
|
+
end
|
76
|
+
|
77
|
+
def bad_metadata_symbol?(_node)
|
78
|
+
style == :hash
|
79
|
+
end
|
80
|
+
|
81
|
+
def format_symbol_to_pair_source(node)
|
82
|
+
"#{node.value}: true"
|
83
|
+
end
|
84
|
+
|
85
|
+
def insert_pair(corrector, node)
|
86
|
+
hash_node = extract_metadata_hash(node.parent)
|
87
|
+
if hash_node.nil?
|
88
|
+
insert_pair_as_last_argument(corrector, node)
|
89
|
+
elsif hash_node.pairs.any?
|
90
|
+
insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
|
91
|
+
else
|
92
|
+
insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def insert_pair_as_last_argument(corrector, node)
|
97
|
+
corrector.insert_before(
|
98
|
+
node.parent.location.end || node.parent.source_range.with(
|
99
|
+
begin_pos: node.parent.source_range.end_pos
|
100
|
+
),
|
101
|
+
", #{format_symbol_to_pair_source(node)}"
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
|
106
|
+
corrector.insert_after(
|
107
|
+
hash_node.location.begin,
|
108
|
+
" #{format_symbol_to_pair_source(node)} "
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
|
113
|
+
corrector.insert_after(
|
114
|
+
hash_node.children.last,
|
115
|
+
", #{format_symbol_to_pair_source(node)}"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
def insert_symbol(corrector, node)
|
120
|
+
corrector.insert_after(
|
121
|
+
node.parent.left_sibling,
|
122
|
+
", #{node.key.value.inspect}"
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def message_for_style
|
127
|
+
format(
|
128
|
+
'Use %<style>s style for metadata.',
|
129
|
+
style: style
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def on_metadata_pair(node)
|
134
|
+
return unless bad_metadata_pair?(node)
|
135
|
+
|
136
|
+
add_offense(node, message: message_for_style) do |corrector|
|
137
|
+
autocorrect_pair(corrector, node)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_metadata_symbol(node)
|
142
|
+
return unless bad_metadata_symbol?(node)
|
143
|
+
|
144
|
+
add_offense(node, message: message_for_style) do |corrector|
|
145
|
+
autocorrect_symbol(corrector, node)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def remove_pair(corrector, node)
|
150
|
+
if !node.parent.braces? || node.left_siblings.any?
|
151
|
+
remove_pair_following(corrector, node)
|
152
|
+
elsif node.right_siblings.any?
|
153
|
+
remove_pair_preceding(corrector, node)
|
154
|
+
else
|
155
|
+
corrector.remove(node)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def remove_pair_following(corrector, node)
|
160
|
+
corrector.remove(
|
161
|
+
range_with_surrounding_comma(
|
162
|
+
range_with_surrounding_space(
|
163
|
+
node.source_range,
|
164
|
+
side: :left
|
165
|
+
),
|
166
|
+
:left
|
167
|
+
)
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
def remove_pair_preceding(corrector, node)
|
172
|
+
corrector.remove(
|
173
|
+
range_with_surrounding_space(
|
174
|
+
range_with_surrounding_comma(
|
175
|
+
node.source_range,
|
176
|
+
:right
|
177
|
+
),
|
178
|
+
side: :right
|
179
|
+
)
|
180
|
+
)
|
181
|
+
end
|
182
|
+
|
183
|
+
def remove_symbol(corrector, node)
|
184
|
+
corrector.remove(
|
185
|
+
range_with_surrounding_comma(
|
186
|
+
range_with_surrounding_space(
|
187
|
+
node.source_range,
|
188
|
+
side: :left
|
189
|
+
),
|
190
|
+
:left
|
191
|
+
)
|
192
|
+
)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -13,7 +13,7 @@ module RuboCop
|
|
13
13
|
def_node_matcher :rspec_metadata, <<~PATTERN
|
14
14
|
(block
|
15
15
|
(send
|
16
|
-
#rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _
|
16
|
+
#rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ $...)
|
17
17
|
...)
|
18
18
|
PATTERN
|
19
19
|
|
@@ -24,25 +24,39 @@ module RuboCop
|
|
24
24
|
|
25
25
|
# @!method metadata_in_block(node)
|
26
26
|
def_node_search :metadata_in_block, <<~PATTERN
|
27
|
-
(send (lvar %) #Hooks.all _
|
27
|
+
(send (lvar %) #Hooks.all _ $...)
|
28
28
|
PATTERN
|
29
29
|
|
30
30
|
def on_block(node)
|
31
31
|
rspec_configure(node) do |block_var|
|
32
|
-
metadata_in_block(node, block_var) do |
|
33
|
-
|
32
|
+
metadata_in_block(node, block_var) do |metadata_arguments|
|
33
|
+
on_matadata_arguments(metadata_arguments)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
rspec_metadata(node) do |
|
38
|
-
|
37
|
+
rspec_metadata(node) do |metadata_arguments|
|
38
|
+
on_matadata_arguments(metadata_arguments)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
alias on_numblock on_block
|
42
42
|
|
43
|
-
def on_metadata(_symbols,
|
43
|
+
def on_metadata(_symbols, _hash)
|
44
44
|
raise ::NotImplementedError
|
45
45
|
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def on_matadata_arguments(metadata_arguments)
|
50
|
+
*symbols, last = metadata_arguments
|
51
|
+
hash = nil
|
52
|
+
case last&.type
|
53
|
+
when :hash
|
54
|
+
hash = last
|
55
|
+
when :sym
|
56
|
+
symbols << last
|
57
|
+
end
|
58
|
+
on_metadata(symbols, hash)
|
59
|
+
end
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
@@ -17,10 +17,12 @@ module RuboCop
|
|
17
17
|
# # bad
|
18
18
|
# it { is_expected.to have_http_status 200 }
|
19
19
|
# it { is_expected.to have_http_status 404 }
|
20
|
+
# it { is_expected.to have_http_status "403" }
|
20
21
|
#
|
21
22
|
# # good
|
22
23
|
# it { is_expected.to have_http_status :ok }
|
23
24
|
# it { is_expected.to have_http_status :not_found }
|
25
|
+
# it { is_expected.to have_http_status :forbidden }
|
24
26
|
# it { is_expected.to have_http_status :success }
|
25
27
|
# it { is_expected.to have_http_status :error }
|
26
28
|
#
|
@@ -28,10 +30,12 @@ module RuboCop
|
|
28
30
|
# # bad
|
29
31
|
# it { is_expected.to have_http_status :ok }
|
30
32
|
# it { is_expected.to have_http_status :not_found }
|
33
|
+
# it { is_expected.to have_http_status "forbidden" }
|
31
34
|
#
|
32
35
|
# # good
|
33
36
|
# it { is_expected.to have_http_status 200 }
|
34
37
|
# it { is_expected.to have_http_status 404 }
|
38
|
+
# it { is_expected.to have_http_status 403 }
|
35
39
|
# it { is_expected.to have_http_status :success }
|
36
40
|
# it { is_expected.to have_http_status :error }
|
37
41
|
#
|
@@ -39,8 +43,10 @@ module RuboCop
|
|
39
43
|
# # bad
|
40
44
|
# it { is_expected.to have_http_status :ok }
|
41
45
|
# it { is_expected.to have_http_status :not_found }
|
46
|
+
# it { is_expected.to have_http_status "forbidden" }
|
42
47
|
# it { is_expected.to have_http_status 200 }
|
43
48
|
# it { is_expected.to have_http_status 404 }
|
49
|
+
# it { is_expected.to have_http_status "403" }
|
44
50
|
#
|
45
51
|
# # good
|
46
52
|
# it { is_expected.to be_ok }
|
@@ -55,7 +61,7 @@ module RuboCop
|
|
55
61
|
|
56
62
|
# @!method http_status(node)
|
57
63
|
def_node_matcher :http_status, <<-PATTERN
|
58
|
-
(send nil? :have_http_status ${int sym})
|
64
|
+
(send nil? :have_http_status ${int sym str})
|
59
65
|
PATTERN
|
60
66
|
|
61
67
|
def on_send(node)
|
@@ -124,7 +130,7 @@ module RuboCop
|
|
124
130
|
end
|
125
131
|
|
126
132
|
def current
|
127
|
-
|
133
|
+
node.value.inspect
|
128
134
|
end
|
129
135
|
|
130
136
|
private
|
@@ -134,7 +140,7 @@ module RuboCop
|
|
134
140
|
end
|
135
141
|
|
136
142
|
def number
|
137
|
-
node.source.to_i
|
143
|
+
node.source.delete('"').to_i
|
138
144
|
end
|
139
145
|
end
|
140
146
|
|
@@ -159,7 +165,7 @@ module RuboCop
|
|
159
165
|
end
|
160
166
|
|
161
167
|
def number
|
162
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
|
168
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol.to_sym]
|
163
169
|
end
|
164
170
|
end
|
165
171
|
|
@@ -177,8 +183,10 @@ module RuboCop
|
|
177
183
|
def prefer
|
178
184
|
if node.sym_type?
|
179
185
|
"be_#{node.value}"
|
180
|
-
|
186
|
+
elsif node.int_type?
|
181
187
|
"be_#{symbol}"
|
188
|
+
elsif node.str_type?
|
189
|
+
"be_#{normalize_str}"
|
182
190
|
end
|
183
191
|
end
|
184
192
|
|
@@ -195,6 +203,15 @@ module RuboCop
|
|
195
203
|
def number
|
196
204
|
node.source.to_i
|
197
205
|
end
|
206
|
+
|
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
|
214
|
+
end
|
198
215
|
end
|
199
216
|
end
|
200
217
|
end
|
@@ -17,6 +17,9 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# expect(foo).not_to be_valid
|
19
19
|
#
|
20
|
+
# # good (with method chain)
|
21
|
+
# expect(foo).to be_invalid.and be_odd
|
22
|
+
#
|
20
23
|
# @example EnforcedStyle: be_invalid
|
21
24
|
# # bad
|
22
25
|
# expect(foo).not_to be_valid
|
@@ -24,6 +27,9 @@ module RuboCop
|
|
24
27
|
# # good
|
25
28
|
# expect(foo).to be_invalid
|
26
29
|
#
|
30
|
+
# # good (with method chain)
|
31
|
+
# expect(foo).to be_invalid.or be_even
|
32
|
+
#
|
27
33
|
class NegationBeValid < Base
|
28
34
|
extend AutoCorrector
|
29
35
|
include ConfigurableEnforcedStyle
|
@@ -124,7 +124,7 @@ module RuboCop
|
|
124
124
|
|
125
125
|
def register_offense(item, repeated_lines, args)
|
126
126
|
add_offense(item, message: message(repeated_lines)) do |corrector|
|
127
|
-
if item.loc.line
|
127
|
+
if item.loc.line > repeated_lines.max
|
128
128
|
replace_to_receive_messages(corrector, item, args)
|
129
129
|
else
|
130
130
|
corrector.remove(item_range_by_whole_lines(item))
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that spec file paths are consistent and well-formed.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# whatever_spec.rb # describe MyClass
|
11
|
+
# my_class_spec.rb # describe MyClass, '#method'
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# my_class_spec.rb # describe MyClass
|
15
|
+
# my_class_method_spec.rb # describe MyClass, '#method'
|
16
|
+
# my_class/method_spec.rb # describe MyClass, '#method'
|
17
|
+
#
|
18
|
+
# @example `CustomTransform: {RuboCop=>rubocop, RSpec=>rspec}` (default)
|
19
|
+
# # good
|
20
|
+
# rubocop_spec.rb # describe RuboCop
|
21
|
+
# rspec_spec.rb # describe RSpec
|
22
|
+
#
|
23
|
+
# @example `IgnoreMethods: false` (default)
|
24
|
+
# # bad
|
25
|
+
# my_class_spec.rb # describe MyClass, '#method'
|
26
|
+
#
|
27
|
+
# @example `IgnoreMethods: true`
|
28
|
+
# # good
|
29
|
+
# my_class_spec.rb # describe MyClass, '#method'
|
30
|
+
#
|
31
|
+
# @example `IgnoreMetadata: {type=>routing}` (default)
|
32
|
+
# # good
|
33
|
+
# whatever_spec.rb # describe MyClass, type: :routing do; end
|
34
|
+
#
|
35
|
+
class SpecFilePathFormat < Base
|
36
|
+
include TopLevelGroup
|
37
|
+
include Namespace
|
38
|
+
include FileHelp
|
39
|
+
|
40
|
+
MSG = 'Spec path should end with `%<suffix>s`.'
|
41
|
+
|
42
|
+
# @!method example_group_arguments(node)
|
43
|
+
def_node_matcher :example_group_arguments, <<~PATTERN
|
44
|
+
(block (send #rspec? #ExampleGroups.all $_ $...) ...)
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
# @!method metadata_key_value(node)
|
48
|
+
def_node_search :metadata_key_value, '(pair (sym $_key) (sym $_value))'
|
49
|
+
|
50
|
+
def on_top_level_example_group(node)
|
51
|
+
return unless top_level_groups.one?
|
52
|
+
|
53
|
+
example_group_arguments(node) do |class_name, arguments|
|
54
|
+
next if !class_name.const_type? || ignore_metadata?(arguments)
|
55
|
+
|
56
|
+
ensure_correct_file_path(class_name, arguments)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def ensure_correct_file_path(class_name, arguments)
|
63
|
+
pattern = correct_path_pattern(class_name, arguments)
|
64
|
+
return if filename_ends_with?(pattern)
|
65
|
+
|
66
|
+
# For the suffix shown in the offense message, modify the regular
|
67
|
+
# expression pattern to resemble a glob pattern for clearer error
|
68
|
+
# messages.
|
69
|
+
suffix = pattern.sub('.*', '*').sub('[^/]*', '*').sub('\.', '.')
|
70
|
+
add_global_offense(format(MSG, suffix: suffix))
|
71
|
+
end
|
72
|
+
|
73
|
+
def ignore_metadata?(arguments)
|
74
|
+
arguments.any? do |argument|
|
75
|
+
metadata_key_value(argument).any? do |key, value|
|
76
|
+
ignore_metadata.values_at(key.to_s).include?(value.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def correct_path_pattern(class_name, arguments)
|
82
|
+
path = [expected_path(class_name)]
|
83
|
+
path << '.*' unless ignore?(arguments.first)
|
84
|
+
path << [name_pattern(arguments.first), '[^/]*_spec\.rb']
|
85
|
+
path.join
|
86
|
+
end
|
87
|
+
|
88
|
+
def name_pattern(method_name)
|
89
|
+
return if ignore?(method_name)
|
90
|
+
|
91
|
+
method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')
|
92
|
+
end
|
93
|
+
|
94
|
+
def ignore?(method_name)
|
95
|
+
!method_name&.str_type? || ignore_methods?
|
96
|
+
end
|
97
|
+
|
98
|
+
def expected_path(constant)
|
99
|
+
constants = namespace(constant) + constant.const_name.split('::')
|
100
|
+
|
101
|
+
File.join(
|
102
|
+
constants.map do |name|
|
103
|
+
custom_transform.fetch(name) { camel_to_snake_case(name) }
|
104
|
+
end
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
def camel_to_snake_case(string)
|
109
|
+
string
|
110
|
+
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
|
111
|
+
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
|
112
|
+
.downcase
|
113
|
+
end
|
114
|
+
|
115
|
+
def custom_transform
|
116
|
+
cop_config.fetch('CustomTransform', {})
|
117
|
+
end
|
118
|
+
|
119
|
+
def ignore_methods?
|
120
|
+
cop_config['IgnoreMethods']
|
121
|
+
end
|
122
|
+
|
123
|
+
def ignore_metadata
|
124
|
+
cop_config.fetch('IgnoreMetadata', {})
|
125
|
+
end
|
126
|
+
|
127
|
+
def filename_ends_with?(pattern)
|
128
|
+
expanded_file_path.match?("#{pattern}$")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that spec file paths suffix are consistent and well-formed.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# my_class/foo_specorb.rb # describe MyClass
|
11
|
+
# spec/models/user.rb # describe User
|
12
|
+
# spec/models/user_specxrb # describe User
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# my_class_spec.rb # describe MyClass
|
16
|
+
#
|
17
|
+
# # good - shared examples are allowed
|
18
|
+
# spec/models/user.rb # shared_examples_for 'foo'
|
19
|
+
#
|
20
|
+
class SpecFilePathSuffix < Base
|
21
|
+
include TopLevelGroup
|
22
|
+
include FileHelp
|
23
|
+
|
24
|
+
MSG = 'Spec path should end with `_spec.rb`.'
|
25
|
+
|
26
|
+
def on_top_level_example_group(node)
|
27
|
+
example_group?(node) do
|
28
|
+
add_global_offense(MSG) unless correct_path?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def correct_path?
|
35
|
+
expanded_file_path.end_with?('_spec.rb')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -35,7 +35,7 @@ module RuboCop
|
|
35
35
|
return unless inside_example_group?(node)
|
36
36
|
|
37
37
|
variable_definition?(node) do |variable|
|
38
|
-
next unless
|
38
|
+
next unless style_offense?(variable)
|
39
39
|
|
40
40
|
add_offense(
|
41
41
|
variable,
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
62
|
+
def style_offense?(variable)
|
63
63
|
style == :symbols && string?(variable) ||
|
64
64
|
style == :strings && symbol?(variable)
|
65
65
|
end
|
@@ -79,8 +79,8 @@ module RuboCop
|
|
79
79
|
expression = class_reference.source_range
|
80
80
|
|
81
81
|
add_offense(expression, message: message) do |corrector|
|
82
|
-
|
83
|
-
corrector.replace(expression, correct_style(
|
82
|
+
offense = class_reference.source
|
83
|
+
corrector.replace(expression, correct_style(offense))
|
84
84
|
|
85
85
|
opposite_style_detected
|
86
86
|
end
|
@@ -98,11 +98,11 @@ module RuboCop
|
|
98
98
|
class_reference_style != style
|
99
99
|
end
|
100
100
|
|
101
|
-
def correct_style(
|
101
|
+
def correct_style(offense)
|
102
102
|
if style == :string
|
103
|
-
"'#{
|
103
|
+
"'#{offense}'"
|
104
104
|
else
|
105
|
-
|
105
|
+
offense.gsub(/^['"]|['"]$/, '')
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -57,6 +57,8 @@ require_relative 'rspec/empty_line_after_example_group'
|
|
57
57
|
require_relative 'rspec/empty_line_after_final_let'
|
58
58
|
require_relative 'rspec/empty_line_after_hook'
|
59
59
|
require_relative 'rspec/empty_line_after_subject'
|
60
|
+
require_relative 'rspec/empty_metadata'
|
61
|
+
require_relative 'rspec/eq'
|
60
62
|
require_relative 'rspec/example_length'
|
61
63
|
require_relative 'rspec/example_without_description'
|
62
64
|
require_relative 'rspec/example_wording'
|
@@ -86,6 +88,7 @@ require_relative 'rspec/match_array'
|
|
86
88
|
require_relative 'rspec/message_chain'
|
87
89
|
require_relative 'rspec/message_expectation'
|
88
90
|
require_relative 'rspec/message_spies'
|
91
|
+
require_relative 'rspec/metadata_style'
|
89
92
|
require_relative 'rspec/missing_example_group_argument'
|
90
93
|
require_relative 'rspec/multiple_describes'
|
91
94
|
require_relative 'rspec/multiple_expectations'
|
@@ -116,6 +119,8 @@ require_relative 'rspec/shared_examples'
|
|
116
119
|
require_relative 'rspec/single_argument_message_chain'
|
117
120
|
require_relative 'rspec/skip_block_inside_example'
|
118
121
|
require_relative 'rspec/sort_metadata'
|
122
|
+
require_relative 'rspec/spec_file_path_format'
|
123
|
+
require_relative 'rspec/spec_file_path_suffix'
|
119
124
|
require_relative 'rspec/stubbed_mock'
|
120
125
|
require_relative 'rspec/subject_declaration'
|
121
126
|
require_relative 'rspec/subject_stub'
|
data/lib/rubocop-rspec.rb
CHANGED
@@ -17,6 +17,7 @@ require_relative 'rubocop/rspec/wording'
|
|
17
17
|
# Dependent on `RuboCop::RSpec::Language::NodePattern`.
|
18
18
|
require_relative 'rubocop/rspec/language'
|
19
19
|
|
20
|
+
require_relative 'rubocop/cop/rspec/mixin/file_help'
|
20
21
|
require_relative 'rubocop/cop/rspec/mixin/final_end_location'
|
21
22
|
require_relative 'rubocop/cop/rspec/mixin/inside_example_group'
|
22
23
|
require_relative 'rubocop/cop/rspec/mixin/location_help'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Backus
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-08
|
13
|
+
date: 2023-09-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
@@ -112,6 +112,8 @@ files:
|
|
112
112
|
- lib/rubocop/cop/rspec/empty_line_after_final_let.rb
|
113
113
|
- lib/rubocop/cop/rspec/empty_line_after_hook.rb
|
114
114
|
- lib/rubocop/cop/rspec/empty_line_after_subject.rb
|
115
|
+
- lib/rubocop/cop/rspec/empty_metadata.rb
|
116
|
+
- lib/rubocop/cop/rspec/eq.rb
|
115
117
|
- lib/rubocop/cop/rspec/example_length.rb
|
116
118
|
- lib/rubocop/cop/rspec/example_without_description.rb
|
117
119
|
- lib/rubocop/cop/rspec/example_wording.rb
|
@@ -147,9 +149,11 @@ files:
|
|
147
149
|
- lib/rubocop/cop/rspec/message_chain.rb
|
148
150
|
- lib/rubocop/cop/rspec/message_expectation.rb
|
149
151
|
- lib/rubocop/cop/rspec/message_spies.rb
|
152
|
+
- lib/rubocop/cop/rspec/metadata_style.rb
|
150
153
|
- lib/rubocop/cop/rspec/missing_example_group_argument.rb
|
151
154
|
- lib/rubocop/cop/rspec/mixin/comments_help.rb
|
152
155
|
- lib/rubocop/cop/rspec/mixin/empty_line_separation.rb
|
156
|
+
- lib/rubocop/cop/rspec/mixin/file_help.rb
|
153
157
|
- lib/rubocop/cop/rspec/mixin/final_end_location.rb
|
154
158
|
- lib/rubocop/cop/rspec/mixin/inside_example_group.rb
|
155
159
|
- lib/rubocop/cop/rspec/mixin/location_help.rb
|
@@ -194,6 +198,8 @@ files:
|
|
194
198
|
- lib/rubocop/cop/rspec/single_argument_message_chain.rb
|
195
199
|
- lib/rubocop/cop/rspec/skip_block_inside_example.rb
|
196
200
|
- lib/rubocop/cop/rspec/sort_metadata.rb
|
201
|
+
- lib/rubocop/cop/rspec/spec_file_path_format.rb
|
202
|
+
- lib/rubocop/cop/rspec/spec_file_path_suffix.rb
|
197
203
|
- lib/rubocop/cop/rspec/stubbed_mock.rb
|
198
204
|
- lib/rubocop/cop/rspec/subject_declaration.rb
|
199
205
|
- lib/rubocop/cop/rspec/subject_stub.rb
|