rubocop-rspec 2.22.0 → 2.27.1

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +94 -9
  3. data/README.md +1 -1
  4. data/config/default.yml +126 -21
  5. data/lib/rubocop/cop/rspec/around_block.rb +3 -3
  6. data/lib/rubocop/cop/rspec/be.rb +1 -1
  7. data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
  8. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  9. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  10. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  11. data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
  12. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -2
  13. data/lib/rubocop/cop/rspec/change_by_zero.rb +30 -4
  14. data/lib/rubocop/cop/rspec/context_method.rb +2 -2
  15. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  16. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  17. data/lib/rubocop/cop/rspec/described_class.rb +33 -11
  18. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +1 -1
  19. data/lib/rubocop/cop/rspec/empty_example_group.rb +4 -1
  20. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
  21. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  22. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  23. data/lib/rubocop/cop/rspec/example_length.rb +11 -5
  24. data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
  25. data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
  26. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
  27. data/lib/rubocop/cop/rspec/expect_actual.rb +7 -4
  28. data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
  29. data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
  30. data/lib/rubocop/cop/rspec/file_path.rb +6 -0
  31. data/lib/rubocop/cop/rspec/focus.rb +17 -2
  32. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  33. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  34. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
  35. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  36. data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
  37. data/lib/rubocop/cop/rspec/indexed_let.rb +32 -1
  38. data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
  39. data/lib/rubocop/cop/rspec/instance_variable.rb +3 -3
  40. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  41. data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
  42. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  43. data/lib/rubocop/cop/rspec/let_before_examples.rb +5 -1
  44. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  45. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -2
  46. data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
  47. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  48. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  49. data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
  50. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
  51. data/lib/rubocop/cop/rspec/multiple_expectations.rb +12 -7
  52. data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
  53. data/lib/rubocop/cop/rspec/pending.rb +12 -2
  54. data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
  55. data/lib/rubocop/cop/rspec/predicate_matcher.rb +9 -9
  56. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -1
  57. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +34 -10
  58. data/lib/rubocop/cop/rspec/rails/http_status.rb +29 -18
  59. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +314 -22
  60. data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
  61. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  62. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  63. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  64. data/lib/rubocop/cop/rspec/remove_const.rb +40 -0
  65. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
  66. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
  67. data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
  68. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +124 -0
  69. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  70. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  71. data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
  72. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -3
  73. data/lib/rubocop/cop/rspec/sort_metadata.rb +2 -1
  74. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  75. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  76. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  77. data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
  78. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
  79. data/lib/rubocop/cop/rspec/variable_definition.rb +4 -4
  80. data/lib/rubocop/cop/rspec/verified_double_reference.rb +6 -6
  81. data/lib/rubocop/cop/rspec/verified_doubles.rb +2 -2
  82. data/lib/rubocop/cop/rspec/void_expect.rb +4 -3
  83. data/lib/rubocop/cop/rspec_cops.rb +11 -0
  84. data/lib/rubocop/rspec/language.rb +8 -8
  85. data/lib/rubocop/rspec/version.rb +1 -1
  86. data/lib/rubocop/rspec/wording.rb +8 -0
  87. data/lib/rubocop-rspec.rb +1 -0
  88. metadata +20 -8
@@ -34,6 +34,18 @@ module RuboCop
34
34
  # # good
35
35
  # describe 'test' do; end
36
36
  #
37
+ # # bad
38
+ # shared_examples 'test', focus: true do; end
39
+ #
40
+ # # good
41
+ # shared_examples 'test' do; end
42
+ #
43
+ # # bad
44
+ # shared_context 'test', focus: true do; end
45
+ #
46
+ # # good
47
+ # shared_context 'test' do; end
48
+ #
37
49
  # # bad (does not support autocorrection)
38
50
  # focus 'test' do; end
39
51
  #
@@ -44,18 +56,19 @@ module RuboCop
44
56
  MSG = 'Focused spec found.'
45
57
 
46
58
  # @!method focusable_selector?(node)
47
- def_node_matcher :focusable_selector?, <<-PATTERN
59
+ def_node_matcher :focusable_selector?, <<~PATTERN
48
60
  {
49
61
  #ExampleGroups.regular
50
62
  #ExampleGroups.skipped
51
63
  #Examples.regular
52
64
  #Examples.skipped
53
65
  #Examples.pending
66
+ #SharedGroups.all
54
67
  }
