rubocop-rspec 2.19.0 → 2.20.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44e3a236fcd4869222a8e50780c5ef50671968fe82661b22f34d93315116c36b
4
- data.tar.gz: 22c11a55d5d29ae10f2ec8fc4a3c95a3bd7f2b1bb5a0b049a9e47d3328178e7d
3
+ metadata.gz: 1ebfcc955ecfa8b3437defde668d955020f03a2dbba2c9337f0b75a8fb9a3c26
4
+ data.tar.gz: 7a547b1e24c278de072b2c912be27e1dc5fb1450b6fa65d563278d0f054d6d1d
5
5
  SHA512:
6
- metadata.gz: bbfab487662a025737c213ab0f9c0a4c54d1d93fe3a3c05a344b20dcd2f83900aef723f07d0ecdade3c616afa15bee4a6cb975345ea9d8a80e3ba0a64c715a05
7
- data.tar.gz: a1f112ce509e7a938a7d0377ac2fcf0a5c723fbac2299f5246ab84b4d989d8223b7a608c5952368094515085d4a106695d3baf71b5f8b02769cdbbf4965d9cd4
6
+ metadata.gz: f8942cdf262718de32bfbc765e3f2aac9eae49e364d7a2d866300b77e0bd39fd24b168d513577a1a95a82ce10a0f325a66c6b2914da71dd3951011c2399b0b3e
7
+ data.tar.gz: 89c8fce01b42c53c50460b9411cd44f96b99663ce3a2e7735e6cb0f260c1cc3a86ba0daba553b4ebeba25625dde25f41d8b2129929cf92514e06e0f0f8191ec5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.20.0 (2023-04-18)
6
+
7
+ - Add new `RSpec/IndexedLet` cop. ([@dmitrytsepelev])
8
+ - Add new `RSpec/BeEmpty` cop. ([@ydah], [@bquorning])
9
+ - Add autocorrect support for `RSpec/ScatteredSetup`. ([@ydah])
10
+ - Add support `be_status` style for `RSpec/Rails/HttpStatus`. ([@ydah])
11
+ - Add support for shared example groups to `RSpec/EmptyLineAfterExampleGroup`. ([@pirj])
12
+ - Add support for `RSpec/HaveHttpStatus` when using `response.code`. ([@ydah])
13
+ - Fix order of expected and actual in correction for `RSpec/Rails/MinitestAssertions` ([@mvz])
14
+ - Fix a false positive for `RSpec/DescribedClassModuleWrapping` when RSpec.describe numblock is nested within a module. ([@ydah])
15
+ - Fix a false positive for `RSpec/FactoryBot/ConsistentParenthesesStyle` inside `&&`, `||` and `:?` when `omit_parentheses` is on ([@dmitrytsepelev])
16
+ - Fix a false positive for `RSpec/PendingWithoutReason` when pending/skip has a reason inside an example group. ([@ydah])
17
+ - Fix a false negative for `RSpec/RedundantAround` when redundant numblock `around`. ([@ydah])
18
+ - Change `RSpec/ContainExactly` to ignore calls with no arguments, and change `RSpec/MatchArray` to ignore calls with an empty array literal argument. ([@ydah], [@bquorning])
19
+ - Make `RSpec/MatchArray` and `RSpec/ContainExactly` pending. ([@ydah])
20
+
5
21
  ## 2.19.0 (2023-03-06)
6
22
 
7
23
  - Fix a false positive for `RSpec/ContextWording` when context is interpolated string literal or execute string. ([@ydah])
@@ -329,7 +345,7 @@
329
345
  ## 1.37.0 (2019-11-25)
330
346
 
331
347
  - Implement `RSpec/DescribedClassModuleWrapping` to disallow RSpec statements within a module. ([@kellysutton])
332
- - Fix documentation rake task to support Rubocop 0.75. ([@nickcampbell18])
348
+ - Fix documentation rake task to support RuboCop 0.75. ([@nickcampbell18])
333
349
  - Fix `RSpec/SubjectStub` to detect implicit subjects stubbed. ([@QQism])
334
350
  - Fix `RSpec/Pending` not flagging `skip` with string values. ([@pirj])
335
351
  - Add `AllowedExplicitMatchers` config option for `RSpec/PredicateMatcher`. ([@mkrawc])
@@ -660,7 +676,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
660
676
  - Skip `DescribeClass` cop for view specs. ([@andyw8])
