rubocop-rspec 2.16.0 → 2.24.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +124 -9
  3. data/README.md +3 -3
  4. data/config/default.yml +145 -18
  5. data/config/obsoletion.yml +15 -0
  6. data/lib/rubocop/cop/rspec/be_empty.rb +44 -0
  7. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  8. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +29 -115
  9. data/lib/rubocop/cop/rspec/capybara/match_style.rb +38 -0
  10. data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +23 -96
  11. data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +19 -75
  12. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +14 -83
  13. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +25 -69
  14. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +26 -63
  15. data/lib/rubocop/cop/rspec/change_by_zero.rb +33 -23
  16. data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
  17. data/lib/rubocop/cop/rspec/context_method.rb +5 -1
  18. data/lib/rubocop/cop/rspec/context_wording.rb +13 -6
  19. data/lib/rubocop/cop/rspec/describe_method.rb +16 -8
  20. data/lib/rubocop/cop/rspec/described_class.rb +2 -1
  21. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +7 -5
  22. data/lib/rubocop/cop/rspec/dialect.rb +1 -1
  23. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +2 -2
  24. data/lib/rubocop/cop/rspec/empty_example_group.rb +10 -7
  25. data/lib/rubocop/cop/rspec/empty_hook.rb +2 -2
  26. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  27. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  28. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  29. data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
  30. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
  31. data/lib/rubocop/cop/rspec/expect_actual.rb +4 -4
  32. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  33. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +25 -118
  34. data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +40 -107
  35. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +30 -250
  36. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +19 -46
  37. data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +23 -64
  38. data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +45 -79
  39. data/lib/rubocop/cop/rspec/file_path.rb +8 -2
  40. data/lib/rubocop/cop/rspec/focus.rb +19 -5
  41. data/lib/rubocop/cop/rspec/hook_argument.rb +12 -9
  42. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +5 -3
  43. data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
  44. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  45. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  46. data/lib/rubocop/cop/rspec/let_before_examples.rb +8 -4
  47. data/lib/rubocop/cop/rspec/let_setup.rb +6 -8
  48. data/lib/rubocop/cop/rspec/match_array.rb +59 -0
  49. data/lib/rubocop/cop/rspec/metadata_style.rb +197 -0
  50. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +1 -2
  51. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  52. data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
  53. data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
  54. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +20 -4
  55. data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -1
  56. data/lib/rubocop/cop/rspec/named_subject.rb +7 -5
  57. data/lib/rubocop/cop/rspec/no_expectation_example.rb +2 -5
  58. data/lib/rubocop/cop/rspec/overwriting_setup.rb +3 -1
  59. data/lib/rubocop/cop/rspec/pending.rb +23 -13
  60. data/lib/rubocop/cop/rspec/pending_without_reason.rb +72 -36
  61. data/lib/rubocop/cop/rspec/predicate_matcher.rb +49 -40
  62. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +11 -6
  63. data/lib/rubocop/cop/rspec/rails/http_status.rb +107 -34
  64. data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +4 -4
  65. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +60 -0
  66. data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
  67. data/lib/rubocop/cop/rspec/rails/travel_around.rb +92 -0
  68. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  69. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  70. data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
  71. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +3 -6
  72. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +3 -6
  73. data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -4
  74. data/lib/rubocop/cop/rspec/scattered_setup.rb +23 -6
  75. data/lib/rubocop/cop/rspec/shared_context.rb +12 -13
  76. data/lib/rubocop/cop/rspec/shared_examples.rb +6 -4
  77. data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
  78. data/lib/rubocop/cop/rspec/sort_metadata.rb +4 -3
  79. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  80. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  81. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  82. data/lib/rubocop/cop/rspec/subject_stub.rb +0 -1
  83. data/lib/rubocop/cop/rspec/variable_definition.rb +5 -2
  84. data/lib/rubocop/cop/rspec/variable_name.rb +4 -1
  85. data/lib/rubocop/cop/rspec/verified_double_reference.rb +7 -7
  86. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  87. data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
  88. data/lib/rubocop/cop/rspec_cops.rb +16 -0
  89. data/lib/rubocop/rspec/config_formatter.rb +16 -0
  90. data/lib/rubocop/rspec/example_group.rb +6 -8
  91. data/lib/rubocop/rspec/language/node_pattern.rb +26 -0
  92. data/lib/rubocop/rspec/language.rb +25 -16
  93. data/lib/rubocop/rspec/version.rb +1 -1
  94. data/lib/rubocop-rspec.rb +4 -5
  95. metadata +50 -8
  96. data/lib/rubocop/cop/rspec/mixin/capybara_help.rb +0 -80
  97. data/lib/rubocop/cop/rspec/mixin/css_selector.rb +0 -146
  98. data/lib/rubocop/rspec/factory_bot/language.rb +0 -37
  99. data/lib/rubocop/rspec/factory_bot.rb +0 -64
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module Rails
7
+ # Prefer to travel in `before` rather than `around`.
8
+ #
9
+ # @safety
10
+ # This cop is unsafe because the automatic `travel_back` is only run
11
+ # on test cases that are considered as Rails related.
12
+ #
13
+ # And also, this cop's autocorrection is unsafe because the order of
14
+ # execution will change if other steps exist before traveling in
15
+ # `around`.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # around do |example|
20
+ # freeze_time do
21
+ # example.run
22
+ # end
23
+ # end
24
+ #
25
+ # # good
26
+ # before { freeze_time }
27
+ class TravelAround < Base
28
+ extend AutoCorrector
29
+
30
+ MSG = 'Prefer to travel in `before` rather than `around`.'
31
+
32
+ TRAVEL_METHOD_NAMES = %i[
33
+ freeze_time
34
+ travel
35
+ travel_to
36
+ ].to_set.freeze
37
+
38
+ # @!method extract_run_in_travel(node)
39
+ def_node_matcher :extract_run_in_travel, <<~PATTERN
40
+ (block
41
+ $(send nil? TRAVEL_METHOD_NAMES ...)
42
+ (args ...)
43
+ (send _ :run)
44
+ )
45
+ PATTERN
46
+
47
+ # @!method match_around_each?(node)
48
+ def_node_matcher :match_around_each?, <<~PATTERN
49
+ (block
50
+ (send _ :around (sym :each)?)
51
+ ...
52
+ )
53
+ PATTERN
54
+
55
+ def on_block(node)
56
+ run_node = extract_run_in_travel(node)
57
+ return unless run_node
58
+
59
+ around_node = extract_surrounding_around_block(run_node)
60
+ return unless around_node
61
+
62
+ add_offense(node) do |corrector|
63
+ autocorrect(corrector, node, run_node, around_node)
64
+ end
65
+ end
66
+ alias on_numblock on_block
67
+
68
+ private
69
+
70
+ def autocorrect(corrector, node, run_node, around_node)
71
+ corrector.replace(
72
+ node,
73
+ node.body.source
74
+ )
75
+ corrector.insert_before(
76
+ around_node,
77
+ "before { #{run_node.source} }\n\n"
78
+ )
79
+ end
80
+
81
+ # @param node [RuboCop::AST::BlockNode]
82
+ # @return [RuboCop::AST::BlockNode, nil]
83
+ def extract_surrounding_around_block(node)
84
+ node.each_ancestor(:block).find do |ancestor|
85
+ match_around_each?(ancestor)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -80,7 +80,7 @@ module RuboCop
80
80
 
81
81
  def range(node, offending_node)
82
82
  offending_node.loc.dot.with(
83
- end_pos: node.loc.expression.end_pos
83
+ end_pos: node.source_range.end_pos
84
84
  )
85
85
  end
86
86
  end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for multiple messages stubbed on the same object.
7
+ #
8
+ # @safety
9
+ # The autocorrection is marked as unsafe, because it may change the
10
+ # order of stubs. This in turn may cause e.g. variables to be called
11
+ # before they are defined.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # before do
16
+ # allow(Service).to receive(:foo).and_return(bar)
17
+ # allow(Service).to receive(:baz).and_return(qux)
18
+ # end
19
+ #
20
+ # # good
21
+ # before do
22
+ # allow(Service).to receive_messages(foo: bar, baz: qux)
23
+ # end
24
+ #
25
+ # # good - ignore same message
26
+ # before do
27
+ # allow(Service).to receive(:foo).and_return(bar)
28
+ # allow(Service).to receive(:foo).and_return(qux)
29
+ # end
30
+ #
31
+ class ReceiveMessages < Base
32
+ extend AutoCorrector
33
+ include RangeHelp
34
+
35
+ MSG = 'Use `receive_messages` instead of multiple stubs on lines ' \
36
+ '%<loc>s.'
37
+
38
+ # @!method allow_receive_message?(node)
39
+ def_node_matcher :allow_receive_message?, <<~PATTERN
40
+ (send (send nil? :allow ...) :to (send (send nil? :receive (sym _)) :and_return !#heredoc_or_splat?))
41
+ PATTERN
42
+
43
+ # @!method allow_argument(node)
44
+ def_node_matcher :allow_argument, <<~PATTERN
45
+ (send (send nil? :allow $_ ...) ...)
46
+ PATTERN
47
+
48
+ # @!method receive_node(node)
49
+ def_node_search :receive_node, <<~PATTERN
50
+ $(send (send nil? :receive ...) ...)
51
+ PATTERN
52
+
53
+ # @!method receive_arg(node)
54
+ def_node_search :receive_arg, <<~PATTERN
55
+ (send (send nil? :receive $_) ...)
56
+ PATTERN
57
+
58
+ # @!method receive_and_return_argument(node)
59
+ def_node_matcher :receive_and_return_argument, <<~PATTERN
60
+ (send (send nil? :allow ...) :to (send (send nil? :receive (sym $_)) :and_return $_))
61
+ PATTERN
62
+
63
+ def on_begin(node)
64
+ repeated_receive_message(node).each do |item, repeated_lines, args|
65
+ next if repeated_lines.empty?
66
+
67
+ register_offense(item, repeated_lines, args)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def repeated_receive_message(node)
74
+ node
75
+ .children
76
+ .select { |child| allow_receive_message?(child) }
77
+ .group_by { |child| allow_argument(child) }
78
+ .values
79
+ .reject(&:one?)
80
+ .flat_map { |items| add_repeated_lines_and_arguments(items) }
81
+ end
82
+
83
+ def add_repeated_lines_and_arguments(items)
84
+ uniq_items = uniq_items(items)
85
+ repeated_lines = uniq_items.map(&:first_line)
86
+ uniq_items.map do |item|
87
+ [item, repeated_lines - [item.first_line], arguments(uniq_items)]
88
+ end
89
+ end
90
+
91
+ def uniq_items(items)
92
+ items.select do |item|
93
+ items.none? do |i|
94
+ receive_arg(item).first == receive_arg(i).first &&
95
+ !same_line?(item, i)
96
+ end
97
+ end
98
+ end
99
+
100
+ def arguments(items)
101
+ items.map do |item|
102
+ receive_and_return_argument(item) do |receive_arg, return_arg|
103
+ "#{normalize_receive_arg(receive_arg)}: " \
104
+ "#{normalize_return_arg(return_arg)}"
105
+ end
106
+ end
107
+ end
108
+
109
+ def normalize_receive_arg(receive_arg)
110
+ if requires_quotes?(receive_arg)
111
+ "'#{receive_arg}'"
112
+ else
113
+ receive_arg
114
+ end
115
+ end
116
+
117
+ def normalize_return_arg(return_arg)
118
+ if return_arg.hash_type? && !return_arg.braces?
119
+ "{ #{return_arg.source} }"
120
+ else
121
+ return_arg.source
122
+ end
123
+ end
124
+
125
+ def register_offense(item, repeated_lines, args)
126
+ add_offense(item, message: message(repeated_lines)) do |corrector|
127
+ if item.loc.line > repeated_lines.max
128
+ replace_to_receive_messages(corrector, item, args)
129
+ else
130
+ corrector.remove(item_range_by_whole_lines(item))
131
+ end
132
+ end
133
+ end
134
+
135
+ def message(repeated_lines)
136
+ format(MSG, loc: repeated_lines)
137
+ end
138
+
139
+ def replace_to_receive_messages(corrector, item, args)
140
+ receive_node(item) do |node|
141
+ corrector.replace(node,
142
+ "receive_messages(#{args.join(', ')})")
143
+ end
144
+ end
145
+
146
+ def item_range_by_whole_lines(item)
147
+ range_by_whole_lines(item.source_range, include_final_newline: true)
148
+ end
149
+
150
+ def heredoc_or_splat?(node)
151
+ (node.str_type? || node.dstr_type?) && node.heredoc? ||
152
+ node.splat_type?
153
+ end
154
+
155
+ def requires_quotes?(value)
156
+ value.match?(/^:".*?"|=$|^\W+$/)
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Remove redundant `around` hook.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # around do |example|
11
+ # example.run
12
+ # end
13
+ #
14
+ # # good
15
+ #
16
+ class RedundantAround < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Remove redundant `around` hook.'
20
+
21
+ RESTRICT_ON_SEND = %i[around].freeze
22
+
23
+ def on_block(node)
24
+ return unless match_redundant_around_hook_block?(node)
25
+
26
+ add_offense(node) do |corrector|
27
+ autocorrect(corrector, node)
28
+ end
29
+ end
30
+ alias on_numblock on_block
31
+
32
+ def on_send(node)
33
+ return unless match_redundant_around_hook_send?(node)
34
+
35
+ add_offense(node) do |corrector|
36
+ autocorrect(corrector, node)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # @!method match_redundant_around_hook_block?(node)
43
+ def_node_matcher :match_redundant_around_hook_block?, <<~PATTERN
44
+ ({block numblock} (send _ :around ...) ... (send _ :run))
45
+ PATTERN
46
+
47
+ # @!method match_redundant_around_hook_send?(node)
48
+ def_node_matcher :match_redundant_around_hook_send?, <<~PATTERN
49
+ (send
50
+ _
51
+ :around
52
+ ...
53
+ (block-pass
54
+ (sym :run)
55
+ )
56
+ )
57
+ PATTERN
58
+
59
+ def autocorrect(corrector, node)
60
+ corrector.remove(node)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -43,6 +43,8 @@ module RuboCop
43
43
  # end
44
44
  #
45
45
  class RepeatedExampleGroupBody < Base
46
+ include SkipOrPending
47
+
46
48
  MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
47
49
 
48
50
  # @!method several_example_groups?(node)
@@ -59,11 +61,6 @@ module RuboCop
59
61
  # @!method const_arg(node)
60
62
  def_node_matcher :const_arg, '(block (send _ _ $const ...) ...)'
61
63
 
62
- # @!method skip_or_pending?(node)
63
- def_node_matcher :skip_or_pending?, <<-PATTERN
64
- (block <(send nil? {:skip :pending} ...) ...>)
65
- PATTERN
66
-
67
64
  def on_begin(node)
68
65
  return unless several_example_groups?(node)
69
66
 
@@ -78,7 +75,7 @@ module RuboCop
78
75
  node
79
76
  .children
80
77
  .select { |child| example_group_with_body?(child) }
81
- .reject { |child| skip_or_pending?(child) }
78
+ .reject { |child| skip_or_pending_inside_block?(child) }
82
79
  .group_by { |group| signature_keys(group) }
83
80
  .values
84
81
  .reject(&:one?)
@@ -43,6 +43,8 @@ module RuboCop
43
43
  # end
44
44
  #
45
45
  class RepeatedExampleGroupDescription < Base
46
+ include SkipOrPending
47
+
46
48
  MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
47
49
 
48
50
  # @!method several_example_groups?(node)
@@ -55,11 +57,6 @@ module RuboCop
55
57
  (block (send _ _ $_ $...) ...)
56
58
  PATTERN
57
59
 
58
- # @!method skip_or_pending?(node)
59
- def_node_matcher :skip_or_pending?, <<-PATTERN
60
- (block <(send nil? {:skip :pending}) ...>)
61
- PATTERN
62
-
63
60
  # @!method empty_description?(node)
64
61
  def_node_matcher :empty_description?, '(block (send _ _) ...)'
65
62
 
@@ -77,7 +74,7 @@ module RuboCop
77
74
  node
78
75
  .children
79
76
  .select { |child| example_group?(child) }
80
- .reject { |child| skip_or_pending?(child) }
77
+ .reject { |child| skip_or_pending_inside_block?(child) }
81
78
  .reject { |child| empty_description?(child) }
82
79
  .group_by { |group| doc_string_and_metadata(group) }
83
80
  .values
@@ -56,12 +56,11 @@ module RuboCop
56
56
 
57
57
  # @!method include_examples?(node)
58
58
  def_node_matcher :include_examples?,
59
- send_pattern('#Includes.examples')
59
+ '(send nil? #Includes.examples ...)'
60
60
 
61
61
  # @!method shared_examples_name(node)
62
- def_node_matcher :shared_examples_name, <<-PATTERN
63
- (send _ #Includes.examples $_ ...)
64
- PATTERN
62
+ def_node_matcher :shared_examples_name,
63
+ '(send nil? #Includes.examples $_name ...)'
65
64
 
66
65
  def on_begin(node)
67
66
  return unless several_include_examples?(node)
@@ -23,6 +23,9 @@ module RuboCop
23
23
  # end
24
24
  #
25
25
  class ScatteredSetup < Base
26
+ include RangeHelp
27
+ extend AutoCorrector
28
+
26
29
  MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
27
30
  'example group (also defined on %<lines>s).'
28
31
 
@@ -30,13 +33,11 @@ module RuboCop
30
33
  return unless example_group?(node)
31
34
 
32
35
  repeated_hooks(node).each do |occurrences|
33
- lines = occurrences.map(&:first_line)
34
-
35
36
  occurrences.each do |occurrence|
36
- lines_except_current = lines - [occurrence.first_line]
37
- message = format(MSG, hook_name: occurrences.first.method_name,
38
- lines: lines_msg(lines_except_current))
39
- add_offense(occurrence, message: message)
37
+ message = message(occurrences, occurrence)
38
+ add_offense(occurrence, message: message) do |corrector|
39
+ autocorrect(corrector, occurrences.first, occurrence)
40
+ end
40
41
  end
41
42
  end
42
43
  end
@@ -63,6 +64,22 @@ module RuboCop
63
64
  "lines #{numbers.join(', ')}"
64
65
  end
65
66
  end
67
+
68
+ def message(occurrences, occurrence)
69
+ lines = occurrences.map(&:first_line)
70
+ lines_except_current = lines - [occurrence.first_line]
71
+ format(MSG, hook_name: occurrences.first.method_name,
72
+ lines: lines_msg(lines_except_current))
73
+ end
74
+
75
+ def autocorrect(corrector, first_occurrence, occurrence)
76
+ return if first_occurrence == occurrence || !first_occurrence.body
77
+
78
+ corrector.insert_after(first_occurrence.body,
79
+ "\n#{occurrence.body.source}")
80
+ corrector.remove(range_by_whole_lines(occurrence.source_range,
81
+ include_final_newline: true))
82
+ end
66
83
  end
67
84
  end
68
85
  end
@@ -57,27 +57,26 @@ module RuboCop
57
57
  MSG_CONTEXT = "Use `shared_context` when you don't define examples."
58
58
 
59
59
  # @!method examples?(node)
60
- def_node_search :examples?,
61
- send_pattern('{#Includes.examples #Examples.all}')
60
+ def_node_search :examples?, <<~PATTERN
61
+ (send nil? {#Includes.examples #Examples.all} ...)
62
+ PATTERN
62
63
 
63
64
  # @!method context?(node)
64
65
  def_node_search :context?, <<-PATTERN
65
- (
66
- send #rspec? {
67
- #Subjects.all
68
- #Helpers.all
69
- #Includes.context
70
- #Hooks.all
71
- } ...
66
+ (send nil?
67
+ {#Subjects.all #Helpers.all #Includes.context #Hooks.all} ...
72
68
  )
73
69
  PATTERN
74
70
 
75
71
  # @!method shared_context(node)
76
- def_node_matcher :shared_context,
77
- block_pattern('#SharedGroups.context')
72
+ def_node_matcher :shared_context, <<~PATTERN
73
+ (block (send #rspec? #SharedGroups.context ...) ...)
74
+ PATTERN
75
+
78
76
  # @!method shared_example(node)
79
- def_node_matcher :shared_example,
80
- block_pattern('#SharedGroups.examples')
77
+ def_node_matcher :shared_example, <<~PATTERN
78
+ (block (send #rspec? #SharedGroups.examples ...) ...)
79
+ PATTERN
81
80
 
82
81
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
83
82
  context_with_only_examples(node) do
@@ -24,10 +24,12 @@ module RuboCop
24
24
  extend AutoCorrector
25
25
 
26
26
  # @!method shared_examples(node)
27
- def_node_matcher :shared_examples,
28
- send_pattern(
29
- '{#SharedGroups.all #Includes.all}'
30
- )
27
+ def_node_matcher :shared_examples, <<~PATTERN
28
+ {
29
+ (send #rspec? #SharedGroups.all ...)
30
+ (send nil? #Includes.all ...)
31
+ }
32
+ PATTERN
31
33
 
32
34
  def on_send(node)
33
35
  shared_examples(node) do
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for passing a block to `skip` within examples.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # it 'does something' do
11
+ # skip 'not yet implemented' do
12
+ # do_something
13
+ # end
14
+ # end
15
+ #
16
+ # # good
17
+ # it 'does something' do
18
+ # skip 'not yet implemented'
19
+ # do_something
20
+ # end
21
+ #
22
+ # # good - when outside example
23
+ # skip 'not yet implemented' do
24
+ # end
25
+ #
26
+ class SkipBlockInsideExample < Base
27
+ MSG = "Don't pass a block to `skip` inside examples."
28
+
29
+ def on_block(node)
30
+ return unless node.method?(:skip)
31
+ return unless inside_example?(node)
32
+
33
+ add_offense(node)
34
+ end
35
+
36
+ alias on_numblock on_block
37
+
38
+ private
39
+
40
+ def inside_example?(node)
41
+ node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -23,7 +23,8 @@ module RuboCop
23
23
 
24
24
  MSG = 'Sort metadata alphabetically.'
25
25
 
26
- def on_metadata(symbols, pairs)
26
+ def on_metadata(symbols, hash)
27
+ pairs = hash&.pairs || []
27
28
  return if sorted?(symbols, pairs)
28
29
 
29
30
  crime_scene = crime_scene(symbols, pairs)
@@ -38,8 +39,8 @@ module RuboCop
38
39
  metadata = symbols + pairs
39
40
 
40
41
  range_between(
41
- metadata.first.loc.expression.begin_pos,
42
- metadata.last.loc.expression.end_pos
42
+ metadata.first.source_range.begin_pos,
43
+ metadata.last.source_range.end_pos
43
44
  )
44
45
  end
45
46