rubocop-rspec 3.8.0 → 3.10.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 +25 -0
- data/config/default.yml +22 -0
- 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 +3 -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 +1 -1
- 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/leaky_local_variable.rb +18 -0
- 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 +89 -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/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/output.rb +78 -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 +1 -1
- 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 +12 -2
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +3 -6
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +17 -10
- 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 +3 -0
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/hook.rb +13 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +1 -0
- metadata +28 -4
|
@@ -57,6 +57,13 @@ module RuboCop
|
|
|
57
57
|
# expectations
|
|
58
58
|
# end
|
|
59
59
|
#
|
|
60
|
+
# # good - when variable is used only in example metadata
|
|
61
|
+
# skip_message = 'not yet implemented'
|
|
62
|
+
#
|
|
63
|
+
# it 'does something', skip: skip_message do
|
|
64
|
+
# expectations
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
60
67
|
# # good - when variable is used only to include other examples
|
|
61
68
|
# examples = foo ? 'some examples' : 'other examples'
|
|
62
69
|
#
|
|
@@ -103,6 +110,8 @@ module RuboCop
|
|
|
103
110
|
def allowed_reference?(node)
|
|
104
111
|
node.each_ancestor.any? do |ancestor|
|
|
105
112
|
next true if example_method?(ancestor)
|
|
113
|
+
next true if in_example_arguments?(ancestor, node)
|
|
114
|
+
|
|
106
115
|
if includes_method?(ancestor)
|
|
107
116
|
next allowed_includes_arguments?(ancestor, node)
|
|
108
117
|
end
|
|
@@ -111,6 +120,15 @@ module RuboCop
|
|
|
111
120
|
end
|
|
112
121
|
end
|
|
113
122
|
|
|
123
|
+
def in_example_arguments?(ancestor, node)
|
|
124
|
+
return false unless ancestor.send_type?
|
|
125
|
+
return false unless Examples.all(ancestor.method_name)
|
|
126
|
+
|
|
127
|
+
ancestor.arguments.any? do |arg|
|
|
128
|
+
arg.equal?(node) || arg.each_descendant.any?(node)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
114
132
|
def allowed_includes_arguments?(node, argument)
|
|
115
133
|
node.arguments[1..].all? do |argument_node|
|
|
116
134
|
next true if argument_node.type?(:dstr, :dsym)
|
|
@@ -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,89 @@
|
|
|
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
|
+
parsed = Regexp::Parser.parse(node.content)
|
|
58
|
+
parsed.expressions.all? { |expr| simple_expression?(expr) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def simple_expression?(expr)
|
|
62
|
+
return false if expr.quantified?
|
|
63
|
+
return true if expr.is_a?(Regexp::Expression::Literal)
|
|
64
|
+
return true if expr.is_a?(Regexp::Expression::EscapeSequence::Literal)
|
|
65
|
+
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Reconstruct the literal string that the regex matches
|
|
70
|
+
def regexp_to_string(node)
|
|
71
|
+
parsed = Regexp::Parser.parse(node.content)
|
|
72
|
+
string_content = parsed.expressions.map do |expr|
|
|
73
|
+
expr.respond_to?(:char) ? expr.char : expr.text
|
|
74
|
+
end.join
|
|
75
|
+
|
|
76
|
+
to_string_literal(string_content)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def to_string_literal(string)
|
|
80
|
+
if string.include?("'")
|
|
81
|
+
%("#{string.gsub('"', '\"')}")
|
|
82
|
+
else
|
|
83
|
+
"'#{string}'"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
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
|
|
@@ -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
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# NOTE: Originally based on the `Rails/Output` cop.
|
|
6
|
+
module RSpec
|
|
7
|
+
# Checks for the use of output calls like puts and print in specs.
|
|
8
|
+
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This autocorrection is marked as unsafe because, in rare cases, print
|
|
11
|
+
# statements can be used on purpose for integration testing and deleting
|
|
12
|
+
# them will cause tests to fail.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# # bad
|
|
16
|
+
# puts 'A debug message'
|
|
17
|
+
# pp 'A debug message'
|
|
18
|
+
# print 'A debug message'
|
|
19
|
+
class Output < Base
|
|
20
|
+
extend AutoCorrector
|
|
21
|
+
|
|
22
|
+
MSG = 'Do not write to stdout in specs.'
|
|
23
|
+
|
|
24
|
+
KERNEL_METHODS = %i[
|
|
25
|
+
ap
|
|
26
|
+
p
|
|
27
|
+
pp
|
|
28
|
+
pretty_print
|
|
29
|
+
print
|
|
30
|
+
puts
|
|
31
|
+
].to_set.freeze
|
|
32
|
+
private_constant :KERNEL_METHODS
|
|
33
|
+
|
|
34
|
+
IO_METHODS = %i[
|
|
35
|
+
binwrite
|
|
36
|
+
syswrite
|
|
37
|
+
write
|
|
38
|
+
write_nonblock
|
|
39
|
+
].to_set.freeze
|
|
40
|
+
private_constant :IO_METHODS
|
|
41
|
+
|
|
42
|
+
RESTRICT_ON_SEND = (KERNEL_METHODS + IO_METHODS).to_a.freeze
|
|
43
|
+
|
|
44
|
+
# @!method output?(node)
|
|
45
|
+
def_node_matcher :output?, <<~PATTERN
|
|
46
|
+
(send nil? KERNEL_METHODS ...)
|
|
47
|
+
PATTERN
|
|
48
|
+
|
|
49
|
+
# @!method io_output?(node)
|
|
50
|
+
def_node_matcher :io_output?, <<~PATTERN
|
|
51
|
+
(send
|
|
52
|
+
{
|
|
53
|
+
(gvar #match_gvar?)
|
|
54
|
+
(const {nil? cbase} {:STDOUT :STDERR})
|
|
55
|
+
}
|
|
56
|
+
IO_METHODS
|
|
57
|
+
...)
|
|
58
|
+
PATTERN
|
|
59
|
+
|
|
60
|
+
def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity
|
|
61
|
+
return if node.parent&.call_type? || node.block_node
|
|
62
|
+
return if !output?(node) && !io_output?(node)
|
|
63
|
+
return if node.arguments.any? { |arg| arg.type?(:hash, :block_pass) }
|
|
64
|
+
|
|
65
|
+
add_offense(node) do |corrector|
|
|
66
|
+
corrector.remove(node)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def match_gvar?(sym)
|
|
73
|
+
%i[$stdout $stderr].include?(sym)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
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|
|
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
|
19
19
|
MSG = "Don't repeat examples within an example group. " \
|
|
20
20
|
'Repeated on line(s) %<lines>s.'
|
|
21
21
|
|
|
22
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
22
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
23
23
|
return unless example_group?(node)
|
|
24
24
|
|
|
25
25
|
find_repeated_examples(node).each do |repeated_examples|
|
|
@@ -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
|
|
@@ -8,6 +8,7 @@ module RuboCop
|
|
|
8
8
|
# Unify `before` and `after` hooks when possible.
|
|
9
9
|
# However, `around` hooks are allowed to be defined multiple times,
|
|
10
10
|
# as unifying them would typically make the code harder to read.
|
|
11
|
+
# Hooks defined in class methods are also ignored.
|
|
11
12
|
#
|
|
12
13
|
# @example
|
|
13
14
|
# # bad
|
|
@@ -30,6 +31,14 @@ module RuboCop
|
|
|
30
31
|
# around { |example| before2; example.call; after2 }
|
|
31
32
|
# end
|
|
32
33
|
#
|
|
34
|
+
# # good
|
|
35
|
+
# describe Foo do
|
|
36
|
+
# before { setup1 }
|
|
37
|
+
# def self.setup
|
|
38
|
+
# before { setup2 }
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
33
42
|
class ScatteredSetup < Base
|
|
34
43
|
include FinalEndLocation
|
|
35
44
|
include RangeHelp
|
|
@@ -38,7 +47,7 @@ module RuboCop
|
|
|
38
47
|
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
|
|
39
48
|
'example group (also defined on %<lines>s).'
|
|
40
49
|
|
|
41
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
50
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
42
51
|
return unless example_group?(node)
|
|
43
52
|
|
|
44
53
|
repeated_hooks(node).each do |occurrences|
|
|
@@ -53,9 +62,10 @@ module RuboCop
|
|
|
53
62
|
|
|
54
63
|
private
|
|
55
64
|
|
|
56
|
-
def repeated_hooks(node)
|
|
65
|
+
def repeated_hooks(node) # rubocop:disable Metrics/CyclomaticComplexity
|
|
57
66
|
hooks = RuboCop::RSpec::ExampleGroup.new(node)
|
|
58
67
|
.hooks
|
|
68
|
+
.reject(&:inside_class_method?)
|
|
59
69
|
.select { |hook| hook.knowable_scope? && hook.name != :around }
|
|
60
70
|
.group_by { |hook| [hook.name, hook.scope, hook.metadata] }
|
|
61
71
|
.values
|
|
@@ -78,7 +78,7 @@ module RuboCop
|
|
|
78
78
|
(block (send #rspec? #SharedGroups.examples ...) ...)
|
|
79
79
|
PATTERN
|
|
80
80
|
|
|
81
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
81
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
82
82
|
context_with_only_examples(node) do
|
|
83
83
|
add_offense(node.send_node, message: MSG_EXAMPLES) do |corrector|
|
|
84
84
|
corrector.replace(node.send_node.loc.selector, 'shared_examples')
|
|
@@ -24,6 +24,8 @@ module RuboCop
|
|
|
24
24
|
# end
|
|
25
25
|
#
|
|
26
26
|
class SkipBlockInsideExample < Base
|
|
27
|
+
include InsideExample
|
|
28
|
+
|
|
27
29
|
MSG = "Don't pass a block to `skip` inside examples."
|
|
28
30
|
|
|
29
31
|
def on_block(node)
|
|
@@ -34,12 +36,7 @@ module RuboCop
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
alias on_numblock on_block
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def inside_example?(node)
|
|
41
|
-
node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
|
|
42
|
-
end
|
|
39
|
+
alias on_itblock on_block
|
|
43
40
|
end
|
|
44
41
|
end
|
|
45
42
|
end
|
|
@@ -44,6 +44,7 @@ module RuboCop
|
|
|
44
44
|
include FileHelp
|
|
45
45
|
|
|
46
46
|
MSG = 'Spec path should end with `%<suffix>s`.'
|
|
47
|
+
PATH_NAME_BOUNDARY = '(?![[:alnum:]])'
|
|
47
48
|
|
|
48
49
|
# @!method example_group_arguments(node)
|
|
49
50
|
def_node_matcher :example_group_arguments, <<~PATTERN
|
|
@@ -119,7 +120,11 @@ module RuboCop
|
|
|
119
120
|
# For the suffix shown in the offense message, modify the regular
|
|
120
121
|
# expression pattern to resemble a glob pattern for clearer error
|
|
121
122
|
# messages.
|
|
122
|
-
suffix = pattern
|
|
123
|
+
suffix = pattern
|
|
124
|
+
.sub(PATH_NAME_BOUNDARY, '')
|
|
125
|
+
.sub('.*', '*')
|
|
126
|
+
.sub('[^/]*', '*')
|
|
127
|
+
.sub('\.', '.')
|
|
123
128
|
add_offense(send_node, message: format(MSG, suffix: suffix))
|
|
124
129
|
end
|
|
125
130
|
|
|
@@ -132,19 +137,21 @@ module RuboCop
|
|
|
132
137
|
end
|
|
133
138
|
|
|
134
139
|
def correct_path_pattern(class_name, arguments)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
[
|
|
141
|
+
expected_path(class_name),
|
|
142
|
+
PATH_NAME_BOUNDARY,
|
|
143
|
+
method_name_pattern(arguments.first),
|
|
144
|
+
'[^/]*_spec\.rb'
|
|
145
|
+
].join
|
|
139
146
|
end
|
|
140
147
|
|
|
141
|
-
def
|
|
142
|
-
return if
|
|
148
|
+
def method_name_pattern(method_name)
|
|
149
|
+
return if ignore_method_name?(method_name)
|
|
143
150
|
|
|
144
|
-
method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')
|
|
151
|
+
".*#{method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')}"
|
|
145
152
|
end
|
|
146
153
|
|
|
147
|
-
def
|
|
154
|
+
def ignore_method_name?(method_name)
|
|
148
155
|
!method_name&.str_type? || ignore_methods?
|
|
149
156
|
end
|
|
150
157
|
|
|
@@ -175,7 +182,7 @@ module RuboCop
|
|
|
175
182
|
end
|
|
176
183
|
|
|
177
184
|
def filename_ends_with?(pattern)
|
|
178
|
-
expanded_file_path.match?(
|
|
185
|
+
expanded_file_path.match?(%r{(?:\A|/)#{pattern}$})
|
|
179
186
|
end
|
|
180
187
|
end
|
|
181
188
|
end
|
|
@@ -20,6 +20,8 @@ module RuboCop
|
|
|
20
20
|
# subject(:test_subject) { foo }
|
|
21
21
|
#
|
|
22
22
|
class SubjectDeclaration < Base
|
|
23
|
+
extend AutoCorrector
|
|
24
|
+
|
|
23
25
|
MSG_LET = 'Use subject explicitly rather than using let'
|
|
24
26
|
MSG_REDUNDANT = 'Ambiguous declaration of subject'
|
|
25
27
|
|
|
@@ -32,7 +34,11 @@ module RuboCop
|
|
|
32
34
|
offense = offensive_subject_declaration?(node)
|
|
33
35
|
return unless offense
|
|
34
36
|
|
|
35
|
-
add_offense(node, message: message_for(offense))
|
|
37
|
+
add_offense(node, message: message_for(offense)) do |corrector|
|
|
38
|
+
corrector.replace(node.loc.selector,
|
|
39
|
+
node.method_name.to_s.sub('let', 'subject'))
|
|
40
|
+
corrector.remove(first_argument_range(node))
|
|
41
|
+
end
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
private
|
|
@@ -40,6 +46,16 @@ module RuboCop
|
|
|
40
46
|
def message_for(offense)
|
|
41
47
|
Helpers.all(offense) ? MSG_LET : MSG_REDUNDANT
|
|
42
48
|
end
|
|
49
|
+
|
|
50
|
+
def first_argument_range(node)
|
|
51
|
+
if node.arguments.one?
|
|
52
|
+
node.loc.begin.join(node.loc.end)
|
|
53
|
+
else
|
|
54
|
+
node.first_argument.source_range.with(
|
|
55
|
+
end_pos: node.arguments[1].source_range.begin_pos
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
43
59
|
end
|
|
44
60
|
end
|
|
45
61
|
end
|
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
|
52
52
|
(block (send #rspec? {#ExampleGroups.all #Examples.all} $_) ...)
|
|
53
53
|
PATTERN
|
|
54
54
|
|
|
55
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
55
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
56
56
|
example_groups_or_example?(node) do |arg|
|
|
57
57
|
add_offense(arg) if offense?(arg)
|
|
58
58
|
end
|
|
@@ -13,6 +13,8 @@ module RuboCop
|
|
|
13
13
|
# expect(something).to be(1)
|
|
14
14
|
#
|
|
15
15
|
class VoidExpect < Base
|
|
16
|
+
include InsideExample
|
|
17
|
+
|
|
16
18
|
MSG = 'Do not use `expect()` without `.to` or `.not_to`. ' \
|
|
17
19
|
'Chain the methods or remove it.'
|
|
18
20
|
RESTRICT_ON_SEND = %i[expect].freeze
|
|
@@ -34,7 +36,7 @@ module RuboCop
|
|
|
34
36
|
check_expect(node)
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
39
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
38
40
|
return unless expect_block?(node)
|
|
39
41
|
return unless inside_example?(node)
|
|
40
42
|
|
|
@@ -55,10 +57,6 @@ module RuboCop
|
|
|
55
57
|
|
|
56
58
|
parent.block_type? && parent.body == expect
|
|
57
59
|
end
|
|
58
|
-
|
|
59
|
-
def inside_example?(node)
|
|
60
|
-
node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
|
|
61
|
-
end
|
|
62
60
|
end
|
|
63
61
|
end
|
|
64
62
|
end
|
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
|
27
27
|
# @!method block_call?(node)
|
|
28
28
|
def_node_matcher :block_call?, '(send (lvar %) :call ...)'
|
|
29
29
|
|
|
30
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
30
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
31
31
|
return unless method_on_stub?(node.send_node)
|
|
32
32
|
|
|
33
33
|
block_arg(node.arguments) do |block|
|
|
@@ -21,6 +21,7 @@ require_relative 'rspec/describe_symbol'
|
|
|
21
21
|
require_relative 'rspec/described_class'
|
|
22
22
|
require_relative 'rspec/described_class_module_wrapping'
|
|
23
23
|
require_relative 'rspec/dialect'
|
|
24
|
+
require_relative 'rspec/discarded_matcher'
|
|
24
25
|
require_relative 'rspec/duplicated_metadata'
|
|
25
26
|
require_relative 'rspec/empty_example_group'
|
|
26
27
|
require_relative 'rspec/empty_hook'
|
|
@@ -61,6 +62,7 @@ require_relative 'rspec/leaky_local_variable'
|
|
|
61
62
|
require_relative 'rspec/let_before_examples'
|
|
62
63
|
require_relative 'rspec/let_setup'
|
|
63
64
|
require_relative 'rspec/match_array'
|
|
65
|
+
require_relative 'rspec/match_with_simple_regex'
|
|
64
66
|
require_relative 'rspec/message_chain'
|
|
65
67
|
require_relative 'rspec/message_expectation'
|
|
66
68
|
require_relative 'rspec/message_spies'
|
|
@@ -75,6 +77,7 @@ require_relative 'rspec/named_subject'
|
|
|
75
77
|
require_relative 'rspec/nested_groups'
|
|
76
78
|
require_relative 'rspec/no_expectation_example'
|
|
77
79
|
require_relative 'rspec/not_to_not'
|
|
80
|
+
require_relative 'rspec/output'
|
|
78
81
|
require_relative 'rspec/overwriting_setup'
|
|
79
82
|
require_relative 'rspec/pending'
|
|
80
83
|
require_relative 'rspec/pending_without_reason'
|