rubocop-rspec 2.16.0 → 2.24.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 (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