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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +110 -86
- data/config/default.yml +44 -6
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +8 -9
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +8 -9
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -0
- data/lib/rubocop/cop/rspec/around_block.rb +26 -3
- data/lib/rubocop/cop/rspec/be.rb +0 -1
- data/lib/rubocop/cop/rspec/be_eq.rb +0 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +0 -1
- data/lib/rubocop/cop/rspec/before_after_all.rb +1 -0
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +9 -3
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -1
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +86 -0
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +91 -10
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -1
- data/lib/rubocop/cop/rspec/change_by_zero.rb +60 -5
- data/lib/rubocop/cop/rspec/class_check.rb +101 -0
- data/lib/rubocop/cop/rspec/context_method.rb +2 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +49 -18
- data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
- data/lib/rubocop/cop/rspec/describe_method.rb +1 -0
- data/lib/rubocop/cop/rspec/described_class.rb +4 -14
- data/lib/rubocop/cop/rspec/dialect.rb +1 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +19 -4
- data/lib/rubocop/cop/rspec/empty_hook.rb +2 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +4 -9
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +2 -1
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +32 -2
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -1
- data/lib/rubocop/cop/rspec/example_length.rb +2 -1
- data/lib/rubocop/cop/rspec/example_without_description.rb +2 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +2 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -0
- data/lib/rubocop/cop/rspec/expect_actual.rb +3 -0
- data/lib/rubocop/cop/rspec/expect_change.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +4 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -0
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +2 -1
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +26 -12
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -0
- data/lib/rubocop/cop/rspec/file_path.rb +6 -3
- data/lib/rubocop/cop/rspec/focus.rb +18 -0
- data/lib/rubocop/cop/rspec/hook_argument.rb +7 -2
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +10 -9
- data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +0 -1
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -0
- data/lib/rubocop/cop/rspec/implicit_expect.rb +0 -2
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_variable.rb +0 -1
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +16 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +15 -15
- data/lib/rubocop/cop/rspec/let_before_examples.rb +7 -8
- data/lib/rubocop/cop/rspec/let_setup.rb +4 -4
- data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +2 -1
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +99 -0
- data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -5
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -3
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -2
- data/lib/rubocop/cop/rspec/named_subject.rb +2 -1
- data/lib/rubocop/cop/rspec/nested_groups.rb +45 -25
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +64 -0
- data/lib/rubocop/cop/rspec/not_to_not.rb +1 -2
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -0
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -1
- data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -2
- data/lib/rubocop/cop/rspec/receive_counts.rb +14 -15
- data/lib/rubocop/cop/rspec/receive_never.rb +4 -5
- data/lib/rubocop/cop/rspec/repeated_description.rb +25 -26
- data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +28 -29
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +28 -29
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +32 -33
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +1 -5
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +0 -1
- data/lib/rubocop/cop/rspec/subject_declaration.rb +0 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +15 -15
- data/lib/rubocop/cop/rspec/variable_definition.rb +1 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +6 -7
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -0
- data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
- data/lib/rubocop/cop/rspec/yield.rb +2 -1
- data/lib/rubocop/cop/rspec_cops.rb +3 -0
- data/lib/rubocop/rspec/config_formatter.rb +14 -3
- data/lib/rubocop/rspec/inject.rb +1 -3
- data/lib/rubocop/rspec/language/node_pattern.rb +4 -0
- data/lib/rubocop/rspec/language.rb +6 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop/rspec/wording.rb +2 -2
- data/lib/rubocop/rspec.rb +14 -0
- data/lib/rubocop-rspec.rb +3 -0
- 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
|
-
# #
|
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
|
-
# #
|
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
|
-
|
63
|
-
next
|
64
|
-
next unless let?(child)
|
62
|
+
first_example.right_siblings.each do |sibling|
|
63
|
+
next unless let?(sibling)
|
65
64
|
|
66
|
-
add_offense(
|
67
|
-
autocorrect(corrector,
|
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
|
-
# #
|
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
|
-
# #
|
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
|
-
# #
|
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|
|
@@ -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
|
@@ -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
|
-
# #
|
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
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
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
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
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
|
-
#
|
80
|
-
#
|
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
|
-
#
|
83
|
-
#
|
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 =
|
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|
|
@@ -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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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
|
-
#
|
11
|
-
#
|
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
|