rubocop-rspec 2.15.0 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.
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