55
68
  PATTERN
56
69
 
57
70
  # @!method metadata(node)
58
- def_node_matcher :metadata, <<-PATTERN
71
+ def_node_matcher :metadata, <<~PATTERN
59
72
  {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
60
73
  (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
61
74
  PATTERN
@@ -66,6 +79,8 @@ module RuboCop
66
79
  PATTERN
67
80
 
68
81
  def on_send(node)
82
+ return if node.chained? || node.each_ancestor(:def, :defs).any?
83
+
69
84
  focus_metadata(node) do |focus|
70
85
  add_offense(focus) do |corrector|
71
86
  if focus.pair_type? || focus.str_type? || focus.sym_type?
@@ -66,12 +66,12 @@ module RuboCop
66
66
  EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
67
67
 
68
68
  # @!method scoped_hook(node)
69
- def_node_matcher :scoped_hook, <<-PATTERN
69
+ def_node_matcher :scoped_hook, <<~PATTERN
70
70
  ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
71
71
  PATTERN
72
72
 
73
73
  # @!method unscoped_hook(node)
74
- def_node_matcher :unscoped_hook, <<-PATTERN
74
+ def_node_matcher :unscoped_hook, <<~PATTERN
75
75
  ({block numblock} $(send _ #Hooks.all) ...)
76
76
  PATTERN
77
77
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MSG = 'Move `%<hook>s` above the examples in the group.'
29
29
 
30
30
  # @!method example_or_group?(node)
31
- def_node_matcher :example_or_group?, <<-PATTERN
31
+ def_node_matcher :example_or_group?, <<~PATTERN
32
32
  {
33
33
  ({block numblock} {
34
34
  (send #rspec? #ExampleGroups.all ...)
@@ -22,7 +22,7 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[is_expected should should_not].freeze
23
23
 
24
24
  # @!method lambda?(node)
25
- def_node_matcher :lambda?, <<-PATTERN
25
+ def_node_matcher :lambda?, <<~PATTERN
26
26
  {
27
27
  (send (const nil? :Proc) :new)
28
28
  (send nil? {:proc :lambda})
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def_node_matcher :lambda_subject?, '(block #lambda? ...)'
34
34
 
35
35
  # @!method implicit_expect(node)
36
- def_node_matcher :implicit_expect, <<-PATTERN
36
+ def_node_matcher :implicit_expect, <<~PATTERN
37
37
  $(send nil? {:is_expected :should :should_not} ...)
38
38
  PATTERN
39
39
 
@@ -31,7 +31,7 @@ module RuboCop
31
31
  RESTRICT_ON_SEND = Runners.all + %i[should should_not]
32
32
 
33
33
  # @!method implicit_expect(node)
34
- def_node_matcher :implicit_expect, <<-PATTERN
34
+ def_node_matcher :implicit_expect, <<~PATTERN
35
35
  {
36
36
  (send nil? ${:should :should_not} ...)
37
37
  (send (send nil? $:is_expected) #Runners.all ...)
@@ -78,12 +78,12 @@ module RuboCop
78
78
  ].freeze
79
79
 
80
80
  # @!method explicit_unnamed_subject?(node)
81
- def_node_matcher :explicit_unnamed_subject?, <<-PATTERN
81
+ def_node_matcher :explicit_unnamed_subject?, <<~PATTERN
82
82
  (send nil? :expect (send nil? :subject))
83
83
  PATTERN
84
84
 
85
85
  # @!method implicit_subject?(node)
86
- def_node_matcher :implicit_subject?, <<-PATTERN
86
+ def_node_matcher :implicit_subject?, <<~PATTERN
87
87
  (send nil? {:should :should_not :is_expected} ...)
88
88
  PATTERN
89
89
 
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # It makes reading the test harder because it's not clear what exactly
9
9
  # is tested by this particular example.
10
10
  #
11
+ # The configurable options `AllowedIdentifiers` and `AllowedPatterns`
12
+ # will also read those set in `Naming/VariableNumber`.
13
+ #
11
14
  # @example `Max: 1 (default)`
12
15
  # # bad
13
16
  # let(:item_1) { create(:item) }
@@ -31,7 +34,20 @@ module RuboCop
31
34
  # let(:item_1) { create(:item) }
32
35
  # let(:item_2) { create(:item) }
33
36
  #
37
+ # @example `AllowedIdentifiers: ['item_1', 'item_2']`
38
+ # # good
39
+ # let(:item_1) { create(:item) }
40
+ # let(:item_2) { create(:item) }
41
+ #
42
+ # @example `AllowedPatterns: ['item']`
43
+ # # good
44
+ # let(:item_1) { create(:item) }
45
+ # let(:item_2) { create(:item) }
46
+ #
34
47
  class IndexedLet < Base
48
+ include AllowedIdentifiers
49
+ include AllowedPattern
50
+
35
51
  MSG = 'This `let` statement uses index in its name. Please give it ' \
36
52
  'a meaningful name.'
37
53
 
@@ -69,12 +85,27 @@ module RuboCop
69
85
  end
70
86
 
71
87
  def indexed_let?(node)
72
- let?(node) && SUFFIX_INDEX_REGEX.match?(let_name(node))
88
+ let?(node) &&
89
+ SUFFIX_INDEX_REGEX.match?(let_name(node)) &&
90
+ !allowed_identifier?(let_name(node).to_s) &&
91
+ !matches_allowed_pattern?(let_name(node).to_s)
73
92
  end
74
93
 
75
94
  def let_name_stripped_index(node)
76
95
  let_name(node).to_s.gsub(INDEX_REGEX, '')
77
96
  end
97
+
98
+ def cop_config_patterns_values
99
+ Array(config.for_cop('Naming/VariableNumber')
100
+ .fetch('AllowedPatterns', [])) +
101
+ Array(cop_config.fetch('AllowedPatterns', []))
102
+ end
103
+
104
+ def allowed_identifiers
105
+ Array(config.for_cop('Naming/VariableNumber')
106
+ .fetch('AllowedIdentifiers', [])) +
107
+ Array(cop_config.fetch('AllowedIdentifiers', []))
108
+ end
78
109
  end
79
110
  end
80
111
  end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  'with `have_received`.'
26
26
 
27
27
  # @!method null_double(node)
28
- def_node_search :null_double, <<-PATTERN
28
+ def_node_search :null_double, <<~PATTERN
29
29
  (lvasgn $_
30
30
  (send
31
31
  $(send nil? :instance_double
@@ -33,7 +33,7 @@ module RuboCop
33
33
  PATTERN
34
34
 
35
35
  # @!method have_received_usage(node)
36
- def_node_search :have_received_usage, <<-PATTERN
36
+ def_node_search :have_received_usage, <<~PATTERN
37
37
  (send
38
38
  (send nil? :expect
39
39
  (lvar $_)) :to
@@ -48,16 +48,16 @@ module RuboCop
48
48
  class InstanceVariable < Base
49
49
  include TopLevelGroup
50
50
 
51
- MSG = 'Avoid instance variables use let, ' \
51
+ MSG = 'Avoid instance variables - use let, ' \
52
52
  'a method call, or a local variable (if possible).'
53
53
 
54
54
  # @!method dynamic_class?(node)
55
- def_node_matcher :dynamic_class?, <<-PATTERN
55
+ def_node_matcher :dynamic_class?, <<~PATTERN
56
56
  (block (send (const nil? :Class) :new ...) ...)
57
57
  PATTERN
58
58
 
59
59
  # @!method custom_matcher?(node)
60
- def_node_matcher :custom_matcher?, <<-PATTERN
60
+ def_node_matcher :custom_matcher?, <<~PATTERN
61
61
  (block {
62
62
  (send nil? :matcher sym)
63
63
  (send (const (const nil? :RSpec) :Matchers) :define sym)
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check for `specify` with `is_expected` and one-liner expectations.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # specify { is_expected.to be_truthy }
11
+ #
12
+ # # good
13
+ # it { is_expected.to be_truthy }
14
+ #
15
+ # # good
16
+ # specify do
17
+ # # ...
18
+ # end
19
+ # specify { expect(sqrt(4)).to eq(2) }
20
+ #
21
+ class IsExpectedSpecify < Base
22
+ extend AutoCorrector
23
+
24
+ RESTRICT_ON_SEND = %i[specify].freeze
25
+ IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
26
+ MSG = 'Use `it` instead of `specify`.'
27
+
28
+ # @!method offense?(node)
29
+ def_node_matcher :offense?, <<~PATTERN
30
+ (block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
31
+ PATTERN
32
+
33
+ def on_send(node)
34
+ block_node = node.parent
35
+ return unless block_node&.single_line? && offense?(block_node)
36
+
37
+ selector = node.loc.selector
38
+ add_offense(selector) do |corrector|
39
+ corrector.replace(selector, 'it')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  'of iterating over an array.'
22
22
 
23
23
  # @!method each?(node)
24
- def_node_matcher :each?, <<-PATTERN
24
+ def_node_matcher :each?, <<~PATTERN
25
25
  (block
26
26
  (send ... :each)
27
27
  (args (arg $_))
@@ -30,14 +30,14 @@ module RuboCop
30
30
  PATTERN
31
31
 
32
32
  # @!method each_numblock?(node)
33
- def_node_matcher :each_numblock?, <<-PATTERN
33
+ def_node_matcher :each_numblock?, <<~PATTERN
34
34
  (numblock
35
35
  (send ... :each) _ $(...)
36
36
  )
37
37
  PATTERN
38
38
 
39
39
  # @!method expectation?(node)
40
- def_node_matcher :expectation?, <<-PATTERN
40
+ def_node_matcher :expectation?, <<~PATTERN
41
41
  (send (send nil? :expect (lvar %)) :to ...)
42
42
  PATTERN
43
43
 
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # Anonymous classes are fine, since they don't result in global
18
18
  # namespace name clashes.
19
19
  #
20
- # @see https://relishapp.com/rspec/rspec-mocks/docs/mutating-constants
20
+ # @see https://rspec.info/features/3-12/rspec-mocks/mutating-constants
21
21
  #
22
22
  # @example Constants leak between examples
23
23
  # # bad
@@ -36,7 +36,7 @@ module RuboCop
36
36
  MSG = 'Move `let` before the examples in the group.'
37
37
 
38
38
  # @!method example_or_group?(node)
39
- def_node_matcher :example_or_group?, <<-PATTERN
39
+ def_node_matcher :example_or_group?, <<~PATTERN
40
40
  {
41
41
  (block (send nil? {#ExampleGroups.all #Examples.all} ...) ...)
42
42
  (send nil? #Includes.examples ...)
@@ -51,6 +51,10 @@ module RuboCop
51
51
  }
52
52
  PATTERN
53
53
 
54
+ def self.autocorrect_incompatible_with
55
+ [RSpec::ScatteredLet]
56
+ end
57
+
54
58
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
55
59
  return unless example_group_with_body?(node)
56
60
 
@@ -37,7 +37,7 @@ module RuboCop
37
37
  PATTERN
38
38
 
39
39
  # @!method let_bang(node)
40
- def_node_matcher :let_bang, <<-PATTERN
40
+ def_node_matcher :let_bang, <<~PATTERN
41
41
  {
42
42
  (block $(send nil? :let! {(sym $_) (str $_)}) ...)
43
43
  $(send nil? :let! {(sym $_) (str $_)} block_pass)
@@ -29,11 +29,10 @@ module RuboCop
29
29
 
30
30
  MSG = 'Prefer `%<style>s` for setting message expectations.'
31
31
 
32
- SUPPORTED_STYLES = %w[allow expect].freeze
33
32
  RESTRICT_ON_SEND = %i[to].freeze
34
33
 
35
34
  # @!method message_expectation(node)
36
- def_node_matcher :message_expectation, <<-PATTERN
35
+ def_node_matcher :message_expectation, <<~PATTERN
37
36
  (send $(send nil? {:expect :allow} ...) :to #receive_message?)
38
37
  PATTERN
39
38
 
@@ -39,8 +39,6 @@ module RuboCop
39
39
  'expectations. Setup `%<source>s` as a spy using ' \
40
40
  '`allow` or `instance_spy`.'
41
41
 
42
- SUPPORTED_STYLES = %w[have_received receive].freeze
43
-
44
42
  RESTRICT_ON_SEND = Runners.all
45
43
 
46
44
  # @!method message_expectation(node)
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Use consistent metadata style.
7
+ #
8
+ # This cop does not support autocorrection in the case of
9
+ # `EnforcedStyle: hash` where the trailing metadata type is ambiguous.
10
+ # (e.g. `describe 'Something', :a, b`)
11
+ #
12
+ # @example EnforcedStyle: symbol (default)
13
+ # # bad
14
+ # describe 'Something', a: true
15
+ #
16
+ # # good
17
+ # describe 'Something', :a
18
+ #
19
+ # @example EnforcedStyle: hash
20
+ # # bad
21
+ # describe 'Something', :a
22
+ #
23
+ # # good
24
+ # describe 'Something', a: true
25
+ class MetadataStyle < Base # rubocop:disable Metrics/ClassLength
26
+ extend AutoCorrector
27
+
28
+ include ConfigurableEnforcedStyle
29
+ include Metadata
30
+ include RangeHelp
31
+
32
+ # @!method extract_metadata_hash(node)
33
+ def_node_matcher :extract_metadata_hash, <<~PATTERN
34
+ (send _ _ _ ... $hash)
35
+ PATTERN
36
+
37
+ # @!method match_boolean_metadata_pair?(node)
38
+ def_node_matcher :match_boolean_metadata_pair?, <<~PATTERN
39
+ (pair sym true)
40
+ PATTERN
41
+
42
+ # @!method match_ambiguous_trailing_metadata?(node)
43
+ def_node_matcher :match_ambiguous_trailing_metadata?, <<~PATTERN
44
+ (send _ _ _ ... !{hash sym})
45
+ PATTERN
46
+
47
+ def on_metadata(symbols, hash)
48
+ # RSpec example groups accept two string arguments. In such a case,
49
+ # the rspec_metadata matcher will interpret the second string
50
+ # argument as a metadata symbol.
51
+ symbols.shift if symbols.first&.str_type?
52
+
53
+ symbols.each do |symbol|
54
+ on_metadata_symbol(symbol)
55
+ end
56
+
57
+ return unless hash
58
+
59
+ hash.pairs.each do |pair|
60
+ on_metadata_pair(pair)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def autocorrect_pair(corrector, node)
67
+ remove_pair(corrector, node)
68
+ insert_symbol(corrector, node)
69
+ end
70
+
71
+ def autocorrect_symbol(corrector, node)
72
+ return if match_ambiguous_trailing_metadata?(node.parent)
73
+
74
+ remove_symbol(corrector, node)
75
+ insert_pair(corrector, node)
76
+ end
77
+
78
+ def bad_metadata_pair?(node)
79
+ style == :symbol && match_boolean_metadata_pair?(node)
80
+ end
81
+
82
+ def bad_metadata_symbol?(_node)
83
+ style == :hash
84
+ end
85
+
86
+ def format_symbol_to_pair_source(node)
87
+ "#{node.value}: true"
88
+ end
89
+
90
+ def insert_pair(corrector, node)
91
+ hash_node = extract_metadata_hash(node.parent)
92
+ if hash_node.nil?
93
+ insert_pair_as_last_argument(corrector, node)
94
+ elsif hash_node.pairs.any?
95
+ insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
96
+ else
97
+ insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
98
+ end
99
+ end
100
+
101
+ def insert_pair_as_last_argument(corrector, node)
102
+ corrector.insert_before(
103
+ node.parent.location.end || node.parent.source_range.with(
104
+ begin_pos: node.parent.source_range.end_pos
105
+ ),
106
+ ", #{format_symbol_to_pair_source(node)}"
107
+ )
108
+ end
109
+
110
+ def insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
111
+ corrector.insert_after(
112
+ hash_node.location.begin,
113
+ " #{format_symbol_to_pair_source(node)} "
114
+ )
115
+ end
116
+
117
+ def insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
118
+ corrector.insert_after(
119
+ hash_node.children.last,
120
+ ", #{format_symbol_to_pair_source(node)}"
121
+ )
122
+ end
123
+
124
+ def insert_symbol(corrector, node)
125
+ corrector.insert_after(
126
+ node.parent.left_sibling,
127
+ ", #{node.key.value.inspect}"
128
+ )
129
+ end
130
+
131
+ def message_for_style
132
+ format(
133
+ 'Use %<style>s style for metadata.',
134
+ style: style
135
+ )
136
+ end
137
+
138
+ def on_metadata_pair(node)
139
+ return unless bad_metadata_pair?(node)
140
+
141
+ add_offense(node, message: message_for_style) do |corrector|
142
+ autocorrect_pair(corrector, node)
143
+ end
144
+ end
145
+
146
+ def on_metadata_symbol(node)
147
+ return unless bad_metadata_symbol?(node)
148
+
149
+ add_offense(node, message: message_for_style) do |corrector|
150
+ autocorrect_symbol(corrector, node)
151
+ end
152
+ end
153
+
154
+ def remove_pair(corrector, node)
155
+ if !node.parent.braces? || node.left_siblings.any?
156
+ remove_pair_following(corrector, node)
157
+ elsif node.right_siblings.any?
158
+ remove_pair_preceding(corrector, node)
159
+ else
160
+ corrector.remove(node)
161
+ end
162
+ end
163
+
164
+ def remove_pair_following(corrector, node)
165
+ corrector.remove(
166
+ range_with_surrounding_comma(
167
+ range_with_surrounding_space(
168
+ node.source_range,
169
+ side: :left
170
+ ),
171
+ :left
172
+ )
173
+ )
174
+ end
175
+
176
+ def remove_pair_preceding(corrector, node)
177
+ corrector.remove(
178
+ range_with_surrounding_space(
179
+ range_with_surrounding_comma(
180
+ node.source_range,
181
+ :right
182
+ ),
183
+ side: :right
184
+ )
185
+ )
186
+ end
187
+
188
+ def remove_symbol(corrector, node)
189
+ corrector.remove(
190
+ range_with_surrounding_comma(
191
+ range_with_surrounding_space(
192
+ node.source_range,
193
+ side: :left
194
+ ),
195
+ :left
196
+ )
197
+ )
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Help methods for file.
7
+ module FileHelp
8
+ def expanded_file_path
9
+ File.expand_path(processed_source.file_path)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -13,7 +13,7 @@ module RuboCop
13
13
  def_node_matcher :rspec_metadata, <<~PATTERN
14
14
  (block
15
15
  (send
16
- #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ ${send str sym}* (hash $...)?)
16
+ #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ $...)
17
17
  ...)
18
18
  PATTERN
19
19
 
@@ -24,25 +24,39 @@ module RuboCop
24
24
 
25
25
  # @!method metadata_in_block(node)
26
26
  def_node_search :metadata_in_block, <<~PATTERN
27
- (send (lvar %) #Hooks.all _ ${send str sym}* (hash $...)?)
27
+ (send (lvar %) #Hooks.all _ $...)
28
28
  PATTERN
29
29
 
30
30
  def on_block(node)
31
31
  rspec_configure(node) do |block_var|
32
- metadata_in_block(node, block_var) do |symbols, pairs|
33
- on_metadata(symbols, pairs.flatten)
32
+ metadata_in_block(node, block_var) do |metadata_arguments|
33
+ on_metadata_arguments(metadata_arguments)
34
34
  end
35
35
  end
36
36
 
37
- rspec_metadata(node) do |symbols, pairs|
38
- on_metadata(symbols, pairs.flatten)
37
+ rspec_metadata(node) do |metadata_arguments|
38
+ on_metadata_arguments(metadata_arguments)
39
39
  end
40
40
  end
41
41
  alias on_numblock on_block
42
42
 
43
- def on_metadata(_symbols, _pairs)
43
+ def on_metadata(_symbols, _hash)
44
44
  raise ::NotImplementedError
45
45
  end
46
+
47
+ private
48
+
49
+ def on_metadata_arguments(metadata_arguments)
50
+ *symbols, last = metadata_arguments
51
+ hash = nil
52
+ case last&.type
53
+ when :hash
54
+ hash = last
55
+ when :sym
56
+ symbols << last
57
+ end
58
+ on_metadata(symbols, hash)
59
+ end
46
60
  end
47
61
  end
48
62
  end
@@ -8,7 +8,7 @@ module RuboCop
8
8
  extend RuboCop::NodePattern::Macros
9
9
 
10
10
  # @!method skipped_in_metadata?(node)
11
- def_node_matcher :skipped_in_metadata?, <<-PATTERN
11
+ def_node_matcher :skipped_in_metadata?, <<~PATTERN
12
12
  {
13
13
  (send _ _ <(sym {:skip :pending}) ...>)
14
14
  (send _ _ ... (hash <(pair (sym {:skip :pending}) { true str dstr }) ...>))
@@ -30,7 +30,7 @@ module RuboCop
30
30
  #
31
31
  # @param node [RuboCop::AST::Node]
32
32
  # @return [Array<RuboCop::AST::Node>] matching nodes
33
- def_node_matcher :skip_or_pending_inside_block?, <<-PATTERN
33
+ def_node_matcher :skip_or_pending_inside_block?, <<~PATTERN
34
34
  (block <(send nil? {:skip :pending} ...) ...>)
35
35
  PATTERN
36
36
  end