rubocop-rspec 1.38.1 → 1.43.0

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -0
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/README.md +1 -61
  5. data/config/default.yml +159 -19
  6. data/lib/rubocop-rspec.rb +5 -2
  7. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +12 -19
  8. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +12 -19
  9. data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
  10. data/lib/rubocop/cop/rspec/around_block.rb +1 -1
  11. data/lib/rubocop/cop/rspec/base.rb +74 -0
  12. data/lib/rubocop/cop/rspec/be.rb +2 -2
  13. data/lib/rubocop/cop/rspec/be_eql.rb +6 -6
  14. data/lib/rubocop/cop/rspec/before_after_all.rb +1 -1
  15. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +19 -17
  16. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +14 -12
  17. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +69 -0
  18. data/lib/rubocop/cop/rspec/context_method.rb +7 -9
  19. data/lib/rubocop/cop/rspec/context_wording.rb +3 -3
  20. data/lib/rubocop/cop/rspec/cop.rb +3 -87
  21. data/lib/rubocop/cop/rspec/describe_class.rb +29 -23
  22. data/lib/rubocop/cop/rspec/describe_method.rb +14 -7
  23. data/lib/rubocop/cop/rspec/describe_symbol.rb +2 -2
  24. data/lib/rubocop/cop/rspec/described_class.rb +12 -9
  25. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -1
  26. data/lib/rubocop/cop/rspec/dialect.rb +5 -12
  27. data/lib/rubocop/cop/rspec/empty_example_group.rb +91 -7
  28. data/lib/rubocop/cop/rspec/empty_hook.rb +46 -0
  29. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +5 -7
  30. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +5 -9
  31. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +8 -8
  32. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +5 -9
  33. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +6 -6
  34. data/lib/rubocop/cop/rspec/example_length.rb +1 -1
  35. data/lib/rubocop/cop/rspec/example_without_description.rb +1 -1
  36. data/lib/rubocop/cop/rspec/example_wording.rb +10 -11
  37. data/lib/rubocop/cop/rspec/expect_actual.rb +8 -11
  38. data/lib/rubocop/cop/rspec/expect_change.rb +10 -35
  39. data/lib/rubocop/cop/rspec/expect_in_hook.rb +3 -3
  40. data/lib/rubocop/cop/rspec/expect_output.rb +2 -2
  41. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +24 -21
  42. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +20 -22
  43. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +7 -8
  44. data/lib/rubocop/cop/rspec/file_path.rb +57 -21
  45. data/lib/rubocop/cop/rspec/focus.rb +7 -11
  46. data/lib/rubocop/cop/rspec/hook_argument.rb +16 -23
  47. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +10 -29
  48. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -1
  49. data/lib/rubocop/cop/rspec/implicit_expect.rb +7 -15
  50. data/lib/rubocop/cop/rspec/implicit_subject.rb +16 -11
  51. data/lib/rubocop/cop/rspec/instance_spy.rb +18 -12
  52. data/lib/rubocop/cop/rspec/instance_variable.rb +4 -8
  53. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +3 -6
  54. data/lib/rubocop/cop/rspec/it_behaves_like.rb +5 -6
  55. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  56. data/lib/rubocop/cop/rspec/leading_subject.rb +22 -26
  57. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +2 -5
  58. data/lib/rubocop/cop/rspec/let_before_examples.rb +10 -26
  59. data/lib/rubocop/cop/rspec/let_setup.rb +21 -6
  60. data/lib/rubocop/cop/rspec/message_chain.rb +7 -6
  61. data/lib/rubocop/cop/rspec/message_expectation.rb +2 -2
  62. data/lib/rubocop/cop/rspec/message_spies.rb +2 -3
  63. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
  64. data/lib/rubocop/cop/rspec/multiple_describes.rb +11 -8
  65. data/lib/rubocop/cop/rspec/multiple_expectations.rb +7 -11
  66. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +148 -0
  67. data/lib/rubocop/cop/rspec/multiple_subjects.rb +18 -19
  68. data/lib/rubocop/cop/rspec/named_subject.rb +8 -8
  69. data/lib/rubocop/cop/rspec/nested_groups.rb +12 -13
  70. data/lib/rubocop/cop/rspec/not_to_not.rb +5 -6
  71. data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
  72. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  73. data/lib/rubocop/cop/rspec/predicate_matcher.rb +32 -69
  74. data/lib/rubocop/cop/rspec/rails/http_status.rb +7 -9
  75. data/lib/rubocop/cop/rspec/receive_counts.rb +15 -17
  76. data/lib/rubocop/cop/rspec/receive_never.rb +12 -12
  77. data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
  78. data/lib/rubocop/cop/rspec/repeated_example.rb +2 -2
  79. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +12 -2
  80. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +1 -1
  81. data/lib/rubocop/cop/rspec/return_from_stub.rb +12 -22
  82. data/lib/rubocop/cop/rspec/scattered_let.rb +12 -2
  83. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  84. data/lib/rubocop/cop/rspec/shared_context.rb +8 -21
  85. data/lib/rubocop/cop/rspec/shared_examples.rb +7 -9
  86. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +15 -18
  87. data/lib/rubocop/cop/rspec/subject_stub.rb +25 -53
  88. data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -1
  89. data/lib/rubocop/cop/rspec/variable_definition.rb +56 -0
  90. data/lib/rubocop/cop/rspec/variable_name.rb +66 -0
  91. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  92. data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
  93. data/lib/rubocop/cop/rspec/yield.rb +14 -11
  94. data/lib/rubocop/cop/rspec_cops.rb +6 -1
  95. data/lib/rubocop/rspec/corrector/move_node.rb +54 -0
  96. data/lib/rubocop/rspec/description_extractor.rb +2 -6
  97. data/lib/rubocop/rspec/{blank_line_separation.rb → empty_line_separation.rb} +13 -10
  98. data/lib/rubocop/rspec/example_group.rb +21 -49
  99. data/lib/rubocop/rspec/factory_bot.rb +7 -1
  100. data/lib/rubocop/rspec/language.rb +13 -3
  101. data/lib/rubocop/rspec/language/node_pattern.rb +11 -2
  102. data/lib/rubocop/rspec/top_level_describe.rb +2 -2
  103. data/lib/rubocop/rspec/top_level_group.rb +55 -0
  104. data/lib/rubocop/rspec/variable.rb +16 -0
  105. data/lib/rubocop/rspec/version.rb +1 -1
  106. metadata +36 -13
  107. data/lib/rubocop/rspec/util.rb +0 -19
