rubocop-rspec 2.22.0 → 2.27.1

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