rubocop-rspec 2.12.1 → 2.13.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -86
  3. data/config/default.yml +44 -6
  4. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +8 -9
  5. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +8 -9
  6. data/lib/rubocop/cop/rspec/any_instance.rb +1 -0
  7. data/lib/rubocop/cop/rspec/around_block.rb +26 -3
  8. data/lib/rubocop/cop/rspec/be.rb +0 -1
  9. data/lib/rubocop/cop/rspec/be_eq.rb +0 -1
  10. data/lib/rubocop/cop/rspec/be_eql.rb +0 -1
  11. data/lib/rubocop/cop/rspec/before_after_all.rb +1 -0
  12. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +9 -3
  13. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -1
  14. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +86 -0
  15. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +91 -10
  16. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -1
  17. data/lib/rubocop/cop/rspec/change_by_zero.rb +60 -5
  18. data/lib/rubocop/cop/rspec/class_check.rb +101 -0
  19. data/lib/rubocop/cop/rspec/context_method.rb +2 -1
  20. data/lib/rubocop/cop/rspec/context_wording.rb +49 -18
  21. data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
  22. data/lib/rubocop/cop/rspec/describe_method.rb +1 -0
  23. data/lib/rubocop/cop/rspec/described_class.rb +4 -14
  24. data/lib/rubocop/cop/rspec/dialect.rb +1 -0
  25. data/lib/rubocop/cop/rspec/empty_example_group.rb +19 -4
  26. data/lib/rubocop/cop/rspec/empty_hook.rb +2 -1
  27. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +4 -9
  28. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  29. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +2 -1
  30. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +32 -2
  31. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -1
  32. data/lib/rubocop/cop/rspec/example_length.rb +2 -1
  33. data/lib/rubocop/cop/rspec/example_without_description.rb +2 -1
  34. data/lib/rubocop/cop/rspec/example_wording.rb +2 -1
  35. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -0
  36. data/lib/rubocop/cop/rspec/expect_actual.rb +3 -0
  37. data/lib/rubocop/cop/rspec/expect_change.rb +1 -1
  38. data/lib/rubocop/cop/rspec/expect_in_hook.rb +4 -1
  39. data/lib/rubocop/cop/rspec/expect_output.rb +1 -0
  40. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +2 -1
  41. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +26 -12
  42. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -0
  43. data/lib/rubocop/cop/rspec/file_path.rb +6 -3
  44. data/lib/rubocop/cop/rspec/focus.rb +18 -0
  45. data/lib/rubocop/cop/rspec/hook_argument.rb +7 -2
  46. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +10 -9
  47. data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +0 -1
  48. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -0
  49. data/lib/rubocop/cop/rspec/implicit_expect.rb +0 -2
  50. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  51. data/lib/rubocop/cop/rspec/instance_variable.rb +0 -1
  52. data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -0
  53. data/lib/rubocop/cop/rspec/iterated_expectation.rb +16 -0
  54. data/lib/rubocop/cop/rspec/leading_subject.rb +15 -15
  55. data/lib/rubocop/cop/rspec/let_before_examples.rb +7 -8
  56. data/lib/rubocop/cop/rspec/let_setup.rb +4 -4
  57. data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
  58. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +2 -1
  59. data/lib/rubocop/cop/rspec/mixin/css_selector.rb +99 -0
  60. data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
  61. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -0
  62. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -5
  63. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -3
  64. data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -2
  65. data/lib/rubocop/cop/rspec/named_subject.rb +2 -1
  66. data/lib/rubocop/cop/rspec/nested_groups.rb +45 -25
  67. data/lib/rubocop/cop/rspec/no_expectation_example.rb +64 -0
  68. data/lib/rubocop/cop/rspec/not_to_not.rb +1 -2
  69. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  70. data/lib/rubocop/cop/rspec/pending.rb +1 -0
  71. data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -1
  72. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -2
  73. data/lib/rubocop/cop/rspec/receive_counts.rb +14 -15
  74. data/lib/rubocop/cop/rspec/receive_never.rb +4 -5
  75. data/lib/rubocop/cop/rspec/repeated_description.rb +25 -26
  76. data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
  77. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +28 -29
  78. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +28 -29
  79. data/lib/rubocop/cop/rspec/repeated_include_example.rb +32 -33
  80. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  81. data/lib/rubocop/cop/rspec/scattered_let.rb +1 -5
  82. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  83. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  84. data/lib/rubocop/cop/rspec/stubbed_mock.rb +0 -1
  85. data/lib/rubocop/cop/rspec/subject_declaration.rb +0 -1
  86. data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
  87. data/lib/rubocop/cop/rspec/unspecified_exception.rb +15 -15
  88. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -0
  89. data/lib/rubocop/cop/rspec/variable_name.rb +6 -7
  90. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -0
  91. data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
  92. data/lib/rubocop/cop/rspec/yield.rb +2 -1
  93. data/lib/rubocop/cop/rspec_cops.rb +3 -0
  94. data/lib/rubocop/rspec/config_formatter.rb +14 -3
  95. data/lib/rubocop/rspec/inject.rb +1 -3
  96. data/lib/rubocop/rspec/language/node_pattern.rb +4 -0
  97. data/lib/rubocop/rspec/language.rb +6 -1
  98. data/lib/rubocop/rspec/version.rb +1 -1
  99. data/lib/rubocop/rspec/wording.rb +2 -2
  100. data/lib/rubocop/rspec.rb +14 -0
  101. data/lib/rubocop-rspec.rb +3 -0
  102. metadata +10 -88
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for `let` definitions that come after an example.
7
7
  #
