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,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