rubocop-rspec 1.43.1 → 2.0.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -4
  3. data/README.md +4 -0
  4. data/config/default.yml +141 -25
  5. data/lib/rubocop-rspec.rb +7 -8
  6. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +7 -3
  7. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +7 -3
  8. data/lib/rubocop/cop/rspec/around_block.rb +1 -1
  9. data/lib/rubocop/cop/rspec/base.rb +7 -54
  10. data/lib/rubocop/cop/rspec/be.rb +1 -1
  11. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +2 -2
  12. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +3 -2
  13. data/lib/rubocop/cop/rspec/describe_class.rb +33 -14
  14. data/lib/rubocop/cop/rspec/describe_method.rb +1 -1
  15. data/lib/rubocop/cop/rspec/described_class.rb +1 -2
  16. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -2
  17. data/lib/rubocop/cop/rspec/dialect.rb +1 -1
  18. data/lib/rubocop/cop/rspec/empty_example_group.rb +33 -38
  19. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  20. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
  21. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  22. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -1
  23. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -1
  24. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -2
  25. data/lib/rubocop/cop/rspec/expect_actual.rb +1 -1
  26. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  27. data/lib/rubocop/cop/rspec/file_path.rb +2 -2
  28. data/lib/rubocop/cop/rspec/focus.rb +13 -7
  29. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -4
  30. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +2 -2
  31. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -2
  32. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  33. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  34. data/lib/rubocop/cop/rspec/leading_subject.rb +5 -1
  35. data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -2
  36. data/lib/rubocop/cop/rspec/let_setup.rb +7 -4
  37. data/lib/rubocop/cop/rspec/message_spies.rb +1 -1
  38. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +51 -0
  39. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  40. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  41. data/lib/rubocop/cop/rspec/mixin/variable.rb +20 -0
  42. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
  43. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  44. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +3 -1
  45. data/lib/rubocop/cop/rspec/named_subject.rb +8 -12
  46. data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
  47. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  48. data/lib/rubocop/cop/rspec/pending.rb +13 -5
  49. data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
  50. data/lib/rubocop/cop/rspec/repeated_include_example.rb +104 -0
  51. data/lib/rubocop/cop/rspec/shared_context.rb +16 -6
  52. data/lib/rubocop/cop/rspec/shared_examples.rb +3 -1
  53. data/lib/rubocop/cop/rspec/stubbed_mock.rb +172 -0
  54. data/lib/rubocop/cop/rspec/subject_stub.rb +6 -6
  55. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
  56. data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
  57. data/lib/rubocop/cop/rspec_cops.rb +2 -1
  58. data/lib/rubocop/rspec/align_let_brace.rb +1 -1
  59. data/lib/rubocop/rspec/concept.rb +2 -2
  60. data/lib/rubocop/rspec/config_formatter.rb +3 -3
  61. data/lib/rubocop/rspec/corrector/move_node.rb +1 -1
  62. data/lib/rubocop/rspec/example_group.rb +15 -5
  63. data/lib/rubocop/rspec/hook.rb +2 -6
  64. data/lib/rubocop/rspec/inject.rb +4 -2
  65. data/lib/rubocop/rspec/language.rb +144 -105
  66. data/lib/rubocop/rspec/language/node_pattern.rb +7 -24
  67. data/lib/rubocop/rspec/version.rb +1 -1
  68. metadata +25 -13
  69. data/lib/rubocop/cop/rspec/cop.rb +0 -10
  70. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +0 -41
  71. data/lib/rubocop/rspec.rb +0 -12
  72. data/lib/rubocop/rspec/empty_line_separation.rb +0 -48
  73. data/lib/rubocop/rspec/final_end_location.rb +0 -17
  74. data/lib/rubocop/rspec/top_level_describe.rb +0 -52
  75. data/lib/rubocop/rspec/top_level_group.rb +0 -57
  76. data/lib/rubocop/rspec/variable.rb +0 -16
@@ -23,7 +23,7 @@ module RuboCop
23
23
  MSG = 'Don\'t use `be` without an argument.'
24
24
 
25
25
  def_node_matcher :be_without_args, <<-PATTERN