8
8
  # @example
9
- # # Bad
9
+ # # bad
10
10
  # let(:foo) { bar }
11
11
  #
12
12
  # it 'checks what foo does' do
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # expect(some).to be
20
20
  # end
21
21
  #
22
- # # Good
22
+ # # good
23
23
  # let(:foo) { bar }
24
24
  # let(:some) { other }
25
25
  #
@@ -43,7 +43,7 @@ module RuboCop
43
43
  }
44
44
  PATTERN
45
45
 
46
- def on_block(node)
46
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
47
  return unless example_group_with_body?(node)
48
48
 
49
49
  check_let_declarations(node.body) if multiline_block?(node.body)
@@ -59,12 +59,11 @@ module RuboCop
59
59
  first_example = find_first_example(node)
60
60
  return unless first_example
61
61
 
62
- node.each_child_node do |child|
63
- next if child.sibling_index < first_example.sibling_index
64
- next unless let?(child)
62
+ first_example.right_siblings.each do |sibling|
63
+ next unless let?(sibling)
65
64
 
66
- add_offense(child) do |corrector|
67
- autocorrect(corrector, child, first_example)
65
+ add_offense(sibling) do |corrector|
66
+ autocorrect(corrector, sibling, first_example)
68
67
  end
69
68
  end
70
69
  end
@@ -6,20 +6,20 @@ module RuboCop
6
6
  # Checks unreferenced `let!` calls being used for test setup.
7
7
  #
8
8
  # @example
9
- # # Bad
9
+ # # bad
10
10
  # let!(:my_widget) { create(:widget) }
11
11
  #
12
12
  # it 'counts widgets' do
13
13
  # expect(Widget.count).to eq(1)
14
14
  # end
15
15
  #
16
- # # Good
16
+ # # good
17
17
  # it 'counts widgets' do
18
18
  # create(:widget)
19
19
  # expect(Widget.count).to eq(1)
20
20
  # end
21
21
  #
22
- # # Good
22
+ # # good
23
23
  # before { create(:widget) }
24
24
  #
25
25
  # it 'counts widgets' do
@@ -49,7 +49,7 @@ module RuboCop
49
49
  # @!method method_called?(node)
50
50
  def_node_search :method_called?, '(send nil? %)'
51
51
 
52
- def on_block(node)
52
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
53
53
  return unless example_or_shared_group_or_including?(node)
54
54
 
55
55
  unused_let_bang(node) do |let|
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # # bad
10
10
  # allow(foo).to receive_message_chain(:bar, :baz).and_return(42)
