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