661
677
  - Skip `FilePath` cop for Rails routing specs. ([@andyw8])
662
678
  - Add cop to check for focused specs. ([@renanborgescampos], [@jaredmoody])
663
- - Clean-up `RSpec::NotToNot` to use same configuration semantics as other Rubocop cops, add autocorrect support for `RSpec::NotToNot`. ([@baberthal])
679
+ - Clean-up `RSpec::NotToNot` to use same configuration semantics as other RuboCop cops, add autocorrect support for `RSpec::NotToNot`. ([@baberthal])
664
680
  - Update to rubocop 0.40.0. ([@nijikon])
665
681
 
666
682
  ## 1.4.1 (2016-04-03)
@@ -758,6 +774,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
758
774
  [@dduugg]: https://github.com/dduugg
759
775
  [@deivid-rodriguez]: https://github.com/deivid-rodriguez
760
776
  [@dgollahon]: https://github.com/dgollahon
777
+ [@dmitrytsepelev]: https://github.com/dmitrytsepelev
761
778
  [@drowze]: https://github.com/Drowze
762
779
  [@dswij]: https://github.com/dswij
763
780
  [@dvandersluis]: https://github.com/dvandersluis
@@ -800,6 +817,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
800
817
  [@mlarraz]: https://github.com/mlarraz
801
818
  [@mockdeep]: https://github.com/mockdeep
802
819
  [@mothonmars]: https://github.com/MothOnMars
820
+ [@mvz]: https://github.com/mvz
803
821
  [@nc-holodakg]: https://github.com/nc-holodakg
804
822
  [@nevir]: https://github.com/nevir
805
823
  [@ngouy]: https://github.com/ngouy
data/config/default.yml CHANGED
@@ -144,6 +144,12 @@ RSpec/Be:
144
144
  StyleGuide: https://rspec.rubystyle.guide/#be-matcher
145
145
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Be
146
146
 
147
+ RSpec/BeEmpty:
148
+ Description: Prefer using `be_empty` when checking for an empty array.
149
+ Enabled: pending
150
+ VersionAdded: '2.20'
151
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEmpty
152
+
147
153
  RSpec/BeEq:
148
154
  Description: Check for expectations where `be(...)` can replace `eq(...)`.
149
155
  Enabled: pending
@@ -202,8 +208,8 @@ RSpec/ClassCheck:
202
208
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ClassCheck
203
209
 
204
210
  RSpec/ContainExactly:
205
- Description: Prefer `match_array` when matching array values.
206
- Enabled: true
211
+ Description: Checks where `contain_exactly` is used.
212
+ Enabled: pending
207
213
  VersionAdded: '2.19'
208
214
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContainExactly
209
215
 
@@ -503,6 +509,13 @@ RSpec/ImplicitSubject:
503
509
  VersionChanged: '2.13'
504
510
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject
505
511
 
512
+ RSpec/IndexedLet:
513
+ Description: Do not set up test data using indexes (e.g., `item_1`, `item_2`).
514
+ Enabled: pending
515
+ VersionAdded: '2.20'
516
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IndexedLet
517
+ Max: 1
518
+
506
519
  RSpec/InstanceSpy:
507
520
  Description: Checks for `instance_double` used with `have_received`.
508
521
  Enabled: true
@@ -563,8 +576,8 @@ RSpec/LetSetup:
563
576
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup
564
577
 
565
578
  RSpec/MatchArray:
566
- Description: Prefer `contain_exactly` when matching an array literal.
567
- Enabled: true
579
+ Description: Checks where `match_array` is used.
580
+ Enabled: pending
568
581
  VersionAdded: '2.19'
569
582
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MatchArray
570
583
 
@@ -1048,8 +1061,9 @@ RSpec/Rails/HttpStatus:
1048
1061
  SupportedStyles:
1049
1062
  - numeric
1050
1063
  - symbolic
1064
+ - be_status
1051
1065
  VersionAdded: '1.23'
1052
- VersionChanged: '2.0'
1066
+ VersionChanged: '2.20'
1053
1067
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus
1054
1068
 