11
11
  #
12
- # # better
12
+ # # good
13
13
  # thing = Thing.new(baz: 42)
14
14
  # allow(foo).to receive(:bar).and_return(thing)
15
15
  #
@@ -19,10 +19,11 @@ module RuboCop
19
19
  #
20
20
  # describe "A feature example" do
21
21
  # end
22
+ #
22
23
  class MissingExampleGroupArgument < Base
23
24
  MSG = 'The first argument to `%<method>s` should not be empty.'
24
25
 
25
- def on_block(node)
26
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
26
27
  return unless example_group?(node)
27
28
  return if node.send_node.arguments?
28
29
 
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps parsing css selector.
7
+ module CssSelector
8
+ COMMON_OPTIONS = %w[
9
+ above below left_of right_of near count minimum maximum between text
10
+ id class style visible obscured exact exact_text normalize_ws match
11
+ wait filter_set focused
12
+ ].freeze
13
+
14
+ module_function
15
+
16
+ # @param selector [String]
17
+ # @return [Boolean]
18
+ # @example
19
+ # id?('#some-id') # => true
20
+ # id?('.some-class') # => false
21
+ def id?(selector)
22
+ selector.start_with?('#')
23
+ end
24
+
25
+ # @param selector [String]
26
+ # @return [Boolean]
27
+ # @example
28
+ # attribute?('[attribute]') # => true
29
+ # attribute?('attribute') # => false
30
+ def attribute?(selector)
31
+ selector.start_with?('[')
32
+ end
33
+
34
+ # @param selector [String]
35
+ # @return [Array<String>]
36
+ # @example
37
+ # attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>true}
38
+ # attributes('button[foo][bar]') # => {"foo"=>true, "bar"=>true}
39
+ # attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
40
+ def attributes(selector)
41
+ selector.scan(/\[(.*?)\]/).flatten.to_h do |attr|
42
+ key, value = attr.split('=')
43
+ [key, normalize_value(value)]
44
+ end
45
+ end
46
+
47
+ # @param selector [String]
48
+ # @return [Boolean]
49
+ # @example
50
+ # common_attributes?('a[focused]') # => true
51
+ # common_attributes?('button[focused][visible]') # => true
52
+ # common_attributes?('table[id=some-id]') # => true
53
+ # common_attributes?('h1[invalid]') # => false
54
+ def common_attributes?(selector)
55
+ attributes(selector).keys.difference(COMMON_OPTIONS).none?
56
+ end
57
+
58
+ # @param selector [String]
59
+ # @return [Array<String>]
60
+ # @example
61
+ # pseudo_classes('button:not([disabled])') # => ['not()']
62
+ # pseudo_classes('a:enabled:not([valid])') # => ['enabled', 'not()']
63
+ def pseudo_classes(selector)
64
+ # Attributes must be excluded or else the colon in the `href`s URL
65
+ # will also be picked up as pseudo classes.
66
+ # "a:not([href='http://example.com']):enabled" => "a:not():enabled"
67
+ ignored_attribute = selector.gsub(/\[.*?\]/, '')
68
+ # "a:not():enabled" => ["not()", "enabled"]
69
+ ignored_attribute.scan(/:([^:]*)/).flatten
70
+ end
71
+
72
+ # @param selector [String]
73
+ # @return [Boolean]
74
+ # @example
75
+ # multiple_selectors?('a.cls b#id') # => true
76
+ # multiple_selectors?('a.cls') # => false
77
+ def multiple_selectors?(selector)
78
+ selector.match?(/[ >,+]/)
79
+ end
80
+
81
+ # @param value [String]
82
+ # @return [Boolean, String]
83
+ # @example
84
+ # normalize_value('true') # => true
85
+ # normalize_value('false') # => false
86
+ # normalize_value(nil) # => false
87
+ # normalize_value("foo") # => "'foo'"
88
+ def normalize_value(value)
89
+ case value
90
+ when 'true' then true
91
+ when 'false' then false
92
+ when nil then true
93
+ else "'#{value}'"
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps to find namespace of the node.
7
+ module Namespace
8
+ private
9
+
10
+ # @param node [RuboCop::AST::Node]
11
+ # @return [Array<String>]
12
+ # @example
13
+ # namespace(node) # => ['A', 'B', 'C']
14
+ def namespace(node)
15
+ node
16
+ .each_ancestor(:class, :module)
17
+ .reverse_each
18
+ .flat_map { |ancestor| ancestor.defined_module_name.split('::') }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -22,6 +22,7 @@ module RuboCop
22
22
  # describe '.do_something_else' do