@@ -23,22 +23,20 @@ module RuboCop
23
23
  # describe '.foo_bar' do
24
24
  # # ...
25
25
  # end
26
- class ContextMethod < Cop
26
+ class ContextMethod < Base
27
+ extend AutoCorrector
28
+
27
29
  MSG = 'Use `describe` for testing methods.'
28
30
 
29
31
  def_node_matcher :context_method, <<-PATTERN
30
- (block (send #{RSPEC} :context $(str #method_name?) ...) ...)
32
+ (block (send #rspec? :context $(str #method_name?) ...) ...)
31
33
  PATTERN
32
34
 
33
35
  def on_block(node)
34
36
  context_method(node) do |context|
35
- add_offense(context)
36
- end
37
- end
38
-
39
- def autocorrect(node)
40
- lambda do |corrector|
41
- corrector.replace(node.parent.loc.selector, 'describe')
37
+ add_offense(context) do |corrector|
38
+ corrector.replace(node.send_node.loc.selector, 'describe')
39
+ end
42
40
  end
43
41
  end
44
42
 
@@ -34,11 +34,11 @@ module RuboCop
34
34
  # context 'when the display name is not present' do
35
35
  # # ...
36
36
  # end
37
- class ContextWording < Cop
37
+ class ContextWording < Base
38
38
  MSG = 'Start context description with %<prefixes>s.'
39
39
 
40
40
  def_node_matcher :context_wording, <<-PATTERN
41
- (block (send #{RSPEC} { :context :shared_context } $(str #bad_prefix?) ...) ...)
41
+ (block (send #rspec? { :context :shared_context } $(str #bad_prefix?) ...) ...)
42
42
  PATTERN
43
43
 
44
44
  def on_block(node)
@@ -51,7 +51,7 @@ module RuboCop
51
51
  private
52
52
 
53
53
  def bad_prefix?(description)
54
- !prefixes.include?(description.split.first)
54
+ !prefixes.include?(description.split(/\b/).first)
55
55
  end
56
56
 
57
57
  def joined_prefixes
@@ -1,94 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
- module Cop # rubocop:disable Style/Documentation
5
- WorkaroundCop = Cop.dup
6
-
7
- # Clone of the the normal RuboCop::Cop::Cop class so we can rewrite
8
- # the inherited method without breaking functionality
9
- class WorkaroundCop
10
- # Remove the Cop.inherited method to be a noop. Our RSpec::Cop
11
- # class will invoke the inherited hook instead
12
- class << self
13
- undef inherited
14
- def inherited(*) end
15
- end
16
-
17
- # Special case `Module#<` so that the rspec support rubocop exports
18
- # is compatible with our subclass
19
- def self.<(other)
20
- other.equal?(RuboCop::Cop::Cop) || super
21
- end
22
- end
23
- private_constant(:WorkaroundCop)
24
-
4
+ module Cop
25
5
  module RSpec
26
- # @abstract parent class to rspec cops
27
- #
28
- # The criteria for whether rubocop-rspec analyzes a certain ruby file
29
- # is configured via `AllCops/RSpec`. For example, if you want to
30
- # customize your project to scan all files within a `test/` directory
31
- # then you could add this to your configuration:
32
- #
33
- # @example configuring analyzed paths
34
- #
35
- # AllCops:
36
- # RSpec:
37
- # Patterns:
38
- # - '_test.rb$'
39
- # - '(?:^|/)test/'
40
- class Cop < WorkaroundCop
41
- include RuboCop::RSpec::Language
42
- include RuboCop::RSpec::Language::NodePattern
43
-
44
- DEFAULT_CONFIGURATION =
45
- RuboCop::RSpec::CONFIG.fetch('AllCops').fetch('RSpec')
46
-
47
- DEFAULT_PATTERN_RE = Regexp.union(
48
- DEFAULT_CONFIGURATION.fetch('Patterns')
49
- .map(&Regexp.public_method(:new))
50
- )
51
-
52
- # Invoke the original inherited hook so our cops are recognized
53
- def self.inherited(subclass)
54
- RuboCop::Cop::Cop.inherited(subclass)
55
- end
56
-
57
- def relevant_file?(file)
58
- relevant_rubocop_rspec_file?(file) && super
59
- end
60
-
61
- private
62
-
63
- def relevant_rubocop_rspec_file?(file)
64
- rspec_pattern =~ file
65
- end
66
-
67
- def rspec_pattern
68
- if rspec_pattern_config?
69
- Regexp.union(rspec_pattern_config.map(&Regexp.public_method(:new)))
70
- else
71
- DEFAULT_PATTERN_RE
72
- end
73
- end
74
-
75
- def all_cops_config
76
- config
77
- .for_all_cops
78
- end
79
-
80
- def rspec_pattern_config?
81
- return unless all_cops_config.key?('RSpec')
82
-
83
- all_cops_config.fetch('RSpec').key?('Patterns')
84
- end
85
-
86
- def rspec_pattern_config
87
- all_cops_config
88
- .fetch('RSpec', DEFAULT_CONFIGURATION)
89
- .fetch('Patterns')
90
- end
91
- end
6
+ # @deprecated Use ::RuboCop::Cop::RSpec::Base instead
7
+ Cop = Base
92
8
  end
93
9
  end
94
10
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Check that the first argument to the top level describe is a constant.
6
+ # Check that the first argument to the top-level describe is a constant.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -12,44 +12,50 @@ module RuboCop
12
12
  #
13
13
  # # good
14
14
  # describe TestedClass do
15
+ # subject { described_class }
16
+ # end
17
+ #
18
+ # describe 'TestedClass::VERSION' do
19
+ # subject { Object.const_get(self.class.description) }
15
20
  # end
16
21
  #
17
22
  # describe "A feature example", type: :feature do
18
23
  # end
19
- class DescribeClass < Cop
20
- include RuboCop::RSpec::TopLevelDescribe
24
+ class DescribeClass < Base
25
+ include RuboCop::RSpec::TopLevelGroup
21
26
 
22
27
  MSG = 'The first argument to describe should be '\
23
28
  'the class or module being tested.'
24
29
 
25
- def_node_matcher :valid_describe?, <<-PATTERN
26
- {
27
- (send #{RSPEC} :describe const ...)
28
- (send #{RSPEC} :describe)
29
- }
30
- PATTERN
31
-
32
- def_node_matcher :describe_with_rails_metadata?, <<-PATTERN
33
- (send #{RSPEC} :describe !const ...
34
- (hash <#rails_metadata? ...>)
35
- )
36
- PATTERN
37
-
38
30
  def_node_matcher :rails_metadata?, <<-PATTERN
39
31
  (pair
40
32
  (sym :type)
41
- (sym {:request :feature :system :routing :view})
33
+ (sym { :channel :controller :helper :job :mailer :model :request
34
+ :routing :view :feature :system :mailbox })
42
35
  )
43
36
  PATTERN
44
37
 
45
- def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
38
+ def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
39
+ (send #rspec? :describe ... (hash <#rails_metadata? ...>))
40
+ PATTERN
41
+
42
+ def_node_matcher :not_a_const_described, <<~PATTERN
43
+ (send #rspec? :describe $[!const !#string_constant?] ...)
44
+ PATTERN
45
+
46
+ def on_top_level_group(top_level_node)
47
+ return if example_group_with_rails_metadata?(top_level_node.send_node)
48
+
49
+ not_a_const_described(top_level_node.send_node) do |described|
50
+ add_offense(described)
51
+ end
52
+ end
46
53
 
47
- def on_top_level_describe(node, args)
48
- return if shared_group?(root_node)
49
- return if valid_describe?(node)
50
- return if describe_with_rails_metadata?(node)
54
+ private
51
55
 
52
- add_offense(args.first)
56
+ def string_constant?(described)
57
+ described.str_type? &&
58
+ described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
53
59
  end
54
60
  end
55
61
  end
@@ -16,18 +16,25 @@ module RuboCop
16
16
  #
17
17
  # describe MyClass, '.my_class_method' do
18
18
  # end
19
- class DescribeMethod < Cop
20
- include RuboCop::RSpec::TopLevelDescribe
21
- include RuboCop::RSpec::Util
19
+ class DescribeMethod < Base
20
+ include RuboCop::RSpec::TopLevelGroup
22
21
 
23
22
  MSG = 'The second argument to describe should be the method '\
24
23
  "being tested. '#instance' or '.class'."
25
24
 
26
- def on_top_level_describe(_node, (_, second_arg))
27
- return unless second_arg&.str_type?
28
- return if second_arg.str_content.start_with?('#', '.')
25
+ def_node_matcher :second_argument, <<~PATTERN
26
+ (block
27
+ (send #rspec? :describe _first_argument $(str _) ...) ...
28
+ )
29
+ PATTERN
29
30
 
30
- add_offense(second_arg)
31
+ def on_top_level_group(node)
32
+ second_argument = second_argument(node)
33
+
34
+ return unless second_argument
35
+ return if second_argument.str_content.start_with?('#', '.')
36
+
37
+ add_offense(second_argument)
31
38
  end
32
39
  end
33
40
  end
@@ -17,11 +17,11 @@ module RuboCop
17
17
  # end
18
18
  #
19
19
  # @see https://github.com/rspec/rspec-core/issues/1610
20
- class DescribeSymbol < Cop
20
+ class DescribeSymbol < Base
21
21
  MSG = 'Avoid describing symbols.'
22
22
 
23
23
  def_node_matcher :describe_symbol?, <<-PATTERN
24
- (send #{RSPEC} :describe $sym ...)
24
+ (send #rspec? :describe $sym ...)
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
@@ -54,7 +54,8 @@ module RuboCop
54
54
  # end
55
55
  # end
56
56
  #
57
- class DescribedClass < Cop
57
+ class DescribedClass < Base
58
+ extend AutoCorrector
58
59
  include ConfigurableEnforcedStyle
59
60
 
60
61
  DESCRIBED_CLASS = 'described_class'
@@ -85,22 +86,24 @@ module RuboCop
85
86
  return unless body
86
87
 
87
88
  find_usage(body) do |match|
88
- add_offense(match, message: message(match.const_name))
89
+ msg = message(match.const_name)
90
+ add_offense(match, message: msg) do |corrector|
91
+ autocorrect(corrector, match)
92
+ end
89
93
  end
90
94
  end
91
95
 
92
- def autocorrect(node)
96
+ private
97
+
98
+ def autocorrect(corrector, match)
93
99
  replacement = if style == :described_class
94
100
  DESCRIBED_CLASS
95
101
  else
96
102
  @described_class.const_name
97
103
  end
98
- lambda do |corrector|
99
- corrector.replace(node.loc.expression, replacement)
100
- end
101
- end
102
104
 
103
- private
105
+ corrector.replace(match, replacement)
106
+ end
104
107
 
105
108
  def find_usage(node, &block)
106
109
  yield(node) if offensive?(node)
@@ -139,7 +142,7 @@ module RuboCop
139
142
  if style == :described_class
140
143
  offensive_described_class?(node)
141
144
  else
142
- node.send_type? && node.method_name == :described_class
145
+ node.send_type? && node.method?(:described_class)
143
146
  end
144
147
  end
145
148
 
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # end
20
20
  #
21
21
  # @see https://github.com/rubocop-hq/rubocop-rspec/issues/735
22
- class DescribedClassModuleWrapping < Cop
22
+ class DescribedClassModuleWrapping < Base
23
23
  MSG = 'Avoid opening modules and defining specs within them.'
24
24
 
25
25
  def_node_search :find_rspec_blocks,
@@ -41,7 +41,8 @@ module RuboCop
41
41
  # describe 'display name presence' do
42
42
  # # ...
43
43
  # end
44
- class Dialect < Cop
44
+ class Dialect < Base
45
+ extend AutoCorrector
45
46
  include MethodPreference
46
47
 
47
48
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
@@ -52,24 +53,16 @@ module RuboCop
52
53
  return unless rspec_method?(node)
53
54
  return unless preferred_methods[node.method_name]
54
55
 
55
- add_offense(node)
56
- end
56
+ msg = format(MSG, prefer: preferred_method(node.method_name),
57
+ current: node.method_name)
57
58
 
58
- def autocorrect(node)
59
- lambda do |corrector|
59
+ add_offense(node, message: msg) do |corrector|
60
60
  current = node.loc.selector
61
61
  preferred = preferred_method(current.source)
62
62
 
63
63
  corrector.replace(current, preferred)
64
64
  end
65
65
  end
66
-
67
- private
68
-
69
- def message(node)
70
- format(MSG, prefer: preferred_method(node.method_name),
71
- current: node.method_name)
72
- end
73
66
  end
74
67
  end
75
68
  end
@@ -57,20 +57,104 @@ module RuboCop
57
57
  # end
58
58
  # end
59
59
  #
60
- class EmptyExampleGroup < Cop
60
+ class EmptyExampleGroup < Base
61
61
  MSG = 'Empty example group detected.'
62
62
 
63
- def_node_search :contains_example?, <<-PATTERN
63
+ # @!method example_group_body(node)
64
+ # Match example group blocks and yield their body
65
+ #
66
+ # @example source that matches
67
+ # describe 'example group' do
68
+ # it { is_expected.to be }
69
+ # end
70
+ #
71
+ # @param node [RuboCop::AST::Node]
72
+ # @yield [RuboCop::AST::Node] example group body
73
+ def_node_matcher :example_group_body, <<~PATTERN
74
+ (block #{ExampleGroups::ALL.send_pattern} args $_)
75
+ PATTERN
76
+
77
+ # @!method example_or_group_or_include?(node)
78
+ # Match examples, example groups and includes
79
+ #
80
+ # @example source that matches
81
+ # it { is_expected.to fly }
82
+ # describe('non-empty example groups too') { }
83
+ # it_behaves_like 'an animal'
84
+ # it_behaves_like('a cat') { let(:food) { 'milk' } }
85
+ # it_has_root_access
86
+ #
87
+ # @param node [RuboCop::AST::Node]
88
+ # @return [Array<RuboCop::AST::Node>] matching nodes
89
+ def_node_matcher :example_or_group_or_include?, <<~PATTERN
64
90
  {
65
- #{(Examples::ALL + Includes::ALL).send_pattern}
66
- (send _ #custom_include? ...)
91
+ #{Examples::ALL.block_pattern}
92
+ #{ExampleGroups::ALL.block_pattern}
93
+ #{Includes::ALL.send_pattern}
94
+ #{Includes::ALL.block_pattern}
95
+ (send nil? #custom_include? ...)
67
96
  }
68
97
  PATTERN
69
98
 
70
- def on_block(node)
71
- return unless example_group?(node) && !contains_example?(node)
99
+ # @!method examples_inside_block?(node)
100
+ # Match examples defined inside a block which is not a hook
101
+ #
102
+ # @example source that matches
103
+ # %w(r g b).each do |color|
104
+ # it { is_expected.to have_color(color) }
105
+ # end
106
+ #
107
+ # @example source that does not match
108
+ # before do
109
+ # it { is_expected.to fall_into_oblivion }
110
+ # end
111
+ #
112
+ # @param node [RuboCop::AST::Node]
113
+ # @return [Array<RuboCop::AST::Node>] matching nodes
114
+ def_node_matcher :examples_inside_block?, <<~PATTERN
115
+ (block !#{Hooks::ALL.send_pattern} _ #examples?)
116
+ PATTERN
72
117
 
73
- add_offense(node.send_node)
118
+ # @!method examples_directly_or_in_block?(node)
119
+ # Match examples or examples inside blocks
120
+ #
121
+ # @example source that matches
122
+ # it { expect(drink).to be_cold }
123
+ # context('when winter') { it { expect(drink).to be_hot } }
124
+ # (1..5).each { |divisor| it { is_expected.to divide_by(divisor) } }
125
+ #
126
+ # @param node [RuboCop::AST::Node]
127
+ # @return [Array<RuboCop::AST::Node>] matching nodes
128
+ def_node_matcher :examples_directly_or_in_block?, <<~PATTERN
129
+ {
130
+ #example_or_group_or_include?
131
+ #examples_inside_block?
132
+ }
133
+ PATTERN
134
+
135
+ # @!method examples?(node)
136
+ # Matches examples defined in scopes where they could run
137
+ #
138
+ # @example source that matches
139
+ # it { expect(myself).to be_run }
140
+ # describe { it { i_run_as_well } }
141
+ #
142
+ # @example source that does not match
143
+ # before { it { whatever here wont run anyway } }
144
+ #
145
+ # @param node [RuboCop::AST::Node]
146
+ # @return [Array<RuboCop::AST::Node>] matching nodes
147
+ def_node_matcher :examples?, <<~PATTERN
148
+ {
149
+ #examples_directly_or_in_block?
150
+ (begin <#examples_directly_or_in_block? ...>)
151
+ }
152
+ PATTERN
153
+
154
+ def on_block(node)
155
+ example_group_body(node) do |body|
156
+ add_offense(node.send_node) unless examples?(body)
157
+ end
74
158
  end
75
159
 
76
160
  private