rubocop-rspec 2.18.1 → 2.20.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 +37 -2
- data/README.md +1 -1
- data/config/default.yml +46 -1
- 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/change_by_zero.rb +3 -3
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_wording.rb +13 -5
- 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 +1 -1
- data/lib/rubocop/cop/rspec/empty_example_group.rb +7 -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/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_actual.rb +2 -2
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +3 -3
- data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +2 -2
- data/lib/rubocop/cop/rspec/file_path.rb +1 -1
- data/lib/rubocop/cop/rspec/focus.rb +4 -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 +76 -0
- data/lib/rubocop/cop/rspec/let_before_examples.rb +4 -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/mixin/empty_line_separation.rb +1 -2
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- 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 +6 -4
- 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 +12 -12
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +74 -36
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +9 -35
- data/lib/rubocop/cop/rspec/rails/have_http_status.rb +8 -5
- data/lib/rubocop/cop/rspec/rails/http_status.rb +89 -33
- data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +4 -4
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +5 -5
- 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/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 +2 -2
- data/lib/rubocop/cop/rspec/variable_definition.rb +3 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +4 -1
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +3 -3
- data/lib/rubocop/cop/rspec_cops.rb +7 -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 +1 -0
- metadata +11 -3
@@ -9,6 +9,7 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# expect(response.status).to be(200)
|
12
|
+
# expect(response.code).to eq("200")
|
12
13
|
#
|
13
14
|
# # good
|
14
15
|
# expect(response).to have_http_status(200)
|
@@ -18,7 +19,7 @@ module RuboCop
|
|
18
19
|
|
19
20
|
MSG =
|
20
21
|
'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
|
21
|
-
'over
|
22
|
+
'over `%<bad_code>s`.'
|
22
23
|
|
23
24
|
RUNNERS = %i[to to_not not_to].to_set
|
24
25
|
RESTRICT_ON_SEND = RUNNERS
|
@@ -27,19 +28,21 @@ module RuboCop
|
|
27
28
|
def_node_matcher :match_status, <<-PATTERN
|
28
29
|
(send
|
29
30
|
(send nil? :expect
|
30
|
-
$(send (send nil? :response) :status)
|
31
|
+
$(send (send nil? :response) {:status :code})
|
31
32
|
)
|
32
33
|
$RUNNERS
|
33
|
-
$(send nil? {:be :eq :eql :equal} (int $_))
|
34
|
+
$(send nil? {:be :eq :eql :equal} ({int str} $_))
|
34
35
|
)
|
35
36
|
PATTERN
|
36
37
|
|
37
38
|
def on_send(node)
|
38
39
|
match_status(node) do |response_status, to, match, status|
|
39
|
-
message = format(MSG, to: to,
|
40
|
+
message = format(MSG, to: to, status: status,
|
41
|
+
bad_code: node.source)
|
40
42
|
add_offense(node, message: message) do |corrector|
|
41
|
-
corrector.replace(response_status
|
43
|
+
corrector.replace(response_status, 'response')
|
42
44
|
corrector.replace(match.loc.selector, 'have_http_status')
|
45
|
+
corrector.replace(match.first_argument, status.to_i.to_s)
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|
@@ -8,6 +8,11 @@ module RuboCop
|
|
8
8
|
module Rails
|
9
9
|
# Enforces use of symbolic or numeric value to describe HTTP status.
|
10
10
|
#
|
11
|
+
# This cop inspects only `have_http_status` calls.
|
12
|
+
# So, this cop does not check if a method starting with `be_*` is used
|
13
|
+
# when setting for `EnforcedStyle: symbolic` or
|
14
|
+
# `EnforcedStyle: numeric`.
|
15
|
+
#
|
11
16
|
# @example `EnforcedStyle: symbolic` (default)
|
12
17
|
# # bad
|
13
18
|
# it { is_expected.to have_http_status 200 }
|
@@ -30,6 +35,19 @@ module RuboCop
|
|
30
35
|
# it { is_expected.to have_http_status :success }
|
31
36
|
# it { is_expected.to have_http_status :error }
|
32
37
|
#
|
38
|
+
# @example `EnforcedStyle: be_status`
|
39
|
+
# # bad
|
40
|
+
# it { is_expected.to have_http_status :ok }
|
41
|
+
# it { is_expected.to have_http_status :not_found }
|
42
|
+
# it { is_expected.to have_http_status 200 }
|
43
|
+
# it { is_expected.to have_http_status 404 }
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# it { is_expected.to be_ok }
|
47
|
+
# it { is_expected.to be_not_found }
|
48
|
+
# it { is_expected.to have_http_status :success }
|
49
|
+
# it { is_expected.to have_http_status :error }
|
50
|
+
#
|
33
51
|
class HttpStatus < Base
|
34
52
|
extend AutoCorrector
|
35
53
|
include ConfigurableEnforcedStyle
|
@@ -41,12 +59,13 @@ module RuboCop
|
|
41
59
|
PATTERN
|
42
60
|
|
43
61
|
def on_send(node)
|
44
|
-
http_status(node) do |
|
45
|
-
checker = checker_class.new(
|
62
|
+
http_status(node) do |arg|
|
63
|
+
checker = checker_class.new(arg)
|
46
64
|
return unless checker.offensive?
|
47
65
|
|
48
|
-
add_offense(checker.
|
49
|
-
|
66
|
+
add_offense(checker.offense_range,
|
67
|
+
message: checker.message) do |corrector|
|
68
|
+
corrector.replace(checker.offense_range, checker.prefer)
|
50
69
|
end
|
51
70
|
end
|
52
71
|
end
|
@@ -59,13 +78,16 @@ module RuboCop
|
|
59
78
|
SymbolicStyleChecker
|
60
79
|
when :numeric
|
61
80
|
NumericStyleChecker
|
81
|
+
when :be_status
|
82
|
+
BeStatusStyleChecker
|
62
83
|
end
|
63
84
|
end
|
64
85
|
|
65
86
|
# :nodoc:
|
66
|
-
class
|
87
|
+
class StyleCheckerBase
|
67
88
|
MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
|
68
89
|
'to describe HTTP status code.'
|
90
|
+
ALLOWED_STATUSES = %i[error success missing redirect].freeze
|
69
91
|
|
70
92
|
attr_reader :node
|
71
93
|
|
@@ -73,16 +95,36 @@ module RuboCop
|
|
73
95
|
@node = node
|
74
96
|
end
|
75
97
|
|
98
|
+
def message
|
99
|
+
format(MSG, prefer: prefer, current: current)
|
100
|
+
end
|
101
|
+
|
102
|
+
def offense_range
|
103
|
+
node
|
104
|
+
end
|
105
|
+
|
106
|
+
def allowed_symbol?
|
107
|
+
node.sym_type? && ALLOWED_STATUSES.include?(node.value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def custom_http_status_code?
|
111
|
+
node.int_type? &&
|
112
|
+
!::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# :nodoc:
|
117
|
+
class SymbolicStyleChecker < StyleCheckerBase
|
76
118
|
def offensive?
|
77
119
|
!node.sym_type? && !custom_http_status_code?
|
78
120
|
end
|
79
121
|
|
80
|
-
def
|
81
|
-
|
122
|
+
def prefer
|
123
|
+
symbol.inspect
|
82
124
|
end
|
83
125
|
|
84
|
-
def
|
85
|
-
|
126
|
+
def current
|
127
|
+
number.inspect
|
86
128
|
end
|
87
129
|
|
88
130
|
private
|
@@ -94,50 +136,64 @@ module RuboCop
|
|
94
136
|
def number
|
95
137
|
node.source.to_i
|
96
138
|
end
|
97
|
-
|
98
|
-
def custom_http_status_code?
|
99
|
-
node.int_type? &&
|
100
|
-
!::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(node.source.to_i)
|
101
|
-
end
|
102
139
|
end
|
103
140
|
|
104
141
|
# :nodoc:
|
105
|
-
class NumericStyleChecker
|
106
|
-
MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
|
107
|
-
'to describe HTTP status code.'
|
108
|
-
|
109
|
-
ALLOWED_STATUSES = %i[error success missing redirect].freeze
|
110
|
-
|
111
|
-
attr_reader :node
|
112
|
-
|
113
|
-
def initialize(node)
|
114
|
-
@node = node
|
115
|
-
end
|
116
|
-
|
142
|
+
class NumericStyleChecker < StyleCheckerBase
|
117
143
|
def offensive?
|
118
144
|
!node.int_type? && !allowed_symbol?
|
119
145
|
end
|
120
146
|
|
121
|
-
def
|
122
|
-
|
147
|
+
def prefer
|
148
|
+
number.to_s
|
123
149
|
end
|
124
150
|
|
125
|
-
def
|
126
|
-
|
151
|
+
def current
|
152
|
+
symbol.inspect
|
127
153
|
end
|
128
154
|
|
129
155
|
private
|
130
156
|
|
157
|
+
def symbol
|
158
|
+
node.value
|
159
|
+
end
|
160
|
+
|
131
161
|
def number
|
132
162
|
::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
|
133
163
|
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# :nodoc:
|
167
|
+
class BeStatusStyleChecker < StyleCheckerBase
|
168
|
+
def offensive?
|
169
|
+
(!node.sym_type? && !custom_http_status_code?) ||
|
170
|
+
(!node.int_type? && !allowed_symbol?)
|
171
|
+
end
|
172
|
+
|
173
|
+
def offense_range
|
174
|
+
node.parent
|
175
|
+
end
|
176
|
+
|
177
|
+
def prefer
|
178
|
+
if node.sym_type?
|
179
|
+
"be_#{node.value}"
|
180
|
+
else
|
181
|
+
"be_#{symbol}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def current
|
186
|
+
offense_range.source
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
134
190
|
|
135
191
|
def symbol
|
136
|
-
|
192
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
137
193
|
end
|
138
194
|
|
139
|
-
def
|
140
|
-
node.
|
195
|
+
def number
|
196
|
+
node.source.to_i
|
141
197
|
end
|
142
198
|
end
|
143
199
|
end
|
@@ -96,12 +96,12 @@ module RuboCop
|
|
96
96
|
# @return [Parser::Source::Range]
|
97
97
|
def remove_range(node)
|
98
98
|
if node.left_sibling
|
99
|
-
node.
|
100
|
-
begin_pos: node.left_sibling.
|
99
|
+
node.source_range.with(
|
100
|
+
begin_pos: node.left_sibling.source_range.end_pos
|
101
101
|
)
|
102
102
|
elsif node.right_sibling
|
103
|
-
node.
|
104
|
-
end_pos: node.right_sibling.
|
103
|
+
node.source_range.with(
|
104
|
+
end_pos: node.right_sibling.source_range.begin_pos
|
105
105
|
)
|
106
106
|
end
|
107
107
|
end
|
@@ -13,9 +13,9 @@ module RuboCop
|
|
13
13
|
# refute_equal(a, b)
|
14
14
|
#
|
15
15
|
# # good
|
16
|
-
# expect(
|
17
|
-
# expect(
|
18
|
-
# expect(
|
16
|
+
# expect(b).to eq(a)
|
17
|
+
# expect(b).to(eq(a), "must be equal")
|
18
|
+
# expect(b).not_to eq(a)
|
19
19
|
#
|
20
20
|
class MinitestAssertions < Base
|
21
21
|
extend AutoCorrector
|
@@ -43,9 +43,9 @@ module RuboCop
|
|
43
43
|
def replacement(node, expected, actual, failure_message)
|
44
44
|
runner = node.method?(:assert_equal) ? 'to' : 'not_to'
|
45
45
|
if failure_message.nil?
|
46
|
-
"expect(#{
|
46
|
+
"expect(#{actual.source}).#{runner} eq(#{expected.source})"
|
47
47
|
else
|
48
|
-
"expect(#{
|
48
|
+
"expect(#{actual.source}).#{runner}(eq(#{expected.source}), " \
|
49
49
|
"#{failure_message.source})"
|
50
50
|
end
|
51
51
|
end
|
@@ -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,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
|