23
23
  # end
24
24
  # end
25
+ #
25
26
  class MultipleDescribes < Base
26
27
  include TopLevelGroup
27
28
 
@@ -11,7 +11,6 @@ module RuboCop
11
11
  # and works with `--auto-gen-config`.
12
12
  #
13
13
  # @example
14
- #
15
14
  # # bad
16
15
  # describe UserCreator do
17
16
  # it 'builds a user' do
@@ -32,7 +31,6 @@ module RuboCop
32
31
  # end
33
32
  #
34
33
  # @example `aggregate_failures: true` (default)
35
- #
36
34
  # # good - the cop ignores when RSpec aggregates failures
37
35
  # describe UserCreator do
38
36
  # it 'builds a user', :aggregate_failures do
@@ -42,7 +40,6 @@ module RuboCop
42
40
  # end
43
41
  #
44
42
  # @example `aggregate_failures: false`
45
- #
46
43
  # # Detected as an offense
47
44
  # describe UserCreator do
48
45
  # it 'builds a user', aggregate_failures: false do
@@ -52,7 +49,6 @@ module RuboCop
52
49
  # end
53
50
  #
54
51
  # @example configuration
55
- #
56
52
  # # .rubocop.yml
57
53
  # # RSpec/MultipleExpectations:
58
54
  # # Max: 2
@@ -88,7 +84,7 @@ module RuboCop
88
84
  (block (send nil? :aggregate_failures ...) ...)
89
85
  PATTERN
90
86
 
91
- def on_block(node)
87
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
92
88
  return unless example?(node)
93
89
 
94
90
  return if example_with_aggregate_failures?(node)
@@ -56,7 +56,6 @@ module RuboCop
56
56
  # end
57
57
  #
58
58
  # @example when disabling AllowSubject configuration
59
- #
60
59
  # # rubocop.yml
61
60
  # # RSpec/MultipleMemoizedHelpers:
62
61
  # # AllowSubject: false
@@ -72,7 +71,6 @@ module RuboCop
72
71
  # end
73
72
  #
74
73
  # @example with Max configuration
75
- #
76
74
  # # rubocop.yml
77
75
  # # RSpec/MultipleMemoizedHelpers:
78
76
  # # Max: 1
@@ -89,7 +87,7 @@ module RuboCop
89
87
 
90
88
  MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
91
89
 
92
- def on_block(node)
90
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
93
91
  return unless spec_group?(node)
94
92
 
95
93
  count = all_helpers(node).uniq.count
@@ -6,7 +6,6 @@ module RuboCop
6
6
  # Checks if an example group defines `subject` multiple times.
7
7
  #
8
8
  # @example
9
- #
10
9
  # # bad
11
10
  # describe Foo do
12
11
  # subject(:user) { User.new }
@@ -19,6 +18,21 @@ module RuboCop
19
18
  # subject(:post) { Post.new }
20
19
  # end
21
20
  #
21
+ # # bad (does not support autocorrection)
22
+ # describe Foo do
23
+ # subject!(:user) { User.new }
24
+ # subject!(:post) { Post.new }
25
+ # end
26
+ #
27
+ # # good
28
+ # describe Foo do
29
+ # before do
30
+ # User.new
31
+ # Post.new
32
+ # end
33
+ # end
34
+ #
35
+ # This cop does not support autocorrection in some cases.
22
36
  # The autocorrect behavior for this cop depends on the type of
23
37
  # duplication:
24
38
  #