26
- (send _ #{Runners::ALL.node_pattern_union} $(send nil? :be))
26
+ (send _ #Runners.all $(send nil? :be))
27
27
  PATTERN
28
28
 
29
29
  def on_send(node)
@@ -37,13 +37,13 @@ module RuboCop
37
37
  # Supported matchers: eq(...) / match(/regexp/) / match('regexp')
38
38
  def_node_matcher :as_is_matcher, <<-PATTERN
39
39
  (send
40
- #expectation_set_on_current_path $#{Runners::ALL.node_pattern_union}
40
+ #expectation_set_on_current_path $#Runners.all
41
41
  ${(send nil? :eq ...) (send nil? :match (regexp ...))})
42
42
  PATTERN
43
43
 
44
44
  def_node_matcher :regexp_str_matcher, <<-PATTERN
45
45
  (send
46
- #expectation_set_on_current_path $#{Runners::ALL.node_pattern_union}
46
+ #expectation_set_on_current_path $#Runners.all
47
47
  $(send nil? :match (str $_)))
48
48
  PATTERN
49
49
 
@@ -55,8 +55,9 @@ module RuboCop
55
55
  feature: :describe
56
56
  }.freeze
57
57
 
58
- def_node_matcher :capybara_speak,
59
- SelectorSet.new(MAP.keys).node_pattern_union
58
+ def_node_matcher :capybara_speak, <<-PATTERN
59
+ {#{MAP.keys.map(&:inspect).join(' ')}}
60
+ PATTERN
60
61
 
61
62
  def_node_matcher :spec?, <<-PATTERN
62
63
  (block
@@ -5,6 +5,19 @@ module RuboCop
5
5
  module RSpec
6
6
  # Check that the first argument to the top-level describe is a constant.
7
7
  #
8
+ # It can be configured to ignore strings when certain metadata is passed.
9
+ #
10
+ # Ignores Rails and Aruba `type` metadata by default.
11
+ #
12
+ # @example `IgnoredMetadata` configuration
13
+ #
14
+ # # .rubocop.yml
15
+ # # RSpec/DescribeClass:
16
+ # # IgnoredMetadata:
17
+ # # type:
18
+ # # - request
19
+ # # - controller
20
+ #
8
21
  # @example
9
22
  # # bad
10
23
  # describe 'Do something' do
@@ -22,41 +35,47 @@ module RuboCop
22
35
  # describe "A feature example", type: :feature do
23
36
  # end
24
37
  class DescribeClass < Base
25
- include RuboCop::RSpec::TopLevelGroup
38
+ include TopLevelGroup
26
39
 
27
40
  MSG = 'The first argument to describe should be '\
28
41
  'the class or module being tested.'
29
42
 
30
- def_node_matcher :rails_metadata?, <<-PATTERN
31
- (pair
32
- (sym :type)
33
- (sym { :channel :controller :helper :job :mailer :model :request
34
- :routing :view :feature :system :mailbox })
35
- )
36
- PATTERN
37
-
38
- def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
39
- (send #rspec? :describe ... (hash <#rails_metadata? ...>))
43
+ def_node_matcher :example_group_with_ignored_metadata?, <<~PATTERN
44
+ (send #rspec? :describe ... (hash <#ignored_metadata? ...>))
40
45
  PATTERN
41
46
 
42
47
  def_node_matcher :not_a_const_described, <<~PATTERN
43
48
  (send #rspec? :describe $[!const !#string_constant?] ...)
44
49
  PATTERN
45
50
 
46
- def on_top_level_group(top_level_node)
47
- return if example_group_with_rails_metadata?(top_level_node.send_node)
51
+ def_node_matcher :sym_pair, <<~PATTERN
52
+ (pair $sym $sym)
53
+ PATTERN
48
54
 
49
- not_a_const_described(top_level_node.send_node) do |described|
55
+ def on_top_level_group(node)
56
+ return if example_group_with_ignored_metadata?(node.send_node)
57
+
58
+ not_a_const_described(node.send_node) do |described|
50
59
  add_offense(described)
51
60
  end
52
61
  end
53
62
 
54
63
  private
55
64
 
65
+ def ignored_metadata?(node)
66
+ sym_pair(node) do |key, value|
67
+ ignored_metadata[key.value.to_s].to_a.include?(value.value.to_s)
68
+ end
69
+ end
70
+
56
71
  def string_constant?(described)
57
72
  described.str_type? &&
58
73
  described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
59
74
  end
75
+
76
+ def ignored_metadata
77
+ cop_config['IgnoredMetadata'] || {}
78
+ end
60
79
  end
61
80
  end
62
81
  end
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # describe MyClass, '.my_class_method' do
18
18
  # end
19
19
  class DescribeMethod < Base
20
- include RuboCop::RSpec::TopLevelGroup
20
+ include TopLevelGroup
21
21
 
22
22
  MSG = 'The second argument to describe should be the method '\
23
23
  "being tested. '#instance' or '.class'."
@@ -65,8 +65,7 @@ module RuboCop
65
65
  (block (send (const nil? {:Class :Module :Struct}) :new ...) ...)
66
66
  PATTERN
67
67
 
68
- def_node_matcher :rspec_block?,
69
- RuboCop::RSpec::Language::ALL.block_pattern
68
+ def_node_matcher :rspec_block?, block_pattern('#ALL.all')
70
69
 
71
70
  def_node_matcher :scope_changing_syntax?, '{def class module}'
72
71
 
@@ -22,8 +22,7 @@ module RuboCop
22
22
  class DescribedClassModuleWrapping < Base
23
23
  MSG = 'Avoid opening modules and defining specs within them.'
24
24
 
25
- def_node_search :find_rspec_blocks,
26
- ExampleGroups::ALL.block_pattern
25
+ def_node_search :find_rspec_blocks, block_pattern('#ExampleGroups.all')
27
26
 
28
27
  def on_module(node)
29
28
  find_rspec_blocks(node) do
@@ -47,7 +47,7 @@ module RuboCop
47
47
 
48
48
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
49
49
 
50
- def_node_matcher :rspec_method?, ALL.send_pattern
50
+ def_node_matcher :rspec_method?, send_pattern('#ALL.all')
51
51
 
52
52
  def on_send(node)
53
53
  return unless rspec_method?(node)
@@ -5,8 +5,6 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks if an example group does not include any tests.
7
7
  #
8
- # This cop is configurable using the `CustomIncludeMethods` option
9
- #
10
8
  # @example usage
11
9
  #
12
10
  # # bad
@@ -33,30 +31,10 @@ module RuboCop
33
31
  # end
34
32
  # end
35
33
  #
36
- # @example configuration
37
- #
38
- # # .rubocop.yml
39
- # # RSpec/EmptyExampleGroup:
40
- # # CustomIncludeMethods:
41
- # # - include_tests
42
- #
43
- # # spec_helper.rb
44
- # RSpec.configure do |config|
45
- # config.alias_it_behaves_like_to(:include_tests)
46
- # end
47
- #
48
- # # bacon_spec.rb
34
+ # # good
49
35
  # describe Bacon do
50
- # let(:bacon) { Bacon.new(chunkiness) }
51
- # let(:chunkiness) { false }
52
- #
53
- # context 'extra chunky' do # not flagged by rubocop
54
- # let(:chunkiness) { true }
55
- #
56
- # include_tests 'shared tests'
57
- # end
36
+ # pending 'will add tests later'
58
37
  # end
59
- #
60
38
  class EmptyExampleGroup < Base
61
39
  MSG = 'Empty example group detected.'
62
40
 
@@ -71,7 +49,7 @@ module RuboCop
71
49
  # @param node [RuboCop::AST::Node]
72
50
  # @yield [RuboCop::AST::Node] example group body
73
51
  def_node_matcher :example_group_body, <<~PATTERN
74
- (block #{ExampleGroups::ALL.send_pattern} args $_)
52
+ (block #{send_pattern('#ExampleGroups.all')} args $_)
75
53
  PATTERN
76
54
 
77
55
  # @!method example_or_group_or_include?(node)
@@ -83,16 +61,17 @@ module RuboCop
83
61
  # it_behaves_like 'an animal'
84
62
  # it_behaves_like('a cat') { let(:food) { 'milk' } }
85
63
  # it_has_root_access
64
+ # skip
65
+ # it 'will be implemented later'
86
66
  #
87
67
  # @param node [RuboCop::AST::Node]
88
68
  # @return [Array<RuboCop::AST::Node>] matching nodes
89
69
  def_node_matcher :example_or_group_or_include?, <<~PATTERN
90
70
  {
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? ...)
71
+ #{block_pattern(
72
+ '{#Examples.all #ExampleGroups.all #Includes.all}'
73
+ )}
74
+ #{send_pattern('{#Examples.all #Includes.all}')}
96
75
  }
97
76
  PATTERN
98
77
 
@@ -112,7 +91,7 @@ module RuboCop
112
91
  # @param node [RuboCop::AST::Node]
113
92
  # @return [Array<RuboCop::AST::Node>] matching nodes
114
93
  def_node_matcher :examples_inside_block?, <<~PATTERN
115
- (block !#{Hooks::ALL.send_pattern} _ #examples?)
94
+ (block !#{send_pattern('#Hooks.all')} _ #examples?)
116
95
  PATTERN
117
96
 
118
97
  # @!method examples_directly_or_in_block?(node)
@@ -152,21 +131,37 @@ module RuboCop
152
131
  PATTERN
153
132
 
154
133
  def on_block(node)
134
+ return if node.each_ancestor(:def, :defs).any?
135
+ return if node.each_ancestor(:block).any? { |block| example?(block) }
136
+
155
137
  example_group_body(node) do |body|
156
- add_offense(node.send_node) unless examples?(body)
138
+ add_offense(node.send_node) if offensive?(body)
157
139
  end
158
140
  end
159
141
 
160
142
  private
161
143
 
162
- def custom_include?(method_name)
163
- custom_include_methods.include?(method_name)
144
+ def offensive?(body)
145
+ return true unless body
146
+ return false if conditionals_with_examples?(body)
147
+
148
+ if body.if_type?
149
+ !examples_in_branches?(body)
150
+ else
151
+ !examples?(body)
152
+ end
153
+ end
154
+
155
+ def conditionals_with_examples?(body)
156
+ return unless body.begin_type?
157
+
158
+ body.each_descendant(:if).any? do |if_node|
159
+ examples_in_branches?(if_node)
160
+ end
164
161
  end
165
162
 
166
- def custom_include_methods
167
- cop_config
168
- .fetch('CustomIncludeMethods', [])
169
- .map(&:to_sym)
163
+ def examples_in_branches?(if_node)
164
+ if_node.branches.any? { |branch| examples?(branch) }
170
165
  end
171
166
  end
172
167
  end
@@ -29,7 +29,7 @@ module RuboCop
29
29
  MSG = 'Empty hook detected.'
30
30
 
31
31
  def_node_matcher :empty_hook?, <<~PATTERN
32
- (block $#{Hooks::ALL.send_pattern} _ nil?)
32
+ (block $#{send_pattern('#Hooks.all')} _ nil?)
33
33
  PATTERN
34
34
 
35
35
  def on_block(node)
@@ -43,7 +43,7 @@ module RuboCop
43
43
  #
44
44
  class EmptyLineAfterExample < Base
45
45
  extend AutoCorrector
46
- include RuboCop::RSpec::EmptyLineSeparation
46
+ include EmptyLineSeparation
47
47
 
48
48
  MSG = 'Add an empty line after `%<example>s`.'
49
49
 
@@ -25,7 +25,7 @@ module RuboCop
25
25
  #
26
26
  class EmptyLineAfterExampleGroup < Base
27
27
  extend AutoCorrector
28
- include RuboCop::RSpec::EmptyLineSeparation
28
+ include EmptyLineSeparation
29
29
 
30
30
  MSG = 'Add an empty line after `%<example_group>s`.'
31
31
 
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # it { does_something }
19
19
  class EmptyLineAfterFinalLet < Base
20
20
  extend AutoCorrector
21
- include RuboCop::RSpec::EmptyLineSeparation
21
+ include EmptyLineSeparation
22
22
 
23
23
  MSG = 'Add an empty line after the last `%<let>s`.'
24
24
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
  #
36
36
  class EmptyLineAfterHook < Base
37
37
  extend AutoCorrector
38
- include RuboCop::RSpec::EmptyLineSeparation
38
+ include EmptyLineSeparation
39
39
 
40
40
  MSG = 'Add an empty line after `%<hook>s`.'
41
41
 
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # let(:foo) { bar }
17
17
  class EmptyLineAfterSubject < Base
18
18
  extend AutoCorrector
19
- include RuboCop::RSpec::EmptyLineSeparation
19
+ include EmptyLineSeparation
20
20
 
21
21
  MSG = 'Add an empty line after `%<subject>s`.'
22
22
 
@@ -32,7 +32,7 @@ module RuboCop
32
32
 
33
33
  def in_spec_block?(node)
34
34
  node.each_ancestor(:block).any? do |ancestor|
35
- Examples::ALL.include?(ancestor.method_name)
35
+ Examples.all(ancestor.method_name)
36
36
  end
37
37
  end
38
38
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def_node_matcher :expect_literal, <<~PATTERN
49
49
  (send
50
50
  (send nil? :expect $#literal?)
51
- #{Runners::ALL.node_pattern_union}
51
+ #Runners.all
52
52
  {
53
53
  (send (send nil? $:be) :== $_)
54
54
  (send nil? $_ $_ ...)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  class ExpectInHook < Base
24
24
  MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'
25
25
 
26
- def_node_search :expectation, Expectations::ALL.send_pattern
26
+ def_node_search :expectation, send_pattern('#Expectations.all')
27
27
 
28
28
  def on_block(node)
29
29
  return unless hook?(node)
@@ -57,7 +57,7 @@ module RuboCop
57
57
  # my_class_spec.rb # describe MyClass, '#method'
58
58
  #
59
59
  class FilePath < Base
60
- include RuboCop::RSpec::TopLevelGroup
60
+ include TopLevelGroup
61
61
 
62
62
  MSG = 'Spec path should end with `%<suffix>s`.'
63
63
 
@@ -69,7 +69,7 @@ module RuboCop
69
69
 
70
70
  def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))'
71
71
 
72
- def on_top_level_group(node)
72
+ def on_top_level_example_group(node)
73
73
  return unless top_level_groups.one?
74
74
 
75
75
  const_described(node) do |send_node, described_class, arguments|
@@ -22,19 +22,25 @@ module RuboCop
22
22
  class Focus < Base
23
23
  MSG = 'Focused spec found.'
24
24
 
25
- focused = ExampleGroups::FOCUSED + Examples::FOCUSED
26
-
27
- def_node_matcher :focusable_selector?,
28
- (ExampleGroups::GROUPS + ExampleGroups::SKIPPED +
29
- Examples::EXAMPLES + Examples::SKIPPED +
30
- Examples::PENDING).node_pattern_union
25
+ def_node_matcher :focusable_selector?, <<-PATTERN
26
+ {
27
+ #ExampleGroups.regular
28
+ #ExampleGroups.skipped
29
+ #Examples.regular
30
+ #Examples.skipped
31
+ #Examples.pending
32
+ }
33
+ PATTERN
31
34
 
32
35
  def_node_matcher :metadata, <<-PATTERN
33
36
  {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
34
37
  (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
35
38
  PATTERN
36
39
 
37
- def_node_matcher :focused_block?, focused.send_pattern
40
+ def_node_matcher :focused_block?,
41
+ send_pattern(<<~PATTERN)
42
+ {#ExampleGroups.focused #Examples.focused}
43
+ PATTERN
38
44
 
39
45
  def on_send(node)
40
46
  focus_metadata(node) do |focus|
@@ -64,13 +64,11 @@ module RuboCop
64
64
  IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
65
65
  EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
66
66
 
67
- def_node_matcher :hook?, Hooks::ALL.node_pattern_union
68
-
69
67
  def_node_matcher :scoped_hook, <<-PATTERN
70
- (block $(send _ #hook? (sym ${:each :example})) ...)
68
+ (block $(send _ #Hooks.all (sym ${:each :example})) ...)
71
69
  PATTERN
72
70
 
73
- def_node_matcher :unscoped_hook, '(block $(send _ #hook?) ...)'
71
+ def_node_matcher :unscoped_hook, '(block $(send _ #Hooks.all) ...)'
74
72
 
75
73
  def on_block(node)
76
74
  hook(node) do |method_send, scope_name|
@@ -30,8 +30,8 @@ module RuboCop
30
30
 
31
31
  def_node_matcher :example_or_group?, <<-PATTERN
32
32
  {
33
- #{(Examples::ALL + ExampleGroups::ALL).block_pattern}
34
- #{Includes::EXAMPLES.send_pattern}
33
+ #{block_pattern('{#ExampleGroups.all #Examples.all}')}
34
+ #{send_pattern('#Includes.examples')}
35
35
  }
36
36
  PATTERN
37
37