rubocop-rspec 2.16.0 → 2.24.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|