@@ -33,13 +47,14 @@ module RuboCop
33
47
  # - If subjects are defined with `subject!` then we don't autocorrect.
34
48
  # This is enough of an edge case that people can just move this to
35
49
  # a `before` hook on their own
50
+ #
36
51
  class MultipleSubjects < Base
37
52
  extend AutoCorrector
38
53
  include RangeHelp
39
54
 
40
55
  MSG = 'Do not set more than one subject per example group'
41
56
 
42
- def on_block(node)
57
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
43
58
  return unless example_group?(node)
44
59
 
45
60
  subjects = RuboCop::RSpec::ExampleGroup.new(node).subjects
@@ -41,6 +41,7 @@ module RuboCop
41
41
  #
42
42
  # it { is_expected.to be_valid }
43
43
  # end
44
+ #
44
45
  class NamedSubject < Base
45
46
  MSG = 'Name your test subject if you need to reference it explicitly.'
46
47
 
@@ -55,7 +56,7 @@ module RuboCop
55
56
  # @!method subject_usage(node)
56
57
  def_node_search :subject_usage, '$(send nil? :subject)'
57
58
 
58
- def on_block(node)
59
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
59
60
  if !example_or_hook_block?(node) || ignored_shared_example?(node)
60
61
  return
61
62
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  # end
37
37
  # end
38
38
  #
39
- # # better
39
+ # # good
40
40
  # context 'using some feature as an admin' do
41
41
  # let(:some) { :various }
42
42
  # let(:feature) { :setup }
@@ -53,34 +53,40 @@ module RuboCop
53
53
  # it 'yada yada'
54
54
  # end
55
55
  #
56
- # @example configuration
57
- #
58
- # # .rubocop.yml
59
- # # RSpec/NestedGroups:
60
- # # Max: 2
61
- #
62
- # context 'when using some feature' do
63
- # let(:some) { :various }
64
- # let(:feature) { :setup }
65
- #
66
- # context 'when user is signed in' do
67
- # let(:user) do
68
- # UserCreate.call(user_attributes)
56
+ # @example `Max: 3` (default)
57
+ # # bad
58
+ # describe Foo do
59
+ # context 'foo' do
60
+ # context 'bar' do
61
+ # context 'baz' do # flagged by rubocop
62
+ # end
69
63
  # end
64
+ # end
65
+ # end
70
66
  #
71
- # let(:user_attributes) do
72
- # {
73
- # name: 'John',
74
- # age: 22,
75
- # role: role
76
- # }
67
+ # @example `Max: 2`
68
+ # # bad
69
+ # describe Foo do
70
+ # context 'foo' do
71
+ # context 'bar' do # flagged by rubocop
72
+ # context 'baz' do # flagged by rubocop
73
+ # end
77
74
  # end
75
+ # end
76
+ # end
78
77
  #
79
- # context 'when user is an admin' do # flagged by rubocop
80
- # let(:role) { 'admin' }
78
+ # @example `AllowedGroups: [] (default)`
79
+ # describe Foo do # <-- nested groups 1
80
+ # context 'foo' do # <-- nested groups 2
81
+ # context 'bar' do # <-- nested groups 3
82
+ # end
83
+ # end
84
+ # end
81
85
  #
82
- # it 'blah blah'
83
- # it 'yada yada'
86
+ # @example `AllowedGroups: [path]`
87
+ # describe Foo do # <-- nested groups 1
88
+ # path '/foo' do # <-- nested groups 1 (not counted)
89
+ # context 'bar' do # <-- nested groups 2
84
90
  # end
85
91
  # end
86
92
  # end
@@ -113,13 +119,23 @@ module RuboCop
113
119
  example_group = example_group?(node)
114
120
  yield node, nesting if example_group && nesting > max_nesting
115
121
 
116
- next_nesting = example_group ? nesting + 1 : nesting
122
+ next_nesting = if count_up_nesting?(node, example_group)
123
+ nesting + 1
124
+ else
125
+ nesting
126
+ end
117
127
 
118
128
  node.each_child_node(:block, :begin) do |child|
119
129
  find_nested_example_groups(child, nesting: next_nesting, &block)
120
130
  end
121
131
  end
122
132
 
133
+ def count_up_nesting?(node, example_group)
134
+ example_group &&
135
+ (node.block_type? &&
136
+ !allowed_groups.include?(node.method_name))
137
+ end
138
+
123
139
  def message(nesting)
124
140
  format(MSG, total: nesting, max: max_nesting)
125
141
  end
@@ -136,6 +152,10 @@ module RuboCop
136
152
  cop_config.fetch('Max', 3)
137
153
  end
138
154
  end
155
+
156
+ def allowed_groups
157
+ @allowed_groups ||= cop_config.fetch('AllowedGroups', [])
158
+ end
139
159
  end
140
160
  end
141
161
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks if an example contains any expectation.
7
+ #
8
+ # All RSpec's example and expectation methods are covered by default.
9
+ # If you are using your own custom methods,
10
+ # add the following configuration:
11
+ #
12
+ # RSpec:
13
+ # Language:
14
+ # Examples:
15
+ # Regular:
16
+ # - custom_it
17
+ # Expectations:
18
+ # - custom_expect
19
+ #
20
+ # @example
21
+ # # bad
22
+ # it do
23
+ # a?
24
+ # end
25
+ #
26
+ # # good
27
+ # it do
28
+ # expect(a?).to be(true)
29
+ # end
30
+ #
31
+ class NoExpectationExample < Base
32
+ MSG = 'No expectation found in this example.'
33
+
34
+ # @!method regular_or_focused_example?(node)
35
+ # @param [RuboCop::AST::Node] node
36
+ # @return [Boolean]
37
+ def_node_matcher :regular_or_focused_example?, <<~PATTERN
38
+ {
39
+ #{block_pattern('{#Examples.regular | #Examples.focused}')}
40
+ #{numblock_pattern('{#Examples.regular | #Examples.focused}')}
41
+ }
42
+ PATTERN
43
+
44
+ # @!method including_any_expectation?(node)
45
+ # @param [RuboCop::AST::Node] node
46
+ # @return [Boolean]
47
+ def_node_search(
48
+ :including_any_expectation?,
49
+ send_pattern('#Expectations.all')
50
+ )
51
+
52
+ # @param [RuboCop::AST::BlockNode] node
53
+ def on_block(node)
54
+ return unless regular_or_focused_example?(node)
55
+ return if including_any_expectation?(node)
56
+
57
+ add_offense(node)
58
+ end
59
+
60
+ alias on_numblock on_block
61
+ end
62
+ end
63
+ end
64
+ end
@@ -6,7 +6,6 @@ module RuboCop
6
6
  # Checks for consistent method usage for negating expectations.
7
7
  #
8
8
  # @example `EnforcedStyle: not_to` (default)
9
- #
10
9
  # # bad
11
10
  # it '...' do
12
11
  # expect(false).to_not be_true
@@ -18,7 +17,6 @@ module RuboCop
18
17
  # end
19
18
  #
20
19
  # @example `EnforcedStyle: to_not`
21
- #
22
20
  # # bad
23
21
  # it '...' do
24
22
  # expect(false).not_to be_true
@@ -28,6 +26,7 @@ module RuboCop
28
26
  # it '...' do
29
27
  # expect(false).to_not be_true
30
28
  # end
29
+ #
31
30
  class NotToNot < Base
32
31
  extend AutoCorrector
33
32
  include ConfigurableEnforcedStyle
@@ -21,6 +21,7 @@ module RuboCop
21
21
  # let(:foo) { bar }
22
22
  # let(:baz) { baz }
23
23
  # let!(:other) { other }
24
+ #
24
25
  class OverwritingSetup < Base
25
26
  MSG = '`%<name>s` is already defined.'
26
27
 
@@ -30,7 +31,7 @@ module RuboCop
30
31
  # @!method first_argument_name(node)
31
32
  def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
32
33
 
33
- def on_block(node)
34
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
34
35
  return unless example_group_with_body?(node)
35
36
 
36
37
  find_duplicates(node.body) do |duplicate, name|
@@ -31,6 +31,7 @@ module RuboCop
31
31
  # # good
32
32
  # describe MyClass do
33
33
  # end
34
+ #
34
35
  class Pending < Base
35
36
  MSG = 'Pending spec found.'
36
37
 
@@ -276,6 +276,7 @@ module RuboCop
276
276
  #
277
277
  # # good - the above code is rewritten to it by this cop
278
278
  # expect(foo.something?).to be_truthy
279
+ #
279
280
  class PredicateMatcher < Base
280
281
  extend AutoCorrector
281
282
  include ConfigurableEnforcedStyle
@@ -291,7 +292,7 @@ module RuboCop
291
292
  end
292
293
  end
293
294
 
294
- def on_block(node)
295
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
295
296
  check_explicit(node) if style == :explicit
296
297
  end
297
298
 
@@ -7,7 +7,6 @@ module RuboCop
7
7
  # Checks that tests use RSpec `before` hook over Rails `setup` method.
8
8
  #
9
9
  # @example
10
- #
11
10
  # # bad
12
11
  # setup do
13
12
  # allow(foo).to receive(:bar)
@@ -30,7 +29,7 @@ module RuboCop
30
29
  (args) _)
31
30
  PATTERN
32
31
 
33
- def on_block(node)
32
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
34
33
  setup_call(node) do |setup|
35
34
  add_offense(node) do |corrector|
36
35
  corrector.replace setup, 'before'
@@ -6,22 +6,21 @@ module RuboCop
6
6
  # Check for `once` and `twice` receive counts matchers usage.
7
7
  #
8
8
  # @example
9
+ # # bad
10
+ # expect(foo).to receive(:bar).exactly(1).times
11
+ # expect(foo).to receive(:bar).exactly(2).times
12
+ # expect(foo).to receive(:bar).at_least(1).times
13
+ # expect(foo).to receive(:bar).at_least(2).times
14
+ # expect(foo).to receive(:bar).at_most(1).times
15
+ # expect(foo).to receive(:bar).at_most(2).times
9
16
  #
10
- # # bad
11
- # expect(foo).to receive(:bar).exactly(1).times
12
- # expect(foo).to receive(:bar).exactly(2).times
13
- # expect(foo).to receive(:bar).at_least(1).times
14
- # expect(foo).to receive(:bar).at_least(2).times
15
- # expect(foo).to receive(:bar).at_most(1).times
16
- # expect(foo).to receive(:bar).at_most(2).times
17
- #
18
- # # good
19
- # expect(foo).to receive(:bar).once
20
- # expect(foo).to receive(:bar).twice
21
- # expect(foo).to receive(:bar).at_least(:once)
22
- # expect(foo).to receive(:bar).at_least(:twice)
23
- # expect(foo).to receive(:bar).at_most(:once)
24
- # expect(foo).to receive(:bar).at_most(:twice).times
17
+ # # good
18
+ # expect(foo).to receive(:bar).once
19
+ # expect(foo).to receive(:bar).twice
20
+ # expect(foo).to receive(:bar).at_least(:once)
21
+ # expect(foo).to receive(:bar).at_least(:twice)
22
+ # expect(foo).to receive(:bar).at_most(:once)
23
+ # expect(foo).to receive(:bar).at_most(:twice).times
25
24
  #
26
25
  class ReceiveCounts < Base
27
26
  extend AutoCorrector
@@ -6,12 +6,11 @@ module RuboCop
6
6
  # Prefer `not_to receive(...)` over `receive(...).never`.
7
7
  #
8
8
  # @example
9
+ # # bad
10
+ # expect(foo).to receive(:bar).never
9
11
  #
10
- # # bad
11
- # expect(foo).to receive(:bar).never
12
- #
13
- # # good
14
- # expect(foo).not_to receive(:bar)
12
+ # # good
13
+ # expect(foo).not_to receive(:bar)
15
14
  #
16
15
  class ReceiveNever < Base
17
16
  extend AutoCorrector