rubocop-rspec 3.9.0 → 3.10.2
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 +26 -0
- data/config/default.yml +18 -1
- data/lib/rubocop/cop/rspec/around_block.rb +22 -0
- data/lib/rubocop/cop/rspec/contain_exactly.rb +8 -22
- data/lib/rubocop/cop/rspec/context_method.rb +1 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/described_class.rb +1 -1
- data/lib/rubocop/cop/rspec/discarded_matcher.rb +113 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +3 -2
- data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +7 -2
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -0
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/example_length.rb +1 -1
- data/lib/rubocop/cop/rspec/example_without_description.rb +1 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_actual.rb +33 -13
- data/lib/rubocop/cop/rspec/expect_change.rb +46 -16
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -0
- data/lib/rubocop/cop/rspec/expect_in_let.rb +1 -1
- data/lib/rubocop/cop/rspec/hook_argument.rb +1 -0
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -0
- data/lib/rubocop/cop/rspec/indexed_let.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +13 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/match_with_simple_regex.rb +92 -0
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
- data/lib/rubocop/cop/rspec/mixin/inside_example.rb +16 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +1 -0
- data/lib/rubocop/cop/rspec/mixin/repeated_items.rb +36 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +1 -0
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/redundant_around.rb +1 -0
- data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +7 -5
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +6 -10
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +6 -10
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +8 -11
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +11 -5
- data/lib/rubocop/cop/rspec/scattered_setup.rb +7 -9
- data/lib/rubocop/cop/rspec/shared_context.rb +47 -7
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +3 -6
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +20 -12
- data/lib/rubocop/cop/rspec/subject_declaration.rb +17 -1
- data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +3 -5
- data/lib/rubocop/cop/rspec/yield.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +2 -0
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +2 -0
- metadata +28 -4
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
}
|
|
60
60
|
PATTERN
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless spec_group?(node)
|
|
64
64
|
|
|
65
65
|
children = node.body&.child_nodes
|
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
|
42
42
|
...)
|
|
43
43
|
PATTERN
|
|
44
44
|
|
|
45
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
45
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
46
46
|
return unless example?(node)
|
|
47
47
|
|
|
48
48
|
null_double(node) do |var, receiver|
|
|
@@ -38,6 +38,13 @@ module RuboCop
|
|
|
38
38
|
)
|
|
39
39
|
PATTERN
|
|
40
40
|
|
|
41
|
+
# @!method each_itblock?(node)
|
|
42
|
+
def_node_matcher :each_itblock?, <<~PATTERN
|
|
43
|
+
(itblock
|
|
44
|
+
(send ... :each) _ (...)
|
|
45
|
+
)
|
|
46
|
+
PATTERN
|
|
47
|
+
|
|
41
48
|
# @!method expectation?(node)
|
|
42
49
|
def_node_matcher :expectation?, <<~PATTERN
|
|
43
50
|
(send (send nil? :expect (lvar %)) :to ...)
|
|
@@ -55,6 +62,12 @@ module RuboCop
|
|
|
55
62
|
end
|
|
56
63
|
end
|
|
57
64
|
|
|
65
|
+
def on_itblock(node)
|
|
66
|
+
each_itblock?(node) do
|
|
67
|
+
check_offense(node, :it)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
private
|
|
59
72
|
|
|
60
73
|
def check_offense(node, argument)
|
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
|
37
37
|
|
|
38
38
|
MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
|
|
39
39
|
|
|
40
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
40
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
41
41
|
return unless subject?(node)
|
|
42
42
|
return unless inside_example_group?(node)
|
|
43
43
|
|
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
|
55
55
|
[RSpec::ScatteredLet]
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
58
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
59
59
|
return unless example_group_with_body?(node)
|
|
60
60
|
|
|
61
61
|
check_let_declarations(node.body) if multiline_block?(node.body)
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
# @!method method_called?(node)
|
|
60
60
|
def_node_search :method_called?, '(send nil? %)'
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless example_or_shared_group_or_including?(node)
|
|
64
64
|
|
|
65
65
|
unused_let_bang(node) do |let|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'regexp_parser'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
module RSpec
|
|
8
|
+
# Enforces the use of `include` matcher instead of `match` when the
|
|
9
|
+
# matcher is a simple string literal without regex-specific features.
|
|
10
|
+
#
|
|
11
|
+
# When `match` is used with a regex that contains only literal characters
|
|
12
|
+
# (no anchors, character classes, quantifiers, alternations, or
|
|
13
|
+
# metacharacters), it's clearer to use the `include` matcher instead.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# expect('foobar').to match(/foo/)
|
|
18
|
+
# expect(response.body).to match(/http:\/\/example\.com/)
|
|
19
|
+
#
|
|
20
|
+
# # good
|
|
21
|
+
# expect('foobar').to include('foo')
|
|
22
|
+
# expect(response.body).to include('http://example.com')
|
|
23
|
+
#
|
|
24
|
+
# # good - regex features needed
|
|
25
|
+
# expect('foobar').to match(/^foo/) # anchor
|
|
26
|
+
# expect('foobar').to match(/foo\d+/) # quantifier
|
|
27
|
+
# expect('foobar').to match(/foo[ob]/) # character class
|
|
28
|
+
#
|
|
29
|
+
class MatchWithSimpleRegex < Base
|
|
30
|
+
extend AutoCorrector
|
|
31
|
+
|
|
32
|
+
MSG = 'Prefer using `include(%<string>s)` when the regex is a simple ' \
|
|
33
|
+
'string literal.'
|
|
34
|
+
RESTRICT_ON_SEND = %i[match].freeze
|
|
35
|
+
|
|
36
|
+
# @!method match_with_regexp?(node)
|
|
37
|
+
def_node_matcher :match_with_regexp?, <<~PATTERN
|
|
38
|
+
(send nil? :match $regexp)
|
|
39
|
+
PATTERN
|
|
40
|
+
|
|
41
|
+
def on_send(node)
|
|
42
|
+
match_with_regexp?(node) do |regexp|
|
|
43
|
+
next unless simple_regexp?(regexp)
|
|
44
|
+
|
|
45
|
+
string_literal = regexp_to_string(regexp)
|
|
46
|
+
message = format(MSG, string: string_literal)
|
|
47
|
+
|
|
48
|
+
add_offense(node, message: message) do |corrector|
|
|
49
|
+
corrector.replace(node, "include(#{string_literal})")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def simple_regexp?(node)
|
|
57
|
+
return false if node.interpolation?
|
|
58
|
+
return false if node.regopt.children.any?
|
|
59
|
+
|
|
60
|
+
parsed = Regexp::Parser.parse(node.content)
|
|
61
|
+
parsed.expressions.all? { |expr| simple_expression?(expr) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def simple_expression?(expr)
|
|
65
|
+
return false if expr.quantified?
|
|
66
|
+
return true if expr.is_a?(Regexp::Expression::Literal)
|
|
67
|
+
return true if expr.is_a?(Regexp::Expression::EscapeSequence::Literal)
|
|
68
|
+
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Reconstruct the literal string that the regex matches
|
|
73
|
+
def regexp_to_string(node)
|
|
74
|
+
parsed = Regexp::Parser.parse(node.content)
|
|
75
|
+
string_content = parsed.expressions.map do |expr|
|
|
76
|
+
expr.respond_to?(:char) ? expr.char : expr.text
|
|
77
|
+
end.join
|
|
78
|
+
|
|
79
|
+
to_string_literal(string_content)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def to_string_literal(string)
|
|
83
|
+
if string.include?("'")
|
|
84
|
+
%("#{string.gsub('"', '\"')}")
|
|
85
|
+
else
|
|
86
|
+
"'#{string}'"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
|
23
23
|
class MissingExampleGroupArgument < Base
|
|
24
24
|
MSG = 'The first argument to `%<method>s` should not be empty.'
|
|
25
25
|
|
|
26
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
26
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
27
27
|
return unless example_group?(node)
|
|
28
28
|
return if node.send_node.arguments?
|
|
29
29
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Helps check if a given node is within an example block.
|
|
7
|
+
module InsideExample
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def inside_example?(node)
|
|
11
|
+
node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Helps find repeated items in a collection
|
|
7
|
+
#
|
|
8
|
+
# Provides a generic method to find repeated items by grouping them
|
|
9
|
+
# by a key and returning pairs of [item, repeated_lines] for items
|
|
10
|
+
# that appear more than once.
|
|
11
|
+
module RepeatedItems
|
|
12
|
+
# Groups items by key and returns only groups with more than one item
|
|
13
|
+
#
|
|
14
|
+
# @param items [Enumerable] the filtered collection to group
|
|
15
|
+
# @param key_proc [Proc] block returning the grouping key for each item
|
|
16
|
+
# @return [Array<Array>] array of groups containing more than one item
|
|
17
|
+
# that share the same key and there are multiple items in the group
|
|
18
|
+
def find_repeated_groups(items, key_proc:)
|
|
19
|
+
items
|
|
20
|
+
.group_by(&key_proc)
|
|
21
|
+
.values
|
|
22
|
+
.reject(&:one?)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Maps a group of items to pairs of [item, repeated_lines]
|
|
26
|
+
#
|
|
27
|
+
# @param items [Array] array of items that share the same key
|
|
28
|
+
# @return [Array<Array>] array of [item, repeated_lines] pairs
|
|
29
|
+
def add_repeated_lines(items)
|
|
30
|
+
repeated_lines = items.map(&:first_line)
|
|
31
|
+
items.map { |item| [item, repeated_lines - [item.first_line]] }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -90,7 +90,7 @@ module RuboCop
|
|
|
90
90
|
(block (send nil? :aggregate_failures ...) ...)
|
|
91
91
|
PATTERN
|
|
92
92
|
|
|
93
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
93
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
94
94
|
return unless example?(node)
|
|
95
95
|
|
|
96
96
|
return if example_with_aggregate_failures?(node)
|
|
@@ -88,7 +88,7 @@ module RuboCop
|
|
|
88
88
|
|
|
89
89
|
exclude_limit 'Max'
|
|
90
90
|
|
|
91
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
91
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
92
92
|
return unless spec_group?(node)
|
|
93
93
|
|
|
94
94
|
count = all_helpers(node).uniq.count
|
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
|
54
54
|
|
|
55
55
|
MSG = 'Do not set more than one subject per example group'
|
|
56
56
|
|
|
57
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
57
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
58
58
|
return unless example_group?(node)
|
|
59
59
|
|
|
60
60
|
subjects = RuboCop::RSpec::ExampleGroup.new(node).subjects
|
|
@@ -94,7 +94,7 @@ module RuboCop
|
|
|
94
94
|
# @!method subject_usage(node)
|
|
95
95
|
def_node_search :subject_usage, '$(send nil? :subject)'
|
|
96
96
|
|
|
97
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
97
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
98
98
|
if !example_or_hook_block?(node) || ignored_shared_example?(node)
|
|
99
99
|
return
|
|
100
100
|
end
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
|
33
33
|
# @!method first_argument_name(node)
|
|
34
34
|
def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
|
|
35
35
|
|
|
36
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
36
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
37
37
|
return unless example_group_with_body?(node)
|
|
38
38
|
|
|
39
39
|
find_duplicates(node.body) do |duplicate, name|
|
|
@@ -340,7 +340,7 @@ module RuboCop
|
|
|
340
340
|
end
|
|
341
341
|
end
|
|
342
342
|
|
|
343
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
343
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
344
344
|
check_explicit(node) if style == :explicit
|
|
345
345
|
end
|
|
346
346
|
end
|
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
|
42
42
|
class RepeatedDescription < Base
|
|
43
43
|
MSG = "Don't repeat descriptions within an example group."
|
|
44
44
|
|
|
45
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
45
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
46
46
|
return unless example_group?(node)
|
|
47
47
|
|
|
48
48
|
repeated_descriptions(node).each do |description|
|
|
@@ -16,10 +16,12 @@ module RuboCop
|
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
18
|
class RepeatedExample < Base
|
|
19
|
+
include RepeatedItems
|
|
20
|
+
|
|
19
21
|
MSG = "Don't repeat examples within an example group. " \
|
|
20
22
|
'Repeated on line(s) %<lines>s.'
|
|
21
23
|
|
|
22
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
24
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
23
25
|
return unless example_group?(node)
|
|
24
26
|
|
|
25
27
|
find_repeated_examples(node).each do |repeated_examples|
|
|
@@ -32,10 +34,10 @@ module RuboCop
|
|
|
32
34
|
def find_repeated_examples(node)
|
|
33
35
|
examples = RuboCop::RSpec::ExampleGroup.new(node).examples
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
find_repeated_groups(
|
|
38
|
+
examples,
|
|
39
|
+
key_proc: ->(example) { build_example_signature(example) }
|
|
40
|
+
)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
def build_example_signature(example)
|
|
@@ -44,6 +44,7 @@ module RuboCop
|
|
|
44
44
|
#
|
|
45
45
|
class RepeatedExampleGroupBody < Base
|
|
46
46
|
include SkipOrPending
|
|
47
|
+
include RepeatedItems
|
|
47
48
|
|
|
48
49
|
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
|
|
49
50
|
|
|
@@ -72,19 +73,14 @@ module RuboCop
|
|
|
72
73
|
private
|
|
73
74
|
|
|
74
75
|
def repeated_group_bodies(node)
|
|
75
|
-
node
|
|
76
|
-
.children
|
|
76
|
+
items = node.children
|
|
77
77
|
.select { |child| example_group_with_body?(child) }
|
|
78
78
|
.reject { |child| skip_or_pending_inside_block?(child) }
|
|
79
|
-
.group_by { |group| signature_keys(group) }
|
|
80
|
-
.values
|
|
81
|
-
.reject(&:one?)
|
|
82
|
-
.flat_map { |groups| add_repeated_lines(groups) }
|
|
83
|
-
end
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
find_repeated_groups(
|
|
81
|
+
items,
|
|
82
|
+
key_proc: ->(group) { signature_keys(group) }
|
|
83
|
+
).flat_map { |group| add_repeated_lines(group) }
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
def signature_keys(group)
|
|
@@ -44,6 +44,7 @@ module RuboCop
|
|
|
44
44
|
#
|
|
45
45
|
class RepeatedExampleGroupDescription < Base
|
|
46
46
|
include SkipOrPending
|
|
47
|
+
include RepeatedItems
|
|
47
48
|
|
|
48
49
|
MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
|
|
49
50
|
|
|
@@ -71,20 +72,15 @@ module RuboCop
|
|
|
71
72
|
private
|
|
72
73
|
|
|
73
74
|
def repeated_group_descriptions(node)
|
|
74
|
-
node
|
|
75
|
-
.children
|
|
75
|
+
items = node.children
|
|
76
76
|
.select { |child| example_group?(child) }
|
|
77
77
|
.reject { |child| skip_or_pending_inside_block?(child) }
|
|
78
78
|
.reject { |child| empty_description?(child) }
|
|
79
|
-
.group_by { |group| doc_string_and_metadata(group) }
|
|
80
|
-
.values
|
|
81
|
-
.reject(&:one?)
|
|
82
|
-
.flat_map { |groups| add_repeated_lines(groups) }
|
|
83
|
-
end
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
find_repeated_groups(
|
|
81
|
+
items,
|
|
82
|
+
key_proc: ->(group) { doc_string_and_metadata(group) }
|
|
83
|
+
).flat_map { |group| add_repeated_lines(group) }
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
def message(group, repeats)
|
|
@@ -46,6 +46,8 @@ module RuboCop
|
|
|
46
46
|
# end
|
|
47
47
|
#
|
|
48
48
|
class RepeatedIncludeExample < Base
|
|
49
|
+
include RepeatedItems
|
|
50
|
+
|
|
49
51
|
MSG = 'Repeated include of shared_examples %<name>s ' \
|
|
50
52
|
'on line(s) %<repeat>s'
|
|
51
53
|
|
|
@@ -73,13 +75,13 @@ module RuboCop
|
|
|
73
75
|
private
|
|
74
76
|
|
|
75
77
|
def repeated_include_examples(node)
|
|
76
|
-
node
|
|
77
|
-
.children
|
|
78
|
+
items = node.children
|
|
78
79
|
.select { |child| literal_include_examples?(child) }
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
|
|
81
|
+
find_repeated_groups(
|
|
82
|
+
items,
|
|
83
|
+
key_proc: ->(child) { signature_keys(child) }
|
|
84
|
+
).flat_map { |group| add_repeated_lines(group) }
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
def literal_include_examples?(node)
|
|
@@ -87,11 +89,6 @@ module RuboCop
|
|
|
87
89
|
node.arguments.all?(&:recursive_literal_or_const?)
|
|
88
90
|
end
|
|
89
91
|
|
|
90
|
-
def add_repeated_lines(items)
|
|
91
|
-
repeated_lines = items.map(&:first_line)
|
|
92
|
-
items.map { |item| [item, repeated_lines - [item.first_line]] }
|
|
93
|
-
end
|
|
94
|
-
|
|
95
92
|
def signature_keys(item)
|
|
96
93
|
item.arguments
|
|
97
94
|
end
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
check_and_return_call(node)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless style == :and_return
|
|
64
64
|
return unless stub_with_block?(node)
|
|
65
65
|
|
|
@@ -19,11 +19,11 @@ module RuboCop
|
|
|
19
19
|
#
|
|
20
20
|
# # good
|
|
21
21
|
# describe Foo do
|
|
22
|
-
# subject { Foo }
|
|
23
|
-
# before { prepare }
|
|
24
22
|
# let(:foo) { 1 }
|
|
25
23
|
# let(:bar) { 2 }
|
|
26
24
|
# let!(:baz) { 3 }
|
|
25
|
+
# subject { Foo }
|
|
26
|
+
# before { prepare }
|
|
27
27
|
# end
|
|
28
28
|
#
|
|
29
29
|
class ScatteredLet < Base
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
|
31
31
|
|
|
32
32
|
MSG = 'Group all let/let! blocks in the example group together.'
|
|
33
33
|
|
|
34
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
34
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
35
35
|
return unless example_group_with_body?(node)
|
|
36
36
|
|
|
37
37
|
check_let_declarations(node.body)
|
|
@@ -41,15 +41,21 @@ module RuboCop
|
|
|
41
41
|
|
|
42
42
|
def check_let_declarations(body)
|
|
43
43
|
lets = body.each_child_node.select { |node| let?(node) }
|
|
44
|
+
return if lets.empty?
|
|
44
45
|
|
|
45
46
|
first_let = lets.first
|
|
47
|
+
reference_let = first_let
|
|
48
|
+
|
|
46
49
|
lets.each_with_index do |node, idx|
|
|
47
|
-
|
|
50
|
+
if node.sibling_index == first_let.sibling_index + idx
|
|
51
|
+
reference_let = node
|
|
52
|
+
next
|
|
53
|
+
end
|
|
48
54
|
|
|
49
55
|
add_offense(node) do |corrector|
|
|
50
56
|
RuboCop::RSpec::Corrector::MoveNode.new(
|
|
51
57
|
node, corrector, processed_source
|
|
52
|
-
).move_after(
|
|
58
|
+
).move_after(reference_let)
|
|
53
59
|
end
|
|
54
60
|
end
|
|
55
61
|
end
|
|
@@ -42,12 +42,13 @@ module RuboCop
|
|
|
42
42
|
class ScatteredSetup < Base
|
|
43
43
|
include FinalEndLocation
|
|
44
44
|
include RangeHelp
|
|
45
|
+
include RepeatedItems
|
|
45
46
|
extend AutoCorrector
|
|
46
47
|
|
|
47
48
|
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
|
|
48
49
|
'example group (also defined on %<lines>s).'
|
|
49
50
|
|
|
50
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
51
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
51
52
|
return unless example_group?(node)
|
|
52
53
|
|
|
53
54
|
repeated_hooks(node).each do |occurrences|
|
|
@@ -63,17 +64,14 @@ module RuboCop
|
|
|
63
64
|
private
|
|
64
65
|
|
|
65
66
|
def repeated_hooks(node) # rubocop:disable Metrics/CyclomaticComplexity
|
|
66
|
-
hooks = RuboCop::RSpec::ExampleGroup.new(node)
|
|
67
|
-
.hooks
|
|
67
|
+
hooks = RuboCop::RSpec::ExampleGroup.new(node).hooks
|
|
68
68
|
.reject(&:inside_class_method?)
|
|
69
69
|
.select { |hook| hook.knowable_scope? && hook.name != :around }
|
|
70
|
-
.group_by { |hook| [hook.name, hook.scope, hook.metadata] }
|
|
71
|
-
.values
|
|
72
|
-
.reject(&:one?)
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
find_repeated_groups(
|
|
72
|
+
hooks,
|
|
73
|
+
key_proc: ->(hook) { [hook.name, hook.scope, hook.metadata] }
|
|
74
|
+
).map { |hook_group| hook_group.map(&:to_node) }
|
|
77
75
|
end
|
|
78
76
|
|
|
79
77
|
def lines_msg(numbers)
|