rubocop-rspec 2.15.0 → 2.16.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/config/default.yml +43 -17
  4. data/lib/rubocop/cop/rspec/be.rb +2 -0
  5. data/lib/rubocop/cop/rspec/be_eq.rb +3 -0
  6. data/lib/rubocop/cop/rspec/be_eql.rb +3 -0
  7. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +3 -3
  8. data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +1 -1
  9. data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +1 -1
  10. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +1 -1
  11. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +3 -5
  12. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +1 -1
  13. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +58 -0
  14. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -0
  15. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -0
  16. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +1 -1
  17. data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +5 -3
  18. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +9 -2
  19. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -1
  20. data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +74 -0
  21. data/lib/rubocop/cop/rspec/implicit_expect.rb +2 -0
  22. data/lib/rubocop/cop/rspec/leading_subject.rb +2 -2
  23. data/lib/rubocop/cop/rspec/message_spies.rb +2 -0
  24. data/lib/rubocop/cop/rspec/mixin/metadata.rb +49 -0
  25. data/lib/rubocop/cop/rspec/pending_without_reason.rb +123 -0
  26. data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -0
  27. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +5 -2
  28. data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +1 -1
  29. data/lib/rubocop/cop/rspec/scattered_setup.rb +2 -0
  30. data/lib/rubocop/cop/rspec/sort_metadata.rb +4 -35
  31. data/lib/rubocop/cop/rspec/stubbed_mock.rb +2 -0
  32. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -0
  33. data/lib/rubocop/cop/rspec_cops.rb +3 -0
  34. data/lib/rubocop/rspec/config_formatter.rb +2 -2
  35. data/lib/rubocop/rspec/description_extractor.rb +5 -10
  36. data/lib/rubocop/rspec/language.rb +6 -2
  37. data/lib/rubocop/rspec/version.rb +1 -1
  38. data/lib/rubocop-rspec.rb +18 -13
  39. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e120f8d06ef760cbe4f3a3d077ca6cd8ceeb274a26042a6e1469fe9a280c969
4
- data.tar.gz: 0c80cc155e8f64b03a97de1327e454d9ff2efb1a75b02e3b098f868afed30d17
3
+ metadata.gz: c31ad963289938a78bf4a8439ad44c6f1a2eaf852a25d715be483ebe71e622fc
4
+ data.tar.gz: 70b4e942004dd6654fa12c3ecaf7367c1f6bcf4bcd122573129984f5f0a66ff5
5
5
  SHA512:
6
- metadata.gz: e982f97a23de2473c5cae6cd20bb653bcbcee806abddf0dda09c7b30692193858fc105c7b46ce3c004f28a7f680d1b3a475298c7b10d0e1499d5b80333fde504
7
- data.tar.gz: c3b79b39a2c98d9103cb4edbf6c2d8e2cc957790fd8fa617e1fdece172f3dfaaf652bf482e7236a1da663f3b9afae943c17efee1e4a5f0293acc771bbc1c3c43
6
+ metadata.gz: 250a68082a8e6406df3934035e5ccba4e1a7f9a13e021e196911c15981a2f4557e7b7a98efa93f995ad164d2a52e41bf948f4c1fa7c9637829c3c219c8116f74
7
+ data.tar.gz: 7ce965fc56bf214eb65b63d2138cc710766ac16b476a63399271cc5562fb259cefc81cddcf3878f6324faf104d6552e48efcba2eff1fa0865bf463963e5a1a8e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.16.0 (2022-12-13)
6
+
7
+ - Add new `RSpec/FactoryBot/FactoryNameStyle` cop. ([@ydah])
8
+ - Improved processing speed for `RSpec/Be`, `RSpec/ExpectActual`, `RSpec/ImplicitExpect`, `RSpec/MessageSpies`, `RSpec/PredicateMatcher` and `RSpec/Rails/HaveHttpStatus`. ([@ydah])
9
+ - Fix wrong autocorrection in `n_times` style on `RSpec/FactoryBot/CreateList`. ([@r7kamura])
10
+ - Fix a false positive for `RSpec/FactoryBot/ConsistentParenthesesStyle` when using `generate` with multiple arguments. ([@ydah])
11
+ - Mark `RSpec/BeEq` as `Safe: false` ([@r7kamura])
12
+ - Add `RSpec/DuplicatedMetadata` cop. ([@r7kamura])
13
+ - Mark `RSpec/BeEql` as `Safe: false`. ([@r7kamura])
14
+ - Add `RSpec/PendingWithoutReason` cop. ([@r7kamura])
15
+
5
16
  ## 2.15.0 (2022-11-03)
6
17
 
7
18
  - Fix a false positive for `RSpec/RepeatedDescription` when different its block expectations are used. ([@ydah])
data/config/default.yml CHANGED
@@ -147,13 +147,17 @@ RSpec/Be:
147
147
  RSpec/BeEq:
148
148
  Description: Check for expectations where `be(...)` can replace `eq(...)`.
149
149
  Enabled: pending
150
+ Safe: false
150
151
  VersionAdded: 2.9.0
152
+ VersionChanged: '2.16'
151
153
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEq
152
154
 
153
155
  RSpec/BeEql:
154
156
  Description: Check for expectations where `be(...)` can replace `eql(...)`.
155
157
  Enabled: true
158
+ Safe: false
156
159
  VersionAdded: '1.7'
160
+ VersionChanged: '2.16'
157
161
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEql
158
162
 