1055
1069
  RSpec/Rails/InferredSpecType:
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Prefer using `be_empty` when checking for an empty array.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect(array).to contain_exactly
11
+ # expect(array).to match_array([])
12
+ #
13
+ # # good
14
+ # expect(array).to be_empty
15
+ #
16
+ class BeEmpty < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Use `be_empty` matchers for checking an empty array.'
20
+ RESTRICT_ON_SEND = %i[contain_exactly match_array].freeze
21
+
22
+ # @!method expect_array_matcher?(node)
23
+ def_node_matcher :expect_array_matcher?, <<~PATTERN
24
+ (send
25
+ (send nil? :expect _)
26
+ #Runners.all
27
+ ${
28
+ (send nil? :match_array (array))
29
+ (send nil? :contain_exactly)
30
+ }
31
+ )
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ expect_array_matcher?(node.parent) do |expect|
36
+ add_offense(expect) do |corrector|
37
+ corrector.replace(expect, 'be_empty')
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -57,7 +57,7 @@ module RuboCop
57
57
  return unless be_nil_matcher?(node)
58
58
 
59
59
  add_offense(node, message: BE_MSG) do |corrector|
60
- corrector.replace(node.source_range, 'be(nil)')
60
+ corrector.replace(node, 'be(nil)')
61
61
  end
62
62
  end
63
63
 
@@ -65,7 +65,7 @@ module RuboCop
65
65
  return unless nil_value_expectation?(node)
66
66
 
67
67
  add_offense(node, message: BE_NIL_MSG) do |corrector|
68
- corrector.replace(node.source_range, 'be_nil')
68
+ corrector.replace(node, 'be_nil')
69
69
  end
70
70
  end
71
71
  end
@@ -3,7 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Prefer `match_array` when matching array values.
6
+ # Checks where `contain_exactly` is used.
7
+ #
8
+ # This cop checks for the following:
9
+ # - Prefer `match_array` when matching array values.
10
+ # - Prefer `be_empty` when using `contain_exactly` with no arguments.
7
11
  #
8
12
  # @example
9
13
  # # bad
@@ -14,6 +18,7 @@ module RuboCop
14
18
  #
15
19
  # # good
16
20
  # it { is_expected.to contain_exactly(content, *array) }
21
+ #
17
22
  class ContainExactly < Base
18
23
  extend AutoCorrector
19
24
 
@@ -21,21 +26,27 @@ module RuboCop
21
26
  RESTRICT_ON_SEND = %i[contain_exactly].freeze
22
27
 
23
28
  def on_send(node)
29
+ return if node.arguments.empty?
30
+
31
+ check_populated_collection(node)
32
+ end
33
+
34
+ private
35
+
36
+ def check_populated_collection(node)
24
37
  return unless node.each_child_node.all?(&:splat_type?)
25
38
 
26
39
  add_offense(node) do |corrector|
27
- autocorrect(node, corrector)
40
+ autocorrect_for_populated_array(node, corrector)
28
41
  end
29
42
  end
30
43
 
31
- private
32
-
33
- def autocorrect(node, corrector)
44
+ def autocorrect_for_populated_array(node, corrector)
34
45
  arrays = node.arguments.map do |splat_node|
35
46
  splat_node.children.first
36
47
  end
37
48
  corrector.replace(
38
- node.source_range,
49
+ node,
39
50
  "match_array(#{arrays.map(&:source).join(' + ')})"
40
51
  )
41
52
  end
@@ -22,15 +22,15 @@ module RuboCop
22
22
  class DescribedClassModuleWrapping < Base
23
23
  MSG = 'Avoid opening modules and defining specs within them.'
24
24
 
25
- # @!method find_rspec_blocks(node)
26
- def_node_search :find_rspec_blocks, <<~PATTERN
27
- (block (send #explicit_rspec? #ExampleGroups.all ...) ...)
25
+ # @!method include_rspec_blocks?(node)
26
+ def_node_search :include_rspec_blocks?, <<~PATTERN
27
+ ({block numblock} (send #explicit_rspec? #ExampleGroups.all ...) ...)
28
28
  PATTERN
29
29
 
30
30
  def on_module(node)
31
- find_rspec_blocks(node) do
32
- add_offense(node)
33
- end
31
+ return unless include_rspec_blocks?(node)
32
+
33
+ add_offense(node)
34
34
  end
35
35
  end
36
36
  end
@@ -30,7 +30,7 @@ module RuboCop
30
30
  MSG = 'Add an empty line after `%<example_group>s`.'
31
31
 
32
32
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
33
- return unless example_group?(node)
33
+ return unless spec_group?(node)
34
34
 
35
35
  missing_separating_line_offense(node) do |method|
36
36
  format(MSG, example_group: method)
@@ -93,8 +93,8 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def swap(corrector, actual, expected)
96
- corrector.replace(actual.source_range, expected.source)
97
- corrector.replace(expected.source_range, actual.source)
96
+ corrector.replace(actual, expected.source)
97
+ corrector.replace(expected, actual.source)
98
98
  end
99
99
  end
100
100
  end
@@ -100,10 +100,10 @@ module RuboCop
100
100
  end
101
101
  end
102
102
 
103
+ AMBIGUOUS_TYPES = %i[send pair array and or if].freeze
104
+
103
105
  def ambiguous_without_parentheses?(node)
104
- node.parent&.send_type? ||
105
- node.parent&.pair_type? ||
106
- node.parent&.array_type?
106
+ node.parent && AMBIGUOUS_TYPES.include?(node.parent.type)
107
107
  end
108
108
 
109
109
  def remove_parentheses(corrector, node)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Do not set up test data using indexes (e.g., `item_1`, `item_2`).
7
+ #
8
+ # It makes reading the test harder because it's not clear what exactly
9
+ # is tested by this particular example.
10
+ #
11
+ # @example `Max: 1 (default)`
12
+ # # bad
13
+ # let(:item_1) { create(:item) }
14
+ # let(:item_2) { create(:item) }
15
+ #
16
+ # let(:item1) { create(:item) }
17
+ # let(:item2) { create(:item) }
18
+ #
19
+ # # good
20
+ #
21
+ # let(:visible_item) { create(:item, visible: true) }
22
+ # let(:invisible_item) { create(:item, visible: false) }
23
+ #
24
+ # @example `Max: 2`
25
+ # # bad
26
+ # let(:item_1) { create(:item) }
27
+ # let(:item_2) { create(:item) }
28
+ # let(:item_3) { create(:item) }
29
+ #
30
+ # # good
31
+ # let(:item_1) { create(:item) }
32
+ # let(:item_2) { create(:item) }
33
+ #
34
+ class IndexedLet < Base
35
+ MSG = 'This `let` statement uses index in its name. Please give it ' \
36
+ 'a meaningful name.'
37
+
38
+ # @!method let_name(node)
39
+ def_node_matcher :let_name, <<~PATTERN
40
+ {
41
+ (block (send nil? #Helpers.all ({str sym} $_) ...) ...)
42
+ (send nil? #Helpers.all ({str sym} $_) block_pass)
43
+ }
44
+ PATTERN
45
+
46
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
+ return unless spec_group?(node)
48
+
49
+ children = node.body&.child_nodes
50
+ return unless children
51
+
52
+ filter_indexed_lets(children).each do |let_node|
53
+ add_offense(let_node)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ INDEX_REGEX = /_?\d+/.freeze
60
+
61
+ def filter_indexed_lets(candidates)
62
+ candidates
63
+ .filter { |node| indexed_let?(node) }
64
+ .group_by { |node| let_name(node).to_s.gsub(INDEX_REGEX, '') }
65
+ .values
66
+ .filter { |lets| lets.length > cop_config['Max'] }
67
+ .flatten
68
+ end
69
+
70
+ def indexed_let?(node)
71
+ let?(node) && INDEX_REGEX.match?(let_name(node))
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -3,7 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Prefer `contain_exactly` when matching an array literal.
6
+ # Checks where `match_array` is used.
7
+ #
8
+ # This cop checks for the following:
9
+ # - Prefer `contain_exactly` when matching an array with values.
10
+ # - Prefer `eq` when using `match_array` with an empty array literal.
7
11
  #
8
12
  # @example
9
13
  # # bad
@@ -17,20 +21,34 @@ module RuboCop
17
21
  #
18
22
  # # good
19
23
  # it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
24
+ #
20
25
  class MatchArray < Base
21
26
  extend AutoCorrector
22
27
 
23
28
  MSG = 'Prefer `contain_exactly` when matching an array literal.'
24
29
  RESTRICT_ON_SEND = %i[match_array].freeze
25
30
 
31
+ # @!method match_array_with_empty_array?(node)
32
+ def_node_matcher :match_array_with_empty_array?, <<~PATTERN
33
+ (send nil? :match_array (array))
34
+ PATTERN
35
+
26
36
  def on_send(node)
27
37
  return unless node.first_argument.array_type?
38
+ return if match_array_with_empty_array?(node)
39
+
40
+ check_populated_array(node)
41
+ end
42
+
43
+ private
44
+
45
+ def check_populated_array(node)
28
46
  return if node.first_argument.percent_literal?
29
47
 
30
48
  add_offense(node) do |corrector|
31
49
  array_contents = node.arguments.flat_map(&:to_a)
32
50
  corrector.replace(
33
- node.source_range,
51
+ node,
34
52
  "contain_exactly(#{array_contents.map(&:source).join(', ')})"
35
53
  )
36
54
  end
@@ -70,7 +70,12 @@ module RuboCop
70
70
 
71
71
  # @!method skipped_by_example_method?(node)
72
72
  def_node_matcher :skipped_by_example_method?, <<~PATTERN
73
- (send nil? ${#Examples.skipped #Examples.pending} ...)
73
+ (send nil? ${#Examples.skipped #Examples.pending})
74
+ PATTERN
75
+
76
+ # @!method skipped_by_example_method_with_block?(node)
77
+ def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
78
+ ({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
74
79
  PATTERN
75
80
 
76
81
  # @!method metadata_without_reason?(node)
@@ -102,7 +107,7 @@ module RuboCop
102
107
  on_skipped_by_example_method(node)
103
108
  on_skipped_by_example_group_method(node)
104
109
  elsif example?(parent)
105
- on_skipped_by_in_example_method(node, parent)
110
+ on_skipped_by_in_example_method(node)
106
111
  end
107
112
  end
108
113
 
@@ -121,7 +126,7 @@ module RuboCop
121
126
  explicit_rspec?(node.receiver)
122
127
  end
123
128
 
124
- def on_skipped_by_in_example_method(node, _direct_parent)
129
+ def on_skipped_by_in_example_method(node)
125
130
  skipped_in_example?(node) do |pending|
126
131
  add_offense(node, message: "Give the reason for #{pending}.")
127
132
  end
@@ -137,6 +142,10 @@ module RuboCop
137
142
  skipped_by_example_method?(node) do |pending|
138
143
  add_offense(node, message: "Give the reason for #{pending}.")
139
144
  end
145
+
146
+ skipped_by_example_method_with_block?(node.parent) do |pending|
147
+ add_offense(node, message: "Give the reason for #{pending}.")
148
+ end
140
149
  end
141
150
 
142
151
  def on_skipped_by_example_group_method(node)
@@ -99,7 +99,7 @@ module RuboCop
99
99
  block = block_loc ? block_loc.source : ''
100
100
 
101
101
  corrector.replace(
102
- matcher.source_range,
102
+ matcher,
103
103
  to_predicate_matcher(predicate.method_name) + args + block
104
104
  )
105
105
  end
@@ -214,7 +214,7 @@ module RuboCop
214
214
 
215
215
  def corrector_explicit(corrector, to_node, actual, matcher, block_child)
216
216
  replacement_matcher = replacement_matcher(to_node)
217
- corrector.replace(matcher.source_range, replacement_matcher)
217
+ corrector.replace(matcher, replacement_matcher)
218
218
  move_predicate(corrector, actual, matcher, block_child)
219
219
  corrector.replace(to_node.loc.selector, 'to')
220
220
  end
@@ -226,8 +226,7 @@ module RuboCop
226
226
  block = block_loc ? block_loc.source : ''
227
227
 
228
228
  corrector.remove(block_loc) if block_loc
229
- corrector.insert_after(actual.source_range,
230
- ".#{predicate}" + args + block)
229
+ corrector.insert_after(actual, ".#{predicate}" + args + block)
231
230
  end
232
231
 
233
232
  # rubocop:disable Metrics/MethodLength
@@ -9,6 +9,7 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # expect(response.status).to be(200)
12
+ # expect(response.code).to eq("200")
12
13
  #
13
14
  # # good
14
15
  # expect(response).to have_http_status(200)
@@ -18,7 +19,7 @@ module RuboCop
18
19
 
19
20
  MSG =
20
21
  'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
21
- 'over `expect(response.status).%<to>s %<match>s`.'
22
+ 'over `%<bad_code>s`.'
22
23
 
23
24
  RUNNERS = %i[to to_not not_to].to_set
24
25
  RESTRICT_ON_SEND = RUNNERS
@@ -27,19 +28,21 @@ module RuboCop
27
28
  def_node_matcher :match_status, <<-PATTERN
28
29
  (send
29
30
  (send nil? :expect
30
- $(send (send nil? :response) :status)
31
+ $(send (send nil? :response) {:status :code})
31
32
  )
32
33
  $RUNNERS
33
- $(send nil? {:be :eq :eql :equal} (int $_))
34
+ $(send nil? {:be :eq :eql :equal} ({int str} $_))
34
35
  )
35
36
  PATTERN
36
37
 
37
38
  def on_send(node)
38
39
  match_status(node) do |response_status, to, match, status|
39
- message = format(MSG, to: to, match: match.source, status: status)
40
+ message = format(MSG, to: to, status: status,
41
+ bad_code: node.source)
40
42
  add_offense(node, message: message) do |corrector|
41
- corrector.replace(response_status.source_range, 'response')
43
+ corrector.replace(response_status, 'response')
42
44
  corrector.replace(match.loc.selector, 'have_http_status')
45
+ corrector.replace(match.first_argument, status.to_i.to_s)
43
46
  end
44
47
  end
45
48
  end
@@ -8,6 +8,11 @@ module RuboCop
8
8
  module Rails
9
9
  # Enforces use of symbolic or numeric value to describe HTTP status.
10
10
  #
11
+ # This cop inspects only `have_http_status` calls.
12
+ # So, this cop does not check if a method starting with `be_*` is used
13
+ # when setting for `EnforcedStyle: symbolic` or
14
+ # `EnforcedStyle: numeric`.
15
+ #
11
16
  # @example `EnforcedStyle: symbolic` (default)
12
17
  # # bad
13
18
  # it { is_expected.to have_http_status 200 }
@@ -30,6 +35,19 @@ module RuboCop
30
35
  # it { is_expected.to have_http_status :success }
31
36
  # it { is_expected.to have_http_status :error }
32
37
  #
38
+ # @example `EnforcedStyle: be_status`
39
+ # # bad
40
+ # it { is_expected.to have_http_status :ok }
41
+ # it { is_expected.to have_http_status :not_found }
42
+ # it { is_expected.to have_http_status 200 }
43
+ # it { is_expected.to have_http_status 404 }
44
+ #
45
+ # # good
46
+ # it { is_expected.to be_ok }
47
+ # it { is_expected.to be_not_found }
48
+ # it { is_expected.to have_http_status :success }
49
+ # it { is_expected.to have_http_status :error }
50
+ #
33
51
  class HttpStatus < Base
34
52
  extend AutoCorrector
35
53
  include ConfigurableEnforcedStyle
@@ -41,12 +59,13 @@ module RuboCop
41
59
  PATTERN
42
60
 
43
61
  def on_send(node)
44
- http_status(node) do |ast_node|
45
- checker = checker_class.new(ast_node)
62
+ http_status(node) do |arg|
63
+ checker = checker_class.new(arg)
46
64
  return unless checker.offensive?
47
65
 
48
- add_offense(checker.node, message: checker.message) do |corrector|
49
- corrector.replace(checker.node, checker.preferred_style)
66
+ add_offense(checker.offense_range,
67
+ message: checker.message) do |corrector|
68
+ corrector.replace(checker.offense_range, checker.prefer)
50
69
  end
51
70
  end
52
71
  end
@@ -59,13 +78,16 @@ module RuboCop
59
78
  SymbolicStyleChecker
60
79
  when :numeric
61
80
  NumericStyleChecker
81
+ when :be_status
82
+ BeStatusStyleChecker
62
83
  end
63
84
  end
64
85
 
65
86
  # :nodoc:
66
- class SymbolicStyleChecker
87
+ class StyleCheckerBase
67
88
  MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
68
89
  'to describe HTTP status code.'
90
+ ALLOWED_STATUSES = %i[error success missing redirect].freeze
69
91
 
70
92
  attr_reader :node
71
93
 
@@ -73,16 +95,36 @@ module RuboCop
73
95
  @node = node
74
96
  end
75
97
 
98
+ def message
99
+ format(MSG, prefer: prefer, current: current)
100
+ end
101
+
102
+ def offense_range
103
+ node
104
+ end
105
+
106
+ def allowed_symbol?
107
+ node.sym_type? && ALLOWED_STATUSES.include?(node.value)
108
+ end
109
+
110
+ def custom_http_status_code?
111
+ node.int_type? &&
112
+ !::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
113
+ end
114
+ end
115
+
116
+ # :nodoc:
117
+ class SymbolicStyleChecker < StyleCheckerBase
76
118
  def offensive?
77
119
  !node.sym_type? && !custom_http_status_code?
78
120
  end
79
121
 
80
- def message
81
- format(MSG, prefer: preferred_style, current: number.to_s)
122
+ def prefer
123
+ symbol.inspect
82
124
  end
83
125
 
84
- def preferred_style
85
- symbol.inspect
126
+ def current
127
+ number.inspect
86
128
  end
87
129
 
88
130
  private
@@ -94,50 +136,64 @@ module RuboCop
94
136
  def number
95
137
  node.source.to_i
96
138
  end
97
-
98
- def custom_http_status_code?
99
- node.int_type? &&
100
- !::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
101
- end
102
139
  end
103
140
 
104
141
  # :nodoc:
105
- class NumericStyleChecker
106
- MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
107
- 'to describe HTTP status code.'
108
-
109
- ALLOWED_STATUSES = %i[error success missing redirect].freeze
110
-
111
- attr_reader :node
112
-
113
- def initialize(node)
114
- @node = node
115
- end
116
-
142
+ class NumericStyleChecker < StyleCheckerBase
117
143
  def offensive?
118
144
  !node.int_type? && !allowed_symbol?
119
145
  end
120
146
 
121
- def message
122
- format(MSG, prefer: preferred_style, current: symbol.inspect)
147
+ def prefer
148
+ number.to_s
123
149
  end
124
150
 
125
- def preferred_style
126
- number.to_s
151
+ def current
152
+ symbol.inspect
127
153
  end
128
154
 
129
155
  private
130
156
 
157
+ def symbol
158
+ node.value
159
+ end
160
+
131
161
  def number
132
162
  ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
133
163
  end
164
+ end
165
+
166
+ # :nodoc:
167
+ class BeStatusStyleChecker < StyleCheckerBase
168
+ def offensive?
169
+ (!node.sym_type? && !custom_http_status_code?) ||
170
+ (!node.int_type? && !allowed_symbol?)
171
+ end
172
+
173
+ def offense_range
174
+ node.parent
175
+ end
176
+
177
+ def prefer
178
+ if node.sym_type?
179
+ "be_#{node.value}"
180
+ else
181
+ "be_#{symbol}"
182
+ end
183
+ end
184
+
185
+ def current
186
+ offense_range.source
187
+ end
188
+
189
+ private
134
190
 
135
191
  def symbol
136
- node.value
192
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
137
193
  end
138
194
 
139
- def allowed_symbol?
140
- node.sym_type? && ALLOWED_STATUSES.include?(node.value)
195
+ def number
196
+ node.source.to_i
141
197
  end
142
198
  end
143
199
  end
@@ -13,9 +13,9 @@ module RuboCop
13
13
  # refute_equal(a, b)
14
14
  #
15
15
  # # good
16
- # expect(a).to eq(b)
17
- # expect(a).to(eq(b), "must be equal")
18
- # expect(a).not_to eq(b)
16
+ # expect(b).to eq(a)
17
+ # expect(b).to(eq(a), "must be equal")
18
+ # expect(b).not_to eq(a)
19
19
  #
20
20
  class MinitestAssertions < Base
21
21
  extend AutoCorrector
@@ -43,9 +43,9 @@ module RuboCop
43
43
  def replacement(node, expected, actual, failure_message)
44
44
  runner = node.method?(:assert_equal) ? 'to' : 'not_to'
45
45
  if failure_message.nil?
46
- "expect(#{expected.source}).#{runner} eq(#{actual.source})"
46
+ "expect(#{actual.source}).#{runner} eq(#{expected.source})"
47
47
  else
48
- "expect(#{expected.source}).#{runner}(eq(#{actual.source}), " \
48
+ "expect(#{actual.source}).#{runner}(eq(#{expected.source}), " \
49
49
  "#{failure_message.source})"
50
50
  end
51
51
  end
@@ -41,11 +41,7 @@ module RuboCop
41
41
 
42
42
  # @!method match_redundant_around_hook_block?(node)
43
43
  def_node_matcher :match_redundant_around_hook_block?, <<~PATTERN
44
- (block
45
- (send _ :around ...)
46
- (args _?)
47
- (send _ :run)
48
- )
44
+ ({block numblock} (send _ :around ...) ... (send _ :run))
49
45
  PATTERN
50
46
 
51
47
  # @!method match_redundant_around_hook_send?(node)
@@ -23,6 +23,9 @@ module RuboCop
23
23
  # end
24
24
  #
25
25
  class ScatteredSetup < Base
26
+ include RangeHelp
27
+ extend AutoCorrector
28
+
26
29
  MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
27
30
  'example group (also defined on %<lines>s).'
28
31
 
@@ -30,13 +33,11 @@ module RuboCop
30
33
  return unless example_group?(node)
31
34
 
32
35
  repeated_hooks(node).each do |occurrences|
33
- lines = occurrences.map(&:first_line)
34
-
35
36
  occurrences.each do |occurrence|
36
- lines_except_current = lines - [occurrence.first_line]
37
- message = format(MSG, hook_name: occurrences.first.method_name,
38
- lines: lines_msg(lines_except_current))
39
- add_offense(occurrence, message: message)
37
+ message = message(occurrences, occurrence)
38
+ add_offense(occurrence, message: message) do |corrector|
39
+ autocorrect(corrector, occurrences.first, occurrence)
40
+ end
40
41
  end
41
42
  end
42
43
  end
@@ -63,6 +64,22 @@ module RuboCop
63
64
  "lines #{numbers.join(', ')}"
64
65
  end
65
66
  end
67
+
68
+ def message(occurrences, occurrence)
69
+ lines = occurrences.map(&:first_line)
70
+ lines_except_current = lines - [occurrence.first_line]
71
+ format(MSG, hook_name: occurrences.first.method_name,
72
+ lines: lines_msg(lines_except_current))
73
+ end
74
+
75
+ def autocorrect(corrector, first_occurrence, occurrence)
76
+ return if first_occurrence == occurrence || !first_occurrence.body
77
+
78
+ corrector.insert_after(first_occurrence.body,
79
+ "\n#{occurrence.body.source}")
80
+ corrector.remove(range_by_whole_lines(occurrence.source_range,
81
+ include_final_newline: true))
82
+ end
66
83
  end
67
84
  end
68
85
  end
@@ -32,6 +32,7 @@ require_relative 'rspec/align_right_let_brace'
32
32
  require_relative 'rspec/any_instance'
33
33
  require_relative 'rspec/around_block'
34
34
  require_relative 'rspec/be'
35
+ require_relative 'rspec/be_empty'
35
36
  require_relative 'rspec/be_eq'
36
37
  require_relative 'rspec/be_eql'
37
38
  require_relative 'rspec/be_nil'
@@ -71,6 +72,7 @@ require_relative 'rspec/identical_equality_assertion'
71
72
  require_relative 'rspec/implicit_block_expectation'
72
73
  require_relative 'rspec/implicit_expect'
73
74
  require_relative 'rspec/implicit_subject'
75
+ require_relative 'rspec/indexed_let'
74
76
  require_relative 'rspec/instance_spy'
75
77
  require_relative 'rspec/instance_variable'
76
78
  require_relative 'rspec/it_behaves_like'
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '2.19.0'
7
+ STRING = '2.20.0'
8
8
  end
9
9
  end
10
10
  end
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.19.0
4
+ version: 2.20.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-03-06 00:00:00.000000000 Z
13
+ date: 2023-04-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -66,6 +66,7 @@ files:
66
66
  - lib/rubocop/cop/rspec/around_block.rb
67
67
  - lib/rubocop/cop/rspec/base.rb
68
68
  - lib/rubocop/cop/rspec/be.rb
69
+ - lib/rubocop/cop/rspec/be_empty.rb
69
70
  - lib/rubocop/cop/rspec/be_eq.rb
70
71
  - lib/rubocop/cop/rspec/be_eql.rb
71
72
  - lib/rubocop/cop/rspec/be_nil.rb
@@ -119,6 +120,7 @@ files:
119
120
  - lib/rubocop/cop/rspec/implicit_block_expectation.rb
120
121
  - lib/rubocop/cop/rspec/implicit_expect.rb
121
122
  - lib/rubocop/cop/rspec/implicit_subject.rb
123
+ - lib/rubocop/cop/rspec/indexed_let.rb
122
124
  - lib/rubocop/cop/rspec/instance_spy.rb
123
125
  - lib/rubocop/cop/rspec/instance_variable.rb
124
126
  - lib/rubocop/cop/rspec/it_behaves_like.rb
@@ -227,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
229
  - !ruby/object:Gem::Version
228
230
  version: '0'
229
231
  requirements: []
230
- rubygems_version: 3.3.26
232
+ rubygems_version: 3.4.10
231
233
  signing_key:
232
234
  specification_version: 4
233
235
  summary: Code style checking for RSpec files