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,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks where `match_array` is used.
7
+ #
8
+ # This cop checks for the following:
9
+ # - Prefer `contain_exactly` when matching an array with values.
10
+ # - Prefer `eq` when using `match_array` with an empty array literal.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # it { is_expected.to match_array([content1, content2]) }
15
+ #
16
+ # # good
17
+ # it { is_expected.to contain_exactly(content1, content2) }
18
+ #
19
+ # # good
20
+ # it { is_expected.to match_array([content] + array) }
21
+ #
22
+ # # good
23
+ # it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
24
+ #
25
+ class MatchArray < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Prefer `contain_exactly` when matching an array literal.'
29
+ RESTRICT_ON_SEND = %i[match_array].freeze
30
+
31
+ # @!method match_array_with_empty_array?(node)
32
+ def_node_matcher :match_array_with_empty_array?, <<~PATTERN
33
+ (send nil? :match_array (array))
34
+ PATTERN
35
+
36
+ def on_send(node)
37
+ return unless node.first_argument&.array_type?
38
+ return if match_array_with_empty_array?(node)
39
+
40
+ check_populated_array(node)
41
+ end
42
+
43
+ private
44
+
45
+ def check_populated_array(node)
46
+ return if node.first_argument.percent_literal?
47
+
48
+ add_offense(node) do |corrector|
49
+ array_contents = node.arguments.flat_map(&:to_a)
50
+ corrector.replace(
51
+ node,
52
+ "contain_exactly(#{array_contents.map(&:source).join(', ')})"
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,197 @@
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
+ symbols.each do |symbol|
49
+ on_metadata_symbol(symbol)
50
+ end
51
+
52
+ return unless hash
53
+
54
+ hash.pairs.each do |pair|
55
+ on_metadata_pair(pair)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def autocorrect_pair(corrector, node)
62
+ remove_pair(corrector, node)
63
+ insert_symbol(corrector, node)
64
+ end
65
+
66
+ def autocorrect_symbol(corrector, node)
67
+ return if match_ambiguous_trailing_metadata?(node.parent)
68
+
69
+ remove_symbol(corrector, node)
70
+ insert_pair(corrector, node)
71
+ end
72
+
73
+ def bad_metadata_pair?(node)
74
+ style == :symbol && match_boolean_metadata_pair?(node)
75
+ end
76
+
77
+ def bad_metadata_symbol?(_node)
78
+ style == :hash
79
+ end
80
+
81
+ def format_symbol_to_pair_source(node)
82
+ "#{node.value}: true"
83
+ end
84
+
85
+ def insert_pair(corrector, node)
86
+ hash_node = extract_metadata_hash(node.parent)
87
+ if hash_node.nil?
88
+ insert_pair_as_last_argument(corrector, node)
89
+ elsif hash_node.pairs.any?
90
+ insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
91
+ else
92
+ insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
93
+ end
94
+ end
95
+
96
+ def insert_pair_as_last_argument(corrector, node)
97
+ corrector.insert_before(
98
+ node.parent.location.end || node.parent.source_range.with(
99
+ begin_pos: node.parent.source_range.end_pos
100
+ ),
101
+ ", #{format_symbol_to_pair_source(node)}"
102
+ )
103
+ end
104
+
105
+ def insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
106
+ corrector.insert_after(
107
+ hash_node.location.begin,
108
+ " #{format_symbol_to_pair_source(node)} "
109
+ )
110
+ end
111
+
112
+ def insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
113
+ corrector.insert_after(
114
+ hash_node.children.last,
115
+ ", #{format_symbol_to_pair_source(node)}"
116
+ )
117
+ end
118
+
119
+ def insert_symbol(corrector, node)
120
+ corrector.insert_after(
121
+ node.parent.left_sibling,
122
+ ", #{node.key.value.inspect}"
123
+ )
124
+ end
125
+
126
+ def message_for_style
127
+ format(
128
+ 'Use %<style>s style for metadata.',
129
+ style: style
130
+ )
131
+ end
132
+
133
+ def on_metadata_pair(node)
134
+ return unless bad_metadata_pair?(node)
135
+
136
+ add_offense(node, message: message_for_style) do |corrector|
137
+ autocorrect_pair(corrector, node)
138
+ end
139
+ end
140
+
141
+ def on_metadata_symbol(node)
142
+ return unless bad_metadata_symbol?(node)
143
+
144
+ add_offense(node, message: message_for_style) do |corrector|
145
+ autocorrect_symbol(corrector, node)
146
+ end
147
+ end
148
+
149
+ def remove_pair(corrector, node)
150
+ if !node.parent.braces? || node.left_siblings.any?
151
+ remove_pair_following(corrector, node)
152
+ elsif node.right_siblings.any?
153
+ remove_pair_preceding(corrector, node)
154
+ else
155
+ corrector.remove(node)
156
+ end
157
+ end
158
+
159
+ def remove_pair_following(corrector, node)
160
+ corrector.remove(
161
+ range_with_surrounding_comma(
162
+ range_with_surrounding_space(
163
+ node.source_range,
164
+ side: :left
165
+ ),
166
+ :left
167
+ )
168
+ )
169
+ end
170
+
171
+ def remove_pair_preceding(corrector, node)
172
+ corrector.remove(
173
+ range_with_surrounding_space(
174
+ range_with_surrounding_comma(
175
+ node.source_range,
176
+ :right
177
+ ),
178
+ side: :right
179
+ )
180
+ )
181
+ end
182
+
183
+ def remove_symbol(corrector, node)
184
+ corrector.remove(
185
+ range_with_surrounding_comma(
186
+ range_with_surrounding_space(
187
+ node.source_range,
188
+ side: :left
189
+ ),
190
+ :left
191
+ )
192
+ )
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -25,8 +25,7 @@ module RuboCop
25
25
 
26
26
  def missing_separating_line(node)
27
27
  line = final_end_line = final_end_location(node).line
28
-
29
- while comment_line?(processed_source[line])
28
+ while processed_source.line_with_comment?(line + 1)
30
29
  line += 1
31
30
  comment = processed_source.comment_at_line(line)
32
31
  if DirectiveComment.new(comment).enabled?
@@ -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
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helper methods to location.
7
+ module LocationHelp
8
+ module_function
9
+
10
+ # @param node [RuboCop::AST::SendNode]
11
+ # @return [Parser::Source::Range]
12
+ # @example
13
+ # foo 1, 2
14
+ # ^^^^^
15
+ def arguments_with_whitespace(node)
16
+ node.loc.selector.end.with(
17
+ end_pos: node.source_range.end_pos
18
+ )
19
+ end
20
+
21
+ # @param node [RuboCop::AST::SendNode]
22
+ # @return [Parser::Source::Range]
23
+ # @example
24
+ # foo { bar }
25
+ # ^^^^^^^^
26
+ def block_with_whitespace(node)
27
+ return unless (parent = node.parent)
28
+ return unless parent.block_type?
29
+
30
+ node.source_range.end.with(
31
+ end_pos: parent.source_range.end_pos
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ 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_matadata_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_matadata_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_matadata_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
@@ -10,13 +10,29 @@ module RuboCop
10
10
  # @!method skipped_in_metadata?(node)
11
11
  def_node_matcher :skipped_in_metadata?, <<-PATTERN
12
12
  {
13
- (send _ _ <#skip_or_pending? ...>)
14
- (send _ _ ... (hash <(pair #skip_or_pending? { true str }) ...>))
13
+ (send _ _ <(sym {:skip :pending}) ...>)
14
+ (send _ _ ... (hash <(pair (sym {:skip :pending}) { true str dstr }) ...>))
15
15
  }
16
16
  PATTERN
17
17
 
18
- # @!method skip_or_pending?(node)
19
- def_node_matcher :skip_or_pending?, '{(sym :skip) (sym :pending)}'
18
+ # @!method skip_or_pending_inside_block?(node)
19
+ # Match skip/pending statements inside a block (e.g. `context`)
20
+ #
21
+ # @example source that matches
22
+ # context 'when color is blue' do
23
+ # skip 'not implemented yet'
24
+ # pending 'not implemented yet'
25
+ # end
26
+ #
27
+ # @example source that does not match
28
+ # skip 'not implemented yet'
29
+ # pending 'not implemented yet'
30
+ #
31
+ # @param node [RuboCop::AST::Node]
32
+ # @return [Array<RuboCop::AST::Node>] matching nodes
33
+ def_node_matcher :skip_or_pending_inside_block?, <<-PATTERN
34
+ (block <(send nil? {:skip :pending} ...) ...>)
35
+ PATTERN
20
36
  end
21
37
  end
22
38
  end
@@ -78,7 +78,8 @@ module RuboCop
78
78
  PATTERN
79
79
 
80
80
  # @!method expect?(node)
81
- def_node_matcher :expect?, send_pattern('#Expectations.all')
81
+ def_node_matcher :expect?, '(send nil? #Expectations.all ...)'
82
+
82
83
  # @!method aggregate_failures_block?(node)
83
84
  def_node_matcher :aggregate_failures_block?, <<-PATTERN
84
85
  (block (send nil? :aggregate_failures ...) ...)
@@ -82,12 +82,14 @@ module RuboCop
82
82
  MSG = 'Name your test subject if you need to reference it explicitly.'
83
83
 
84
84
  # @!method example_or_hook_block?(node)
85
- def_node_matcher :example_or_hook_block?,
86
- block_pattern('{#Examples.all #Hooks.all}')
85
+ def_node_matcher :example_or_hook_block?, <<~PATTERN
86
+ (block (send nil? {#Examples.all #Hooks.all} ...) ...)
87
+ PATTERN
87
88
 
88
89
  # @!method shared_example?(node)
89
- def_node_matcher :shared_example?,
90
- block_pattern('#SharedGroups.examples')
90
+ def_node_matcher :shared_example?, <<~PATTERN
91
+ (block (send #rspec? #SharedGroups.examples ...) ...)
92
+ PATTERN
91
93
 
92
94
  # @!method subject_usage(node)
93
95
  def_node_search :subject_usage, '$(send nil? :subject)'
@@ -143,7 +145,7 @@ module RuboCop
143
145
  end
144
146
 
145
147
  def find_subject(block_node)
146
- block_node.body.child_nodes.find { |send_node| subject?(send_node) }
148
+ block_node.body&.child_nodes&.find { |send_node| subject?(send_node) }
147
149
  end
148
150
  end
149
151
  end
@@ -65,10 +65,7 @@ module RuboCop
65
65
  # @param [RuboCop::AST::Node] node
66
66
  # @return [Boolean]
67
67
  def_node_matcher :regular_or_focused_example?, <<~PATTERN
68
- {
69
- #{block_pattern('{#Examples.regular | #Examples.focused}')}
70
- #{numblock_pattern('{#Examples.regular | #Examples.focused}')}
71
- }
68
+ ({block numblock} (send nil? {#Examples.regular #Examples.focused} ...) ...)
72
69
  PATTERN
73
70
 
74
71
  # @!method includes_expectation?(node)
@@ -76,7 +73,7 @@ module RuboCop
76
73
  # @return [Boolean]
77
74
  def_node_search :includes_expectation?, <<~PATTERN
78
75
  {
79
- #{send_pattern('#Expectations.all')}
76
+ (send nil? #Expectations.all ...)
80
77
  (send nil? `#matches_allowed_pattern? ...)
81
78
  }
82
79
  PATTERN
@@ -26,7 +26,9 @@ module RuboCop
26
26
  MSG = '`%<name>s` is already defined.'
27
27
 
28
28
  # @!method setup?(node)
29
- def_node_matcher :setup?, block_pattern('{#Helpers.all #Subjects.all}')
29
+ def_node_matcher :setup?, <<~PATTERN
30
+ (block (send nil? {#Helpers.all #Subjects.all} ...) ...)
31
+ PATTERN
30
32
 
31
33
  # @!method first_argument_name(node)
32
34
  def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
@@ -38,20 +38,25 @@ module RuboCop
38
38
  MSG = 'Pending spec found.'
39
39
 
40
40
  # @!method skippable?(node)
41
- def_node_matcher :skippable?,
42
- send_pattern(<<~PATTERN)
43
- {#ExampleGroups.regular #Examples.regular}
44
- PATTERN
41
+ def_node_matcher :skippable?, <<~PATTERN
42
+ {
43
+ (send #rspec? #ExampleGroups.regular ...)
44
+ #skippable_example?
45
+ }
46
+ PATTERN
47
+
48
+ # @!method skippable_example?(node)
49
+ def_node_matcher :skippable_example?, <<~PATTERN
50
+ (send nil? #Examples.regular ...)
51
+ PATTERN
45
52
 
46
53
  # @!method pending_block?(node)
47
- def_node_matcher :pending_block?,
48
- send_pattern(<<~PATTERN)
49
- {
50
- #ExampleGroups.skipped
51
- #Examples.skipped
52
- #Examples.pending
53
- }
54
- PATTERN
54
+ def_node_matcher :pending_block?, <<~PATTERN
55
+ {
56
+ (send #rspec? #ExampleGroups.skipped ...)
57
+ (send nil? {#Examples.skipped #Examples.pending} ...)
58
+ }
59
+ PATTERN
55
60
 
56
61
  def on_send(node)
57
62
  return unless pending_block?(node) || skipped?(node)
@@ -62,7 +67,12 @@ module RuboCop
62
67
  private
63
68
 
64
69
  def skipped?(node)
65
- skippable?(node) && skipped_in_metadata?(node)
70
+ skippable?(node) && skipped_in_metadata?(node) ||
71
+ skipped_regular_example_without_body?(node)
72
+ end
73
+
74
+ def skipped_regular_example_without_body?(node)
75
+ skippable_example?(node) && !node.block_node
66
76
  end
67
77
  end
68
78
  end
@@ -59,63 +59,99 @@ module RuboCop
59
59
  class PendingWithoutReason < Base
60
60
  MSG = 'Give the reason for pending or skip.'
61
61
 
62
- # @!method pending_by_example_method?(node)
63
- def_node_matcher :pending_by_example_method?, block_pattern(<<~PATTERN)
64
- #Examples.pending
62
+ # @!method skipped_in_example?(node)
63
+ def_node_matcher :skipped_in_example?, <<~PATTERN
64
+ {
65
+ (send nil? ${#Examples.skipped #Examples.pending})
66
+ (block (send nil? ${#Examples.skipped}) ...)
67
+ (numblock (send nil? ${#Examples.skipped}) ...)
68
+ }
65
69
  PATTERN
66
70
 
67
- # @!method pending_by_metadata_without_reason?(node)
68
- def_node_matcher :pending_by_metadata_without_reason?, <<~PATTERN
69
- (send #rspec? {#ExampleGroups.all #Examples.all} ... {<(sym :pending) ...> (hash <(pair (sym :pending) true) ...>)})
71
+ # @!method skipped_by_example_method?(node)
72
+ def_node_matcher :skipped_by_example_method?, <<~PATTERN
73
+ (send nil? ${#Examples.skipped #Examples.pending})
70
74
  PATTERN
71
75
 
72
- # @!method skipped_by_example_method?(node)
73
- def_node_matcher :skipped_by_example_method?, block_pattern(<<~PATTERN)
74
- #Examples.skipped
76
+ # @!method skipped_by_example_method_with_block?(node)
77
+ def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
78
+ ({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
79
+ PATTERN
80
+
81
+ # @!method metadata_without_reason?(node)
82
+ def_node_matcher :metadata_without_reason?, <<~PATTERN
83
+ (send #rspec?
84
+ {#ExampleGroups.all #Examples.all} ...
85
+ {
86
+ <(sym ${:pending :skip}) ...>
87
+ (hash <(pair (sym ${:pending :skip}) true) ...>)
88
+ }
89
+ )
75
90
  PATTERN
76
91
 
77
92
  # @!method skipped_by_example_group_method?(node)
78
- def_node_matcher(
79
- :skipped_by_example_group_method?,
80
- block_pattern(<<~PATTERN)
81
- #ExampleGroups.skipped
82
- PATTERN
83
- )
84
-
85
- # @!method skipped_by_metadata_without_reason?(node)
86
- def_node_matcher :skipped_by_metadata_without_reason?, <<~PATTERN
87
- (send #rspec? {#ExampleGroups.all #Examples.all} ... {<(sym :skip) ...> (hash <(pair (sym :skip) true) ...>)})
93
+ def_node_matcher :skipped_by_example_group_method?, <<~PATTERN
94
+ (send #rspec? ${#ExampleGroups.skipped} ...)
95
+ PATTERN
96
+
97
+ # @!method pending_step_without_reason?(node)
98
+ def_node_matcher :pending_step_without_reason?, <<~PATTERN
99
+ (send nil? {:skip :pending})
88
100
  PATTERN
89
101
 
90
102
  def on_send(node)
91
- if pending_without_reason?(node)
92
- add_offense(node, message: 'Give the reason for pending.')
93
- elsif skipped_without_reason?(node)
94
- add_offense(node, message: 'Give the reason for skip.')
103
+ on_pending_by_metadata(node)
104
+ return unless (parent = parent_node(node))
105
+
106
+ if example_group?(parent) || block_node_example_group?(node)
107
+ on_skipped_by_example_method(node)
108
+ on_skipped_by_example_group_method(node)
109
+ elsif example?(parent)
110
+ on_skipped_by_in_example_method(node)
95
111
  end
96
112
  end
97
113
 
98
114
  private
99
115
 
100
- def pending_by_pending_step_without_reason?(node)
101
- node.method?(:pending) && node.first_argument.nil?
116
+ def parent_node(node)
117
+ node_or_block = node.block_node || node
118
+ return unless (parent = node_or_block.parent)
119
+
120
+ parent.begin_type? && parent.parent ? parent.parent : parent
121
+ end
122
+
123
+ def block_node_example_group?(node)
124
+ node.block_node &&
125
+ example_group?(node.block_node) &&
126
+ explicit_rspec?(node.receiver)
102
127
  end
103
128
 
104
- def pending_without_reason?(node)
105
- pending_by_example_method?(node.block_node) ||
106
- pending_by_metadata_without_reason?(node) ||
107
- pending_by_pending_step_without_reason?(node)
129
+ def on_skipped_by_in_example_method(node)
130
+ skipped_in_example?(node) do |pending|
131
+ add_offense(node, message: "Give the reason for #{pending}.")
132
+ end
133
+ end
134
+
135
+ def on_pending_by_metadata(node)
136
+ metadata_without_reason?(node) do |pending|
137
+ add_offense(node, message: "Give the reason for #{pending}.")
138
+ end
108
139
  end
109
140
 
110
- def skipped_by_skip_step_without_reason?(node)
111
- node.method?(:skip) && node.first_argument.nil?
141
+ def on_skipped_by_example_method(node)
142
+ skipped_by_example_method?(node) do |pending|
143
+ add_offense(node, message: "Give the reason for #{pending}.")
144
+ end
145
+
146
+ skipped_by_example_method_with_block?(node.parent) do |pending|
147
+ add_offense(node, message: "Give the reason for #{pending}.")
148
+ end
112
149
  end
113
150
 
114
- def skipped_without_reason?(node)
115
- skipped_by_example_group_method?(node.block_node) ||
116
- skipped_by_example_method?(node.block_node) ||
117
- skipped_by_metadata_without_reason?(node) ||
118
- skipped_by_skip_step_without_reason?(node)
151
+ def on_skipped_by_example_group_method(node)
152
+ skipped_by_example_group_method?(node) do
153
+ add_offense(node, message: 'Give the reason for skip.')
154
+ end
119
155
  end
120
156
  end
121
157
  end