159
163
  RSpec/BeNil:
@@ -284,6 +288,12 @@ RSpec/Dialect:
284
288
  VersionAdded: '1.33'
285
289
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Dialect
286
290
 
291
+ RSpec/DuplicatedMetadata:
292
+ Description: Avoid duplicated metadata.
293
+ Enabled: pending
294
+ VersionAdded: '2.16'
295
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DuplicatedMetadata
296
+
287
297
  RSpec/EmptyExampleGroup:
288
298
  Description: Checks if an example group does not include any tests.
289
299
  Enabled: true
@@ -356,12 +366,6 @@ RSpec/ExampleWithoutDescription:
356
366
  VersionAdded: '1.22'
357
367
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWithoutDescription
358
368
 
359
- RSpec/ExcessiveDocstringSpacing:
360
- Description: Checks for excessive whitespace in example descriptions.
361
- Enabled: pending
362
- VersionAdded: '2.5'
363
- Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExcessiveDocstringSpacing
364
-
365
369
  RSpec/ExampleWording:
366
370
  Description: Checks for common mistakes in example descriptions.
367
371
  Enabled: true
@@ -378,6 +382,12 @@ RSpec/ExampleWording:
378
382
  StyleGuide: https://rspec.rubystyle.guide/#should-in-example-docstrings
379
383
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWording
380
384
 
385
+ RSpec/ExcessiveDocstringSpacing:
386
+ Description: Checks for excessive whitespace in example descriptions.
387
+ Enabled: pending
388
+ VersionAdded: '2.5'
389
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExcessiveDocstringSpacing
390
+
381
391
  RSpec/ExpectActual:
382
392
  Description: Checks for `expect(...)` calls containing literal values.
383
393
  Enabled: true
@@ -664,6 +674,12 @@ RSpec/Pending:
664
674
  VersionAdded: '1.25'
665
675
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Pending
666
676
 
677
+ RSpec/PendingWithoutReason:
678
+ Description: Checks for pending or skipped examples without reason.
679
+ Enabled: pending
680
+ VersionAdded: '2.16'
681
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/PendingWithoutReason
682
+
667
683
  RSpec/PredicateMatcher:
668
684
  Description: Prefer using predicate matcher over using predicate method directly.
669
685
  Enabled: true
@@ -960,6 +976,16 @@ RSpec/FactoryBot/FactoryClassName:
960
976
  VersionChanged: '2.0'
961
977
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName
962
978
 
979
+ RSpec/FactoryBot/FactoryNameStyle:
980
+ Description: Checks for name style for argument of FactoryBot::Syntax::Methods.
981
+ Enabled: pending
982
+ VersionAdded: '2.16'
983
+ EnforcedStyle: symbol
984
+ SupportedStyles:
985
+ - symbol
986
+ - string
987
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryNameStyle
988
+
963
989
  RSpec/FactoryBot/SyntaxMethods:
964
990
  Description: Use shorthands from `FactoryBot::Syntax::Methods` in your specs.
965
991
  Enabled: pending
@@ -985,6 +1011,17 @@ RSpec/Rails/HaveHttpStatus:
985
1011
  VersionAdded: '2.12'
986
1012
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
987
1013
 
1014
+ RSpec/Rails/HttpStatus:
1015
+ Description: Enforces use of symbolic or numeric value to describe HTTP status.
1016
+ Enabled: true
1017
+ EnforcedStyle: symbolic
1018
+ SupportedStyles:
1019
+ - numeric
1020
+ - symbolic
1021
+ VersionAdded: '1.23'
1022
+ VersionChanged: '2.0'
1023
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus
1024
+
988
1025
  RSpec/Rails/InferredSpecType:
989
1026
  Description: Identifies redundant spec type.
990
1027
  Enabled: pending
@@ -1007,14 +1044,3 @@ RSpec/Rails/InferredSpecType:
1007
1044
  routing: routing
1008
1045
  system: system
1009
1046
  views: view
1010
-
1011
- RSpec/Rails/HttpStatus:
1012
- Description: Enforces use of symbolic or numeric value to describe HTTP status.
1013
- Enabled: true
1014
- EnforcedStyle: symbolic
1015
- SupportedStyles:
1016
- - numeric
1017
- - symbolic
1018
- VersionAdded: '1.23'
1019
- VersionChanged: '2.0'
1020
- Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus
@@ -21,6 +21,8 @@ module RuboCop
21
21
  class Be < Base
22
22
  MSG = "Don't use `be` without an argument."
23
23
 
24
+ RESTRICT_ON_SEND = Runners.all
25
+
24
26
  # @!method be_without_args(node)
25
27
  def_node_matcher :be_without_args, <<-PATTERN
26
28
  (send _ #Runners.all $(send nil? :be))
@@ -9,6 +9,9 @@ module RuboCop
9
9
  # using `==`. Booleans and nil can be compared by identity and therefore
10
10
  # the `be` matcher is preferable as it is a more strict test.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because it changes how values are compared.
14
+ #
12
15
  # @example
13
16
  # # bad
14
17
  # expect(foo).to eq(true)
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # can be compared by identity and therefore the `be` matcher is
11
11
  # preferable as it is a more strict test.
12
12
  #
13
+ # @safety
14
+ # This cop is unsafe because it changes how values are compared.
15
+ #
13
16
  # @example
14
17
  # # bad
15
18
  # expect(foo).to eql(1)
@@ -29,7 +29,7 @@ module RuboCop
29
29
  # # good
30
30
  # expect(page).to have_current_path('/callback')
31
31
  #
32
- class CurrentPathExpectation < Base
32
+ class CurrentPathExpectation < ::RuboCop::Cop::Base
33
33
  extend AutoCorrector
34
34
 
35
35
  MSG = 'Do not set an RSpec expectation on `current_path` in ' \
@@ -47,14 +47,14 @@ module RuboCop
47
47
  # @!method as_is_matcher(node)
48
48
  def_node_matcher :as_is_matcher, <<-PATTERN
49
49
  (send
50
- #expectation_set_on_current_path $#Runners.all
50
+ #expectation_set_on_current_path ${:to :to_not :not_to}
51
51
  ${(send nil? :eq ...) (send nil? :match (regexp ...))})
52
52
  PATTERN
53
53
 
54
54
  # @!method regexp_str_matcher(node)
55
55
  def_node_matcher :regexp_str_matcher, <<-PATTERN
56
56
  (send
57
- #expectation_set_on_current_path $#Runners.all
57
+ #expectation_set_on_current_path ${:to :to_not :not_to}
58
58
  $(send nil? :match (str $_)))
59
59
  PATTERN
60
60
 
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # expect(page).to have_no_selector
25
25
  # expect(page).to have_no_css('a')
26
26
  #
27
- class NegationMatcher < Base
27
+ class NegationMatcher < ::RuboCop::Cop::Base
28
28
  extend AutoCorrector
29
29
  include ConfigurableEnforcedStyle
30
30
 
@@ -20,7 +20,7 @@ module RuboCop
20
20
  # click_link(exact_text: 'foo')
21
21
  # find('div').click_button
22
22
  #
23
- class SpecificActions < Base
23
+ class SpecificActions < ::RuboCop::Cop::Base
24
24
  MSG = "Prefer `%<good_action>s` over `find('%<selector>s').click`."
25
25
  RESTRICT_ON_SEND = %i[click].freeze
26
26
  SPECIFIC_ACTION = {
@@ -15,7 +15,7 @@ module RuboCop
15
15
  # find_by_id('some-id')
16
16
  # find_by_id('some-id', visible: true)
17
17
  #
18
- class SpecificFinders < Base
18
+ class SpecificFinders < ::RuboCop::Cop::Base
19
19
  extend AutoCorrector
20
20
 
21
21
  include RangeHelp
@@ -26,9 +26,7 @@ module RuboCop
26
26
  # expect(page).to have_select
27
27
  # expect(page).to have_field('foo')
28
28
  #
29
- class SpecificMatcher < Base
30
- include CapybaraHelp
31
-
29
+ class SpecificMatcher < ::RuboCop::Cop::Base
32
30
  MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
33
31
  RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
34
32
  have_no_css].freeze
@@ -49,8 +47,8 @@ module RuboCop
49
47
  first_argument(node) do |arg|
50
48
  next unless (matcher = specific_matcher(arg))
51
49
  next if CssSelector.multiple_selectors?(arg)
52
- next unless specific_option?(node, arg, matcher)
53
- next unless specific_pseudo_classes?(arg)
50
+ next unless CapybaraHelp.specific_option?(node, arg, matcher)
51
+ next unless CapybaraHelp.specific_pseudo_classes?(arg)
54
52
 
55
53
  add_offense(node, message: message(node, matcher))
56
54
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
  # expect(page).to have_css('.foo', visible: :all)
27
27
  # expect(page).to have_link('my link', visible: :hidden)
28
28
  #
29
- class VisibilityMatcher < Base
29
+ class VisibilityMatcher < ::RuboCop::Cop::Base
30
30
  MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
31
31
  MSG_TRUE = 'Use `:visible` instead of `true`.'
32
32
  CAPYBARA_MATCHER_METHODS = %w[
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Avoid duplicated metadata.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # describe 'Something', :a, :a
11
+ #
12
+ # # good
13
+ # describe 'Something', :a
14
+ class DuplicatedMetadata < Base
15
+ extend AutoCorrector
16
+
17
+ include Metadata
18
+ include RangeHelp
19
+
20
+ MSG = 'Avoid duplicated metadata.'
21
+
22
+ def on_metadata(symbols, _pairs)
23
+ symbols.each do |symbol|
24
+ on_metadata_symbol(symbol)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def on_metadata_symbol(node)
31
+ return unless duplicated?(node)
32
+
33
+ add_offense(node) do |corrector|
34
+ autocorrect(corrector, node)
35
+ end
36
+ end
37
+
38
+ def autocorrect(corrector, node)
39
+ corrector.remove(
40
+ range_with_surrounding_comma(
41
+ range_with_surrounding_space(
42
+ node.location.expression,
43
+ side: :left
44
+ ),
45
+ :left
46
+ )
47
+ )
48
+ end
49
+
50
+ def duplicated?(node)
51
+ node.left_siblings.any? do |sibling|
52
+ sibling.eql?(node)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -55,6 +55,8 @@ module RuboCop
55
55
  end
56
56
  end
57
57
 
58
+ private
59
+
58
60
  def allowed_one_liner?(node)
59
61
  consecutive_one_liner?(node) && allow_consecutive_one_liners?
60
62
  end
@@ -26,6 +26,8 @@ module RuboCop
26
26
 
27
27
  MSG = 'Provide the actual you are testing to `expect(...)`.'
28
28
 
29
+ RESTRICT_ON_SEND = Runners.all
30
+
29
31
  SIMPLE_LITERALS = %i[
30
32
  true
31
33
  false
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # # good
26
26
  # count { 1 }
27
27
  #
28
- class AttributeDefinedStatically < Base
28
+ class AttributeDefinedStatically < ::RuboCop::Cop::Base
29
29
  extend AutoCorrector
30
30
 
31
31
  MSG = 'Use a block to declare attribute values.'
@@ -40,7 +40,7 @@ module RuboCop
40
40
  # name: 'foo'
41
41
  # )
42
42
  #
43
- class ConsistentParenthesesStyle < Base
43
+ class ConsistentParenthesesStyle < ::RuboCop::Cop::Base
44
44
  extend AutoCorrector
45
45
  include ConfigurableEnforcedStyle
46
46
  include RuboCop::RSpec::FactoryBot::Language
@@ -69,6 +69,8 @@ module RuboCop
69
69
  return if ambiguous_without_parentheses?(node)
70
70
 
71
71
  factory_call(node) do
72
+ return if node.method?(:generate) && node.arguments.count > 1
73
+
72
74
  if node.parenthesized?
73
75
  process_with_parentheses(node)
74
76
  else
@@ -77,6 +79,8 @@ module RuboCop
77
79
  end
78
80
  end
79
81
 
82
+ private
83
+
80
84
  def process_with_parentheses(node)
81
85
  return unless style == :omit_parentheses
82
86
  return unless same_line?(node, node.first_argument)
@@ -102,8 +106,6 @@ module RuboCop
102
106
  node.parent&.array_type?
103
107
  end
104
108
 
105
- private
106
-
107
109
  def remove_parentheses(corrector, node)
108
110
  corrector.replace(node.location.begin, ' ')
109
111
  corrector.remove(node.location.end)
@@ -31,7 +31,7 @@ module RuboCop
31
31
  # # good
32
32
  # 3.times { create :user }
33
33
  #
34
- class CreateList < Base
34
+ class CreateList < ::RuboCop::Cop::Base
35
35
  extend AutoCorrector
36
36
  include ConfigurableEnforcedStyle
37
37
  include RuboCop::RSpec::FactoryBot::Language
@@ -143,7 +143,7 @@ module RuboCop
143
143
 
144
144
  def call(corrector)
145
145
  replacement = generate_n_times_block(node)
146
- corrector.replace(node, replacement)
146
+ corrector.replace(node.block_node || node, replacement)
147
147
  end
148
148
 
149
149
  private
@@ -159,8 +159,15 @@ module RuboCop
159
159
 
160
160
  replacement = format_receiver(node.receiver)
161
161
  replacement += format_method_call(node, 'create', arguments)
162
+ replacement += " #{factory_call_block_source}" if node.block_node
162
163
  "#{count.source}.times { #{replacement} }"
163
164
  end
165
+
166
+ def factory_call_block_source
167
+ node.block_node.location.begin.with(
168
+ end_pos: node.block_node.location.end.end_pos
169
+ ).source
170
+ end
164
171
  end
165
172
 
166
173
  # :nodoc:
@@ -20,7 +20,7 @@ module RuboCop
20
20
  # factory :foo, class: 'Foo' do
21
21
  # end
22
22
  #
23
- class FactoryClassName < Base
23
+ class FactoryClassName < ::RuboCop::Cop::Base
24
24
  extend AutoCorrector
25
25
 
26
26
  MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module FactoryBot
7
+ # Checks for name style for argument of FactoryBot::Syntax::Methods.
8
+ #
9
+ # @example EnforcedStyle: symbol (default)
10
+ # # bad
11
+ # create('user')
12
+ # build "user", username: "NAME"
13
+ #
14
+ # # good
15
+ # create(:user)
16
+ # build :user, username: "NAME"
17
+ #
18
+ # @example EnforcedStyle: string
19
+ # # bad
20
+ # create(:user)
21
+ # build :user, username: "NAME"
22
+ #
23
+ # # good
24
+ # create('user')
25
+ # build "user", username: "NAME"
26
+ #
27
+ class FactoryNameStyle < ::RuboCop::Cop::Base
28
+ extend AutoCorrector
29
+ include ConfigurableEnforcedStyle
30
+ include RuboCop::RSpec::FactoryBot::Language
31
+
32
+ MSG = 'Use %<prefer>s to refer to a factory.'
33
+ FACTORY_CALLS = RuboCop::RSpec::FactoryBot::Language::METHODS
34
+ RESTRICT_ON_SEND = FACTORY_CALLS
35
+
36
+ # @!method factory_call(node)
37
+ def_node_matcher :factory_call, <<-PATTERN
38
+ (send
39
+ {#factory_bot? nil?} %FACTORY_CALLS
40
+ ${str sym} ...
41
+ )
42
+ PATTERN
43
+
44
+ def on_send(node)
45
+ factory_call(node) do |name|
46
+ if offense_for_symbol_style?(name)
47
+ register_offense(name, name.value.to_sym.inspect)
48
+ elsif offense_for_string_style?(name)
49
+ register_offense(name, name.value.to_s.inspect)
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def offense_for_symbol_style?(name)
57
+ name.str_type? && style == :symbol
58
+ end
59
+
60
+ def offense_for_string_style?(name)
61
+ name.sym_type? && style == :string
62
+ end
63
+
64
+ def register_offense(name, prefer)
65
+ add_offense(name,
66
+ message: format(MSG, prefer: style.to_s)) do |corrector|
67
+ corrector.replace(name, prefer)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -28,6 +28,8 @@ module RuboCop
28
28
 
29
29
  MSG = 'Prefer `%<good>s` over `%<bad>s`.'
30
30
 
31
+ RESTRICT_ON_SEND = Runners.all + %i[should should_not]
32
+
31
33
  # @!method implicit_expect(node)
32
34
  def_node_matcher :implicit_expect, <<-PATTERN
33
35
  {
@@ -44,6 +44,8 @@ module RuboCop
44
44
  check_previous_nodes(node)
45
45
  end
46
46
 
47
+ private
48
+
47
49
  def check_previous_nodes(node)
48
50
  offending_node(node) do |offender|
49
51
  msg = format(MSG, offending: offender.method_name)
@@ -53,8 +55,6 @@ module RuboCop
53
55
  end
54
56
  end
55
57
 
56
- private
57
-
58
58
  def offending_node(node)
59
59
  parent(node).each_child_node.find do |sibling|
60
60
  break if sibling.equal?(node)
@@ -41,6 +41,8 @@ module RuboCop
41
41
 
42
42
  SUPPORTED_STYLES = %w[have_received receive].freeze
43
43
 
44
+ RESTRICT_ON_SEND = Runners.all
45
+
44
46
  # @!method message_expectation(node)
45
47
  def_node_matcher :message_expectation, %(
46
48
  (send (send nil? :expect $_) #Runners.all ...)
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helper methods to find RSpec metadata.
7
+ module Metadata
8
+ extend RuboCop::NodePattern::Macros
9
+
10
+ include RuboCop::RSpec::Language
11
+
12
+ # @!method rspec_metadata(node)
13
+ def_node_matcher :rspec_metadata, <<~PATTERN
14
+ (block
15
+ (send
16
+ #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ ${send str sym}* (hash $...)?)
17
+ ...)
18
+ PATTERN
19
+
20
+ # @!method rspec_configure(node)
21
+ def_node_matcher :rspec_configure, <<~PATTERN
22
+ (block (send #rspec? :configure) (args (arg $_)) ...)
23
+ PATTERN
24
+
25
+ # @!method metadata_in_block(node)
26
+ def_node_search :metadata_in_block, <<~PATTERN
27
+ (send (lvar %) #Hooks.all _ ${send str sym}* (hash $...)?)
28
+ PATTERN
29
+
30
+ def on_block(node)
31
+ rspec_configure(node) do |block_var|
32
+ metadata_in_block(node, block_var) do |symbols, pairs|
33
+ on_metadata(symbols, pairs.flatten)
34
+ end
35
+ end
36
+
37
+ rspec_metadata(node) do |symbols, pairs|
38
+ on_metadata(symbols, pairs.flatten)
39
+ end
40
+ end
41
+ alias on_numblock on_block
42
+
43
+ def on_metadata(_symbols, _pairs)
44
+ raise ::NotImplementedError
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for pending or skipped examples without reason.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # pending 'does something' do
11
+ # end
12
+ #
13
+ # # bad
14
+ # it 'does something', :pending do
15
+ # end
16
+ #
17
+ # # bad
18
+ # it 'does something' do
19
+ # pending
20
+ # end
21
+ #
22
+ # # bad
23
+ # xdescribe 'something' do
24
+ # end
25
+ #
26
+ # # bad
27
+ # skip 'does something' do
28
+ # end
29
+ #
30
+ # # bad
31
+ # it 'does something', :skip do
32
+ # end
33
+ #
34
+ # # bad
35
+ # it 'does something' do
36
+ # skip
37
+ # end
38
+ #
39
+ # # bad
40
+ # it 'does something'
41
+ #
42
+ # # good
43
+ # it 'does something' do
44
+ # pending 'reason'
45
+ # end
46
+ #
47
+ # # good
48
+ # it 'does something' do
49
+ # skip 'reason'
50
+ # end
51
+ #
52
+ # # good
53
+ # it 'does something', pending: 'reason' do
54
+ # end
55
+ #
56
+ # # good
57
+ # it 'does something', skip: 'reason' do
58
+ # end
59
+ class PendingWithoutReason < Base
60
+ MSG = 'Give the reason for pending or skip.'
61
+
62
+ # @!method pending_by_example_method?(node)
63
+ def_node_matcher :pending_by_example_method?, block_pattern(<<~PATTERN)
64
+ #Examples.pending
65
+ PATTERN
66
+
67
+ # @!method pending_by_metadata_without_reason?(node)
68
+ def_node_matcher :pending_by_metadata_without_reason?, <<~PATTERN
69
+ (send #rspec? {#ExampleGroups.all #Examples.all} ... {<(sym :pending) ...> (hash <(pair (sym :pending) true) ...>)})
70
+ PATTERN
71
+
72
+ # @!method skipped_by_example_method?(node)
73
+ def_node_matcher :skipped_by_example_method?, block_pattern(<<~PATTERN)
74
+ #Examples.skipped
75
+ PATTERN
76
+
77
+ # @!method skipped_by_example_group_method?(node)
78
+ def_node_matcher(
79
+ :skipped_by_example_group_method?,
80
+ block_pattern(<<~PATTERN)
81
+ #ExampleGroups.skipped
82
+ PATTERN
83
+ )
84
+
85
+ # @!method skipped_by_metadata_without_reason?(node)
86
+ def_node_matcher :skipped_by_metadata_without_reason?, <<~PATTERN
87
+ (send #rspec? {#ExampleGroups.all #Examples.all} ... {<(sym :skip) ...> (hash <(pair (sym :skip) true) ...>)})
88
+ PATTERN
89
+
90
+ def on_send(node)
91
+ if pending_without_reason?(node)
92
+ add_offense(node, message: 'Give the reason for pending.')
93
+ elsif skipped_without_reason?(node)
94
+ add_offense(node, message: 'Give the reason for skip.')
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def pending_by_pending_step_without_reason?(node)
101
+ node.method?(:pending) && node.first_argument.nil?
102
+ end
103
+
104
+ def pending_without_reason?(node)
105
+ pending_by_example_method?(node.block_node) ||
106
+ pending_by_metadata_without_reason?(node) ||
107
+ pending_by_pending_step_without_reason?(node)
108
+ end
109
+
110
+ def skipped_by_skip_step_without_reason?(node)
111
+ node.method?(:skip) && node.first_argument.nil?
112
+ end
113
+
114
+ def skipped_without_reason?(node)
115
+ skipped_by_example_group_method?(node.block_node) ||
116
+ skipped_by_example_method?(node.block_node) ||
117
+ skipped_by_metadata_without_reason?(node) ||
118
+ skipped_by_skip_step_without_reason?(node)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -283,6 +283,8 @@ module RuboCop
283
283
  include InflectedHelper
284
284
  include ExplicitHelper
285
285
 
286
+ RESTRICT_ON_SEND = Runners.all
287
+
286
288
  def on_send(node)
287
289
  case style
288
290
  when :inflected
@@ -13,20 +13,23 @@ module RuboCop
13
13
  # # good
14
14
  # expect(response).to have_http_status(200)
15
15
  #
16
- class HaveHttpStatus < Base
16
+ class HaveHttpStatus < ::RuboCop::Cop::Base
17
17
  extend AutoCorrector
18
18
 
19
19
  MSG =
20
20
  'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
21
21
  'over `expect(response.status).%<to>s %<match>s`.'
22
22
 
23
+ RUNNERS = %i[to to_not not_to].to_set
24
+ RESTRICT_ON_SEND = RUNNERS
25
+
23
26
  # @!method match_status(node)
24
27
  def_node_matcher :match_status, <<-PATTERN
25
28
  (send
26
29
  (send nil? :expect
27
30
  $(send (send nil? :response) :status)
28
31
  )
29
- $#Runners.all
32
+ $RUNNERS
30
33
  $(send nil? {:be :eq :eql :equal} (int $_))
31
34
  )
32
35
  PATTERN
@@ -33,7 +33,7 @@ module RuboCop
33
33
  #
34
34
  # @example `Inferences` configuration
35
35
  # # .rubocop.yml
36
- # # RSpec/InferredSpecType:
36
+ # # RSpec/Rails/InferredSpecType:
37
37
  # # Inferences:
38
38
  # # services: service
39
39
  #
@@ -41,6 +41,8 @@ module RuboCop
41
41
  end
42
42
  end
43
43
 
44
+ private
45
+
44
46
  def repeated_hooks(node)
45
47
  hooks = RuboCop::RSpec::ExampleGroup.new(node)
46
48
  .hooks
@@ -18,45 +18,12 @@ module RuboCop
18
18
  #
19
19
  class SortMetadata < Base
20
20
  extend AutoCorrector
21
+ include Metadata
21
22
  include RangeHelp
22
23
 
23
24
  MSG = 'Sort metadata alphabetically.'
24
25
 
25
- # @!method rspec_metadata(node)
26
- def_node_matcher :rspec_metadata, <<~PATTERN
27
- (block
28
- (send
29
- #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ ${send str sym}* (hash $...)?)
30
- ...)
31
- PATTERN
32
-
33
- # @!method rspec_configure(node)
34
- def_node_matcher :rspec_configure, <<~PATTERN
35
- (block (send #rspec? :configure) (args (arg $_)) ...)
36
- PATTERN
37
-
38
- # @!method metadata_in_block(node)
39
- def_node_search :metadata_in_block, <<~PATTERN
40
- (send (lvar %) #Hooks.all _ ${send str sym}* (hash $...)?)
41
- PATTERN
42
-
43
- def on_block(node)
44
- rspec_configure(node) do |block_var|
45
- metadata_in_block(node, block_var) do |symbols, pairs|
46
- investigate(symbols, pairs.flatten)
47
- end
48
- end
49
-
50
- rspec_metadata(node) do |symbols, pairs|
51
- investigate(symbols, pairs.flatten)
52
- end
53
- end
54
-
55
- alias on_numblock on_block
56
-
57
- private
58
-
59
- def investigate(symbols, pairs)
26
+ def on_metadata(symbols, pairs)
60
27
  return if sorted?(symbols, pairs)
61
28
 
62
29
  crime_scene = crime_scene(symbols, pairs)
@@ -65,6 +32,8 @@ module RuboCop
65
32
  end
66
33
  end
67
34
 
35
+ private
36
+
68
37
  def crime_scene(symbols, pairs)
69
38
  metadata = symbols + pairs
70
39
 
@@ -133,6 +133,8 @@ module RuboCop
133
133
  }
134
134
  PATTERN
135
135
 
136
+ RESTRICT_ON_SEND = %i[to].freeze
137
+
136
138
  def on_send(node)
137
139
  expectation(node, &method(:on_expectation))
138
140
  end
@@ -50,6 +50,8 @@ module RuboCop
50
50
  add_offense(node.children.last)
51
51
  end
52
52
 
53
+ private
54
+
53
55
  def empty_exception_matcher?(node)
54
56
  empty_raise_error_or_exception(node) && !block_with_args?(node.parent)
55
57
  end
@@ -12,6 +12,7 @@ require_relative 'rspec/factory_bot/attribute_defined_statically'
12
12
  require_relative 'rspec/factory_bot/consistent_parentheses_style'
13
13
  require_relative 'rspec/factory_bot/create_list'
14
14
  require_relative 'rspec/factory_bot/factory_class_name'
15
+ require_relative 'rspec/factory_bot/factory_name_style'
15
16
  require_relative 'rspec/factory_bot/syntax_methods'
16
17
 
17
18
  require_relative 'rspec/rails/avoid_setup_hook'
@@ -42,6 +43,7 @@ require_relative 'rspec/describe_symbol'
42
43
  require_relative 'rspec/described_class'
43
44
  require_relative 'rspec/described_class_module_wrapping'
44
45
  require_relative 'rspec/dialect'
46
+ require_relative 'rspec/duplicated_metadata'
45
47
  require_relative 'rspec/empty_example_group'
46
48
  require_relative 'rspec/empty_hook'
47
49
  require_relative 'rspec/empty_line_after_example'
@@ -87,6 +89,7 @@ require_relative 'rspec/no_expectation_example'
87
89
  require_relative 'rspec/not_to_not'
88
90
  require_relative 'rspec/overwriting_setup'
89
91
  require_relative 'rspec/pending'
92
+ require_relative 'rspec/pending_without_reason'
90
93
  require_relative 'rspec/predicate_matcher'
91
94
  require_relative 'rspec/receive_counts'
92
95
  require_relative 'rspec/receive_never'
@@ -9,7 +9,7 @@ module RuboCop
9
9
  EXTENSION_ROOT_DEPARTMENT = %r{^(RSpec/)}.freeze
10
10
  SUBDEPARTMENTS = %(RSpec/Capybara RSpec/FactoryBot RSpec/Rails)
11
11
  AMENDMENTS = %(Metrics/BlockLength)
12
- COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/'
12
+ COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/'
13
13
 
14
14
  def initialize(config, descriptions)
15
15
  @config = config
@@ -47,7 +47,7 @@ module RuboCop
47
47
  end
48
48
 
49
49
  def reference(cop)
50
- COP_DOC_BASE_URL + cop.sub('RSpec/', '')
50
+ COP_DOC_BASE_URL + cop
51
51
  end
52
52
 
53
53
  attr_reader :config, :descriptions
@@ -21,7 +21,8 @@ module RuboCop
21
21
 
22
22
  # Decorator of a YARD code object for working with documented rspec cops
23
23
  class CodeObject
24
- COP_CLASS_NAME = 'RuboCop::Cop::RSpec::Base'
24
+ RSPEC_COP_CLASS_NAME = 'RuboCop::Cop::RSpec::Base'
25
+ RUBOCOP_COP_CLASS_NAME = 'RuboCop::Cop::Base'
25
26
  RSPEC_NAMESPACE = 'RuboCop::Cop::RSpec'
26
27
 
27
28
  def initialize(yardoc)
@@ -32,10 +33,7 @@ module RuboCop
32
33
  #
33
34
  # @return [Boolean]
34
35
  def rspec_cop?
35
- class_documentation? &&
36
- rspec_cop_namespace? &&
37
- cop_subclass? &&
38
- !abstract?
36
+ cop_subclass? && !abstract? && rspec_cop_namespace?
39
37
  end
40
38
 
41
39
  # Configuration for the documented cop that would live in default.yml
@@ -55,10 +53,6 @@ module RuboCop
55
53
  yardoc.docstring.split("\n\n").first.to_s
56
54
  end
57
55
 
58
- def class_documentation?
59
- yardoc.type.equal?(:class)
60
- end
61
-
62
56
  def rspec_cop_namespace?
63
57
  documented_constant.start_with?(RSPEC_NAMESPACE)
64
58
  end
@@ -68,7 +62,8 @@ module RuboCop
68
62
  end
69
63
 
70
64
  def cop_subclass?
71
- yardoc.superclass.path == COP_CLASS_NAME
65
+ yardoc.superclass.path == RSPEC_COP_CLASS_NAME ||
66
+ yardoc.superclass.path == RUBOCOP_COP_CLASS_NAME
72
67
  end
73
68
 
74
69
  def abstract?
@@ -160,8 +160,12 @@ module RuboCop
160
160
 
161
161
  module Runners # :nodoc:
162
162
  ALL = %i[to to_not not_to].freeze
163
- def self.all(element)
164
- ALL.include?(element)
163
+ class << self
164
+ def all(element = nil)
165
+ return ALL if element.nil?
166
+
167
+ ALL.include?(element)
168
+ end
165
169
  end
166
170
  end
167
171
 
@@ -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.15.0'
7
+ STRING = '2.16.0'
8
8
  end
9
9
  end
10
10
  end
data/lib/rubocop-rspec.rb CHANGED
@@ -6,34 +6,39 @@ require 'yaml'
6
6
  require 'rubocop'
7
7
 
8
8
  require_relative 'rubocop/rspec'
9
- require_relative 'rubocop/rspec/version'
10
9
  require_relative 'rubocop/rspec/inject'
10
+ require_relative 'rubocop/rspec/language/node_pattern'
11
11
  require_relative 'rubocop/rspec/node'
12
+ require_relative 'rubocop/rspec/version'
12
13
  require_relative 'rubocop/rspec/wording'
13
- require_relative 'rubocop/rspec/language/node_pattern'
14
+
15
+ # Dependent on `RuboCop::RSpec::Language::NodePattern`.
14
16
  require_relative 'rubocop/rspec/language'
15
17
 
16
18
  require_relative 'rubocop/rspec/factory_bot/language'
17
19
 
18
- require_relative 'rubocop/cop/rspec/mixin/top_level_group'
19
- require_relative 'rubocop/cop/rspec/mixin/variable'
20
+ require_relative 'rubocop/cop/rspec/mixin/capybara_help'
21
+ require_relative 'rubocop/cop/rspec/mixin/css_selector'
20
22
  require_relative 'rubocop/cop/rspec/mixin/final_end_location'
21
- require_relative 'rubocop/cop/rspec/mixin/comments_help'
22
- require_relative 'rubocop/cop/rspec/mixin/empty_line_separation'
23
23
  require_relative 'rubocop/cop/rspec/mixin/inside_example_group'
24
+ require_relative 'rubocop/cop/rspec/mixin/metadata'
24
25
  require_relative 'rubocop/cop/rspec/mixin/namespace'
25
- require_relative 'rubocop/cop/rspec/mixin/css_selector'
26
26
  require_relative 'rubocop/cop/rspec/mixin/skip_or_pending'
27
- require_relative 'rubocop/cop/rspec/mixin/capybara_help'
27
+ require_relative 'rubocop/cop/rspec/mixin/top_level_group'
28
+ require_relative 'rubocop/cop/rspec/mixin/variable'
29
+
30
+ # Dependent on `RuboCop::Cop::RSpec::FinalEndLocation`.
31
+ require_relative 'rubocop/cop/rspec/mixin/comments_help'
32
+ require_relative 'rubocop/cop/rspec/mixin/empty_line_separation'
28
33
 
29
- require_relative 'rubocop/rspec/concept'
30
- require_relative 'rubocop/rspec/example_group'
31
- require_relative 'rubocop/rspec/example'
32
- require_relative 'rubocop/rspec/hook'
33
34
  require_relative 'rubocop/cop/rspec/base'
34
35
  require_relative 'rubocop/rspec/align_let_brace'
35
- require_relative 'rubocop/rspec/factory_bot'
36
+ require_relative 'rubocop/rspec/concept'
36
37
  require_relative 'rubocop/rspec/corrector/move_node'
38
+ require_relative 'rubocop/rspec/example'
39
+ require_relative 'rubocop/rspec/example_group'
40
+ require_relative 'rubocop/rspec/factory_bot'
41
+ require_relative 'rubocop/rspec/hook'
37
42
 
38
43
  RuboCop::RSpec::Inject.defaults!
39
44
 
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.15.0
4
+ version: 2.16.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: 2022-11-03 00:00:00.000000000 Z
13
+ date: 2022-12-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -73,6 +73,7 @@ files:
73
73
  - lib/rubocop/cop/rspec/described_class.rb
74
74
  - lib/rubocop/cop/rspec/described_class_module_wrapping.rb
75
75
  - lib/rubocop/cop/rspec/dialect.rb
76
+ - lib/rubocop/cop/rspec/duplicated_metadata.rb
76
77
  - lib/rubocop/cop/rspec/empty_example_group.rb
77
78
  - lib/rubocop/cop/rspec/empty_hook.rb
78
79
  - lib/rubocop/cop/rspec/empty_line_after_example.rb
@@ -92,6 +93,7 @@ files:
92
93
  - lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb
93
94
  - lib/rubocop/cop/rspec/factory_bot/create_list.rb
94
95
  - lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb
96
+ - lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb
95
97
  - lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb
96
98
  - lib/rubocop/cop/rspec/file_path.rb
97
99
  - lib/rubocop/cop/rspec/focus.rb
@@ -119,6 +121,7 @@ files:
119
121
  - lib/rubocop/cop/rspec/mixin/empty_line_separation.rb
120
122
  - lib/rubocop/cop/rspec/mixin/final_end_location.rb
121
123
  - lib/rubocop/cop/rspec/mixin/inside_example_group.rb
124
+ - lib/rubocop/cop/rspec/mixin/metadata.rb
122
125
  - lib/rubocop/cop/rspec/mixin/namespace.rb
123
126
  - lib/rubocop/cop/rspec/mixin/skip_or_pending.rb
124
127
  - lib/rubocop/cop/rspec/mixin/top_level_group.rb
@@ -133,6 +136,7 @@ files:
133
136
  - lib/rubocop/cop/rspec/not_to_not.rb
134
137
  - lib/rubocop/cop/rspec/overwriting_setup.rb
135
138
  - lib/rubocop/cop/rspec/pending.rb
139
+ - lib/rubocop/cop/rspec/pending_without_reason.rb
136
140
  - lib/rubocop/cop/rspec/predicate_matcher.rb
137
141
  - lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb
138
142
  - lib/rubocop/cop/rspec/rails/have_http_status.rb