rubocop-rspec 2.16.0 → 2.24.1
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 +124 -9
- data/README.md +3 -3
- data/config/default.yml +145 -18
- data/config/obsoletion.yml +15 -0
- data/lib/rubocop/cop/rspec/be_empty.rb +44 -0
- data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +29 -115
- data/lib/rubocop/cop/rspec/capybara/match_style.rb +38 -0
- data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +23 -96
- data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +19 -75
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +14 -83
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +25 -69
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +26 -63
- data/lib/rubocop/cop/rspec/change_by_zero.rb +33 -23
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_method.rb +5 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +13 -6
- data/lib/rubocop/cop/rspec/describe_method.rb +16 -8
- data/lib/rubocop/cop/rspec/described_class.rb +2 -1
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +7 -5
- data/lib/rubocop/cop/rspec/dialect.rb +1 -1
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_example_group.rb +10 -7
- data/lib/rubocop/cop/rspec/empty_hook.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
- data/lib/rubocop/cop/rspec/eq.rb +47 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
- data/lib/rubocop/cop/rspec/expect_actual.rb +4 -4
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +25 -118
- data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +40 -107
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +30 -250
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +19 -46
- data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +23 -64
- data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +45 -79
- data/lib/rubocop/cop/rspec/file_path.rb +8 -2
- data/lib/rubocop/cop/rspec/focus.rb +19 -5
- data/lib/rubocop/cop/rspec/hook_argument.rb +12 -9
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +5 -3
- data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +8 -4
- data/lib/rubocop/cop/rspec/let_setup.rb +6 -8
- data/lib/rubocop/cop/rspec/match_array.rb +59 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +197 -0
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +1 -2
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +20 -4
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +7 -5
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +2 -5
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +3 -1
- data/lib/rubocop/cop/rspec/pending.rb +23 -13
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +72 -36
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +49 -40
- data/lib/rubocop/cop/rspec/rails/have_http_status.rb +11 -6
- data/lib/rubocop/cop/rspec/rails/http_status.rb +107 -34
- data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +4 -4
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +60 -0
- data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
- data/lib/rubocop/cop/rspec/rails/travel_around.rb +92 -0
- data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
- data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -4
- data/lib/rubocop/cop/rspec/scattered_setup.rb +23 -6
- data/lib/rubocop/cop/rspec/shared_context.rb +12 -13
- data/lib/rubocop/cop/rspec/shared_examples.rb +6 -4
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
- data/lib/rubocop/cop/rspec/sort_metadata.rb +4 -3
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
- data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +0 -1
- data/lib/rubocop/cop/rspec/variable_definition.rb +5 -2
- data/lib/rubocop/cop/rspec/variable_name.rb +4 -1
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +7 -7
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
- data/lib/rubocop/cop/rspec_cops.rb +16 -0
- data/lib/rubocop/rspec/config_formatter.rb +16 -0
- data/lib/rubocop/rspec/example_group.rb +6 -8
- data/lib/rubocop/rspec/language/node_pattern.rb +26 -0
- data/lib/rubocop/rspec/language.rb +25 -16
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +4 -5
- metadata +50 -8
- data/lib/rubocop/cop/rspec/mixin/capybara_help.rb +0 -80
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +0 -146
- data/lib/rubocop/rspec/factory_bot/language.rb +0 -37
- data/lib/rubocop/rspec/factory_bot.rb +0 -64
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
module Rails
|
7
|
+
# Prefer to travel in `before` rather than `around`.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop is unsafe because the automatic `travel_back` is only run
|
11
|
+
# on test cases that are considered as Rails related.
|
12
|
+
#
|
13
|
+
# And also, this cop's autocorrection is unsafe because the order of
|
14
|
+
# execution will change if other steps exist before traveling in
|
15
|
+
# `around`.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# around do |example|
|
20
|
+
# freeze_time do
|
21
|
+
# example.run
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# before { freeze_time }
|
27
|
+
class TravelAround < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
30
|
+
MSG = 'Prefer to travel in `before` rather than `around`.'
|
31
|
+
|
32
|
+
TRAVEL_METHOD_NAMES = %i[
|
33
|
+
freeze_time
|
34
|
+
travel
|
35
|
+
travel_to
|
36
|
+
].to_set.freeze
|
37
|
+
|
38
|
+
# @!method extract_run_in_travel(node)
|
39
|
+
def_node_matcher :extract_run_in_travel, <<~PATTERN
|
40
|
+
(block
|
41
|
+
$(send nil? TRAVEL_METHOD_NAMES ...)
|
42
|
+
(args ...)
|
43
|
+
(send _ :run)
|
44
|
+
)
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
# @!method match_around_each?(node)
|
48
|
+
def_node_matcher :match_around_each?, <<~PATTERN
|
49
|
+
(block
|
50
|
+
(send _ :around (sym :each)?)
|
51
|
+
...
|
52
|
+
)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
def on_block(node)
|
56
|
+
run_node = extract_run_in_travel(node)
|
57
|
+
return unless run_node
|
58
|
+
|
59
|
+
around_node = extract_surrounding_around_block(run_node)
|
60
|
+
return unless around_node
|
61
|
+
|
62
|
+
add_offense(node) do |corrector|
|
63
|
+
autocorrect(corrector, node, run_node, around_node)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
alias on_numblock on_block
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def autocorrect(corrector, node, run_node, around_node)
|
71
|
+
corrector.replace(
|
72
|
+
node,
|
73
|
+
node.body.source
|
74
|
+
)
|
75
|
+
corrector.insert_before(
|
76
|
+
around_node,
|
77
|
+
"before { #{run_node.source} }\n\n"
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param node [RuboCop::AST::BlockNode]
|
82
|
+
# @return [RuboCop::AST::BlockNode, nil]
|
83
|
+
def extract_surrounding_around_block(node)
|
84
|
+
node.each_ancestor(:block).find do |ancestor|
|
85
|
+
match_around_each?(ancestor)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for multiple messages stubbed on the same object.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# The autocorrection is marked as unsafe, because it may change the
|
10
|
+
# order of stubs. This in turn may cause e.g. variables to be called
|
11
|
+
# before they are defined.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# before do
|
16
|
+
# allow(Service).to receive(:foo).and_return(bar)
|
17
|
+
# allow(Service).to receive(:baz).and_return(qux)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# before do
|
22
|
+
# allow(Service).to receive_messages(foo: bar, baz: qux)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good - ignore same message
|
26
|
+
# before do
|
27
|
+
# allow(Service).to receive(:foo).and_return(bar)
|
28
|
+
# allow(Service).to receive(:foo).and_return(qux)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
class ReceiveMessages < Base
|
32
|
+
extend AutoCorrector
|
33
|
+
include RangeHelp
|
34
|
+
|
35
|
+
MSG = 'Use `receive_messages` instead of multiple stubs on lines ' \
|
36
|
+
'%<loc>s.'
|
37
|
+
|
38
|
+
# @!method allow_receive_message?(node)
|
39
|
+
def_node_matcher :allow_receive_message?, <<~PATTERN
|
40
|
+
(send (send nil? :allow ...) :to (send (send nil? :receive (sym _)) :and_return !#heredoc_or_splat?))
|
41
|
+
PATTERN
|
42
|
+
|
43
|
+
# @!method allow_argument(node)
|
44
|
+
def_node_matcher :allow_argument, <<~PATTERN
|
45
|
+
(send (send nil? :allow $_ ...) ...)
|
46
|
+
PATTERN
|
47
|
+
|
48
|
+
# @!method receive_node(node)
|
49
|
+
def_node_search :receive_node, <<~PATTERN
|
50
|
+
$(send (send nil? :receive ...) ...)
|
51
|
+
PATTERN
|
52
|
+
|
53
|
+
# @!method receive_arg(node)
|
54
|
+
def_node_search :receive_arg, <<~PATTERN
|
55
|
+
(send (send nil? :receive $_) ...)
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
# @!method receive_and_return_argument(node)
|
59
|
+
def_node_matcher :receive_and_return_argument, <<~PATTERN
|
60
|
+
(send (send nil? :allow ...) :to (send (send nil? :receive (sym $_)) :and_return $_))
|
61
|
+
PATTERN
|
62
|
+
|
63
|
+
def on_begin(node)
|
64
|
+
repeated_receive_message(node).each do |item, repeated_lines, args|
|
65
|
+
next if repeated_lines.empty?
|
66
|
+
|
67
|
+
register_offense(item, repeated_lines, args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def repeated_receive_message(node)
|
74
|
+
node
|
75
|
+
.children
|
76
|
+
.select { |child| allow_receive_message?(child) }
|
77
|
+
.group_by { |child| allow_argument(child) }
|
78
|
+
.values
|
79
|
+
.reject(&:one?)
|
80
|
+
.flat_map { |items| add_repeated_lines_and_arguments(items) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_repeated_lines_and_arguments(items)
|
84
|
+
uniq_items = uniq_items(items)
|
85
|
+
repeated_lines = uniq_items.map(&:first_line)
|
86
|
+
uniq_items.map do |item|
|
87
|
+
[item, repeated_lines - [item.first_line], arguments(uniq_items)]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def uniq_items(items)
|
92
|
+
items.select do |item|
|
93
|
+
items.none? do |i|
|
94
|
+
receive_arg(item).first == receive_arg(i).first &&
|
95
|
+
!same_line?(item, i)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def arguments(items)
|
101
|
+
items.map do |item|
|
102
|
+
receive_and_return_argument(item) do |receive_arg, return_arg|
|
103
|
+
"#{normalize_receive_arg(receive_arg)}: " \
|
104
|
+
"#{normalize_return_arg(return_arg)}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def normalize_receive_arg(receive_arg)
|
110
|
+
if requires_quotes?(receive_arg)
|
111
|
+
"'#{receive_arg}'"
|
112
|
+
else
|
113
|
+
receive_arg
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def normalize_return_arg(return_arg)
|
118
|
+
if return_arg.hash_type? && !return_arg.braces?
|
119
|
+
"{ #{return_arg.source} }"
|
120
|
+
else
|
121
|
+
return_arg.source
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def register_offense(item, repeated_lines, args)
|
126
|
+
add_offense(item, message: message(repeated_lines)) do |corrector|
|
127
|
+
if item.loc.line > repeated_lines.max
|
128
|
+
replace_to_receive_messages(corrector, item, args)
|
129
|
+
else
|
130
|
+
corrector.remove(item_range_by_whole_lines(item))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def message(repeated_lines)
|
136
|
+
format(MSG, loc: repeated_lines)
|
137
|
+
end
|
138
|
+
|
139
|
+
def replace_to_receive_messages(corrector, item, args)
|
140
|
+
receive_node(item) do |node|
|
141
|
+
corrector.replace(node,
|
142
|
+
"receive_messages(#{args.join(', ')})")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def item_range_by_whole_lines(item)
|
147
|
+
range_by_whole_lines(item.source_range, include_final_newline: true)
|
148
|
+
end
|
149
|
+
|
150
|
+
def heredoc_or_splat?(node)
|
151
|
+
(node.str_type? || node.dstr_type?) && node.heredoc? ||
|
152
|
+
node.splat_type?
|
153
|
+
end
|
154
|
+
|
155
|
+
def requires_quotes?(value)
|
156
|
+
value.match?(/^:".*?"|=$|^\W+$/)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Remove redundant `around` hook.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# around do |example|
|
11
|
+
# example.run
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
#
|
16
|
+
class RedundantAround < Base
|
17
|
+
extend AutoCorrector
|
18
|
+
|
19
|
+
MSG = 'Remove redundant `around` hook.'
|
20
|
+
|
21
|
+
RESTRICT_ON_SEND = %i[around].freeze
|
22
|
+
|
23
|
+
def on_block(node)
|
24
|
+
return unless match_redundant_around_hook_block?(node)
|
25
|
+
|
26
|
+
add_offense(node) do |corrector|
|
27
|
+
autocorrect(corrector, node)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias on_numblock on_block
|
31
|
+
|
32
|
+
def on_send(node)
|
33
|
+
return unless match_redundant_around_hook_send?(node)
|
34
|
+
|
35
|
+
add_offense(node) do |corrector|
|
36
|
+
autocorrect(corrector, node)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @!method match_redundant_around_hook_block?(node)
|
43
|
+
def_node_matcher :match_redundant_around_hook_block?, <<~PATTERN
|
44
|
+
({block numblock} (send _ :around ...) ... (send _ :run))
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
# @!method match_redundant_around_hook_send?(node)
|
48
|
+
def_node_matcher :match_redundant_around_hook_send?, <<~PATTERN
|
49
|
+
(send
|
50
|
+
_
|
51
|
+
:around
|
52
|
+
...
|
53
|
+
(block-pass
|
54
|
+
(sym :run)
|
55
|
+
)
|
56
|
+
)
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
def autocorrect(corrector, node)
|
60
|
+
corrector.remove(node)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -43,6 +43,8 @@ module RuboCop
|
|
43
43
|
# end
|
44
44
|
#
|
45
45
|
class RepeatedExampleGroupBody < Base
|
46
|
+
include SkipOrPending
|
47
|
+
|
46
48
|
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
|
47
49
|
|
48
50
|
# @!method several_example_groups?(node)
|
@@ -59,11 +61,6 @@ module RuboCop
|
|
59
61
|
# @!method const_arg(node)
|
60
62
|
def_node_matcher :const_arg, '(block (send _ _ $const ...) ...)'
|
61
63
|
|
62
|
-
# @!method skip_or_pending?(node)
|
63
|
-
def_node_matcher :skip_or_pending?, <<-PATTERN
|
64
|
-
(block <(send nil? {:skip :pending} ...) ...>)
|
65
|
-
PATTERN
|
66
|
-
|
67
64
|
def on_begin(node)
|
68
65
|
return unless several_example_groups?(node)
|
69
66
|
|
@@ -78,7 +75,7 @@ module RuboCop
|
|
78
75
|
node
|
79
76
|
.children
|
80
77
|
.select { |child| example_group_with_body?(child) }
|
81
|
-
.reject { |child|
|
78
|
+
.reject { |child| skip_or_pending_inside_block?(child) }
|
82
79
|
.group_by { |group| signature_keys(group) }
|
83
80
|
.values
|
84
81
|
.reject(&:one?)
|
@@ -43,6 +43,8 @@ module RuboCop
|
|
43
43
|
# end
|
44
44
|
#
|
45
45
|
class RepeatedExampleGroupDescription < Base
|
46
|
+
include SkipOrPending
|
47
|
+
|
46
48
|
MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
|
47
49
|
|
48
50
|
# @!method several_example_groups?(node)
|
@@ -55,11 +57,6 @@ module RuboCop
|
|
55
57
|
(block (send _ _ $_ $...) ...)
|
56
58
|
PATTERN
|
57
59
|
|
58
|
-
# @!method skip_or_pending?(node)
|
59
|
-
def_node_matcher :skip_or_pending?, <<-PATTERN
|
60
|
-
(block <(send nil? {:skip :pending}) ...>)
|
61
|
-
PATTERN
|
62
|
-
|
63
60
|
# @!method empty_description?(node)
|
64
61
|
def_node_matcher :empty_description?, '(block (send _ _) ...)'
|
65
62
|
|
@@ -77,7 +74,7 @@ module RuboCop
|
|
77
74
|
node
|
78
75
|
.children
|
79
76
|
.select { |child| example_group?(child) }
|
80
|
-
.reject { |child|
|
77
|
+
.reject { |child| skip_or_pending_inside_block?(child) }
|
81
78
|
.reject { |child| empty_description?(child) }
|
82
79
|
.group_by { |group| doc_string_and_metadata(group) }
|
83
80
|
.values
|
@@ -56,12 +56,11 @@ module RuboCop
|
|
56
56
|
|
57
57
|
# @!method include_examples?(node)
|
58
58
|
def_node_matcher :include_examples?,
|
59
|
-
|
59
|
+
'(send nil? #Includes.examples ...)'
|
60
60
|
|
61
61
|
# @!method shared_examples_name(node)
|
62
|
-
def_node_matcher :shared_examples_name,
|
63
|
-
|
64
|
-
PATTERN
|
62
|
+
def_node_matcher :shared_examples_name,
|
63
|
+
'(send nil? #Includes.examples $_name ...)'
|
65
64
|
|
66
65
|
def on_begin(node)
|
67
66
|
return unless several_include_examples?(node)
|
@@ -23,6 +23,9 @@ module RuboCop
|
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
class ScatteredSetup < Base
|
26
|
+
include RangeHelp
|
27
|
+
extend AutoCorrector
|
28
|
+
|
26
29
|
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
|
27
30
|
'example group (also defined on %<lines>s).'
|
28
31
|
|
@@ -30,13 +33,11 @@ module RuboCop
|
|
30
33
|
return unless example_group?(node)
|
31
34
|
|
32
35
|
repeated_hooks(node).each do |occurrences|
|
33
|
-
lines = occurrences.map(&:first_line)
|
34
|
-
|
35
36
|
occurrences.each do |occurrence|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
message = message(occurrences, occurrence)
|
38
|
+
add_offense(occurrence, message: message) do |corrector|
|
39
|
+
autocorrect(corrector, occurrences.first, occurrence)
|
40
|
+
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
@@ -63,6 +64,22 @@ module RuboCop
|
|
63
64
|
"lines #{numbers.join(', ')}"
|
64
65
|
end
|
65
66
|
end
|
67
|
+
|
68
|
+
def message(occurrences, occurrence)
|
69
|
+
lines = occurrences.map(&:first_line)
|
70
|
+
lines_except_current = lines - [occurrence.first_line]
|
71
|
+
format(MSG, hook_name: occurrences.first.method_name,
|
72
|
+
lines: lines_msg(lines_except_current))
|
73
|
+
end
|
74
|
+
|
75
|
+
def autocorrect(corrector, first_occurrence, occurrence)
|
76
|
+
return if first_occurrence == occurrence || !first_occurrence.body
|
77
|
+
|
78
|
+
corrector.insert_after(first_occurrence.body,
|
79
|
+
"\n#{occurrence.body.source}")
|
80
|
+
corrector.remove(range_by_whole_lines(occurrence.source_range,
|
81
|
+
include_final_newline: true))
|
82
|
+
end
|
66
83
|
end
|
67
84
|
end
|
68
85
|
end
|
@@ -57,27 +57,26 @@ module RuboCop
|
|
57
57
|
MSG_CONTEXT = "Use `shared_context` when you don't define examples."
|
58
58
|
|
59
59
|
# @!method examples?(node)
|
60
|
-
def_node_search :examples?,
|
61
|
-
|
60
|
+
def_node_search :examples?, <<~PATTERN
|
61
|
+
(send nil? {#Includes.examples #Examples.all} ...)
|
62
|
+
PATTERN
|
62
63
|
|
63
64
|
# @!method context?(node)
|
64
65
|
def_node_search :context?, <<-PATTERN
|
65
|
-
(
|
66
|
-
|
67
|
-
#Subjects.all
|
68
|
-
#Helpers.all
|
69
|
-
#Includes.context
|
70
|
-
#Hooks.all
|
71
|
-
} ...
|
66
|
+
(send nil?
|
67
|
+
{#Subjects.all #Helpers.all #Includes.context #Hooks.all} ...
|
72
68
|
)
|
73
69
|
PATTERN
|
74
70
|
|
75
71
|
# @!method shared_context(node)
|
76
|
-
def_node_matcher :shared_context,
|
77
|
-
|
72
|
+
def_node_matcher :shared_context, <<~PATTERN
|
73
|
+
(block (send #rspec? #SharedGroups.context ...) ...)
|
74
|
+
PATTERN
|
75
|
+
|
78
76
|
# @!method shared_example(node)
|
79
|
-
def_node_matcher :shared_example,
|
80
|
-
|
77
|
+
def_node_matcher :shared_example, <<~PATTERN
|
78
|
+
(block (send #rspec? #SharedGroups.examples ...) ...)
|
79
|
+
PATTERN
|
81
80
|
|
82
81
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
83
82
|
context_with_only_examples(node) do
|
@@ -24,10 +24,12 @@ module RuboCop
|
|
24
24
|
extend AutoCorrector
|
25
25
|
|
26
26
|
# @!method shared_examples(node)
|
27
|
-
def_node_matcher :shared_examples,
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def_node_matcher :shared_examples, <<~PATTERN
|
28
|
+
{
|
29
|
+
(send #rspec? #SharedGroups.all ...)
|
30
|
+
(send nil? #Includes.all ...)
|
31
|
+
}
|
32
|
+
PATTERN
|
31
33
|
|
32
34
|
def on_send(node)
|
33
35
|
shared_examples(node) do
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for passing a block to `skip` within examples.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# it 'does something' do
|
11
|
+
# skip 'not yet implemented' do
|
12
|
+
# do_something
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# it 'does something' do
|
18
|
+
# skip 'not yet implemented'
|
19
|
+
# do_something
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good - when outside example
|
23
|
+
# skip 'not yet implemented' do
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
class SkipBlockInsideExample < Base
|
27
|
+
MSG = "Don't pass a block to `skip` inside examples."
|
28
|
+
|
29
|
+
def on_block(node)
|
30
|
+
return unless node.method?(:skip)
|
31
|
+
return unless inside_example?(node)
|
32
|
+
|
33
|
+
add_offense(node)
|
34
|
+
end
|
35
|
+
|
36
|
+
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
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -23,7 +23,8 @@ module RuboCop
|
|
23
23
|
|
24
24
|
MSG = 'Sort metadata alphabetically.'
|
25
25
|
|
26
|
-
def on_metadata(symbols,
|
26
|
+
def on_metadata(symbols, hash)
|
27
|
+
pairs = hash&.pairs || []
|
27
28
|
return if sorted?(symbols, pairs)
|
28
29
|
|
29
30
|
crime_scene = crime_scene(symbols, pairs)
|
@@ -38,8 +39,8 @@ module RuboCop
|
|
38
39
|
metadata = symbols + pairs
|
39
40
|
|
40
41
|
range_between(
|
41
|
-
metadata.first.
|
42
|
-
metadata.last.
|
42
|
+
metadata.first.source_range.begin_pos,
|
43
|
+
metadata.last.source_range.end_pos
|
43
44
|
)
|
44
45
|
end
|
45
46
|
|