rubocop-rspec 1.42.0 → 1.43.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 +17 -0
- data/config/default.yml +12 -2
- data/lib/rubocop-rspec.rb +2 -1
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +1 -1
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +1 -1
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
- data/lib/rubocop/cop/rspec/around_block.rb +1 -1
- data/lib/rubocop/cop/rspec/base.rb +74 -0
- data/lib/rubocop/cop/rspec/be.rb +1 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
- data/lib/rubocop/cop/rspec/before_after_all.rb +1 -1
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +6 -3
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/context_method.rb +2 -2
- data/lib/rubocop/cop/rspec/context_wording.rb +3 -3
- data/lib/rubocop/cop/rspec/cop.rb +2 -66
- data/lib/rubocop/cop/rspec/describe_class.rb +21 -30
- data/lib/rubocop/cop/rspec/describe_method.rb +14 -6
- data/lib/rubocop/cop/rspec/describe_symbol.rb +2 -2
- data/lib/rubocop/cop/rspec/described_class.rb +2 -2
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -1
- data/lib/rubocop/cop/rspec/dialect.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_example_group.rb +91 -7
- data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +4 -8
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +4 -8
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +7 -10
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +4 -8
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +5 -8
- 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 +4 -4
- data/lib/rubocop/cop/rspec/expect_actual.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_change.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +3 -3
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +10 -6
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -1
- data/lib/rubocop/cop/rspec/file_path.rb +25 -17
- data/lib/rubocop/cop/rspec/focus.rb +7 -11
- data/lib/rubocop/cop/rspec/hook_argument.rb +5 -6
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -6
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/leading_subject.rb +20 -17
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/let_setup.rb +6 -3
- data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/message_spies.rb +1 -1
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_describes.rb +11 -8
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +7 -11
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +148 -0
- 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/nested_groups.rb +4 -4
- data/lib/rubocop/cop/rspec/not_to_not.rb +1 -1
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +7 -14
- data/lib/rubocop/cop/rspec/rails/http_status.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_never.rb +2 -2
- data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +2 -2
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +1 -1
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -1
- data/lib/rubocop/cop/rspec/variable_definition.rb +6 -6
- data/lib/rubocop/cop/rspec/variable_name.rb +28 -9
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
- data/lib/rubocop/cop/rspec/yield.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +1 -0
- data/lib/rubocop/rspec/corrector/move_node.rb +7 -5
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/{blank_line_separation.rb → empty_line_separation.rb} +13 -2
- data/lib/rubocop/rspec/example_group.rb +2 -2
- data/lib/rubocop/rspec/language.rb +6 -4
- data/lib/rubocop/rspec/language/node_pattern.rb +6 -1
- data/lib/rubocop/rspec/top_level_describe.rb +2 -2
- data/lib/rubocop/rspec/top_level_group.rb +24 -13
- data/lib/rubocop/rspec/variable.rb +1 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +23 -7
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
|
-
# Checks for multiple top
|
6
|
+
# Checks for multiple top-level example groups.
|
7
7
|
#
|
8
8
|
# Multiple descriptions for the same class or module should either
|
9
9
|
# be nested or separated into different test files.
|
@@ -22,17 +22,20 @@ module RuboCop
|
|
22
22
|
# describe '.do_something_else' do
|
23
23
|
# end
|
24
24
|
# end
|
25
|
-
class MultipleDescribes <
|
26
|
-
include RuboCop::RSpec::
|
25
|
+
class MultipleDescribes < Base
|
26
|
+
include RuboCop::RSpec::TopLevelGroup
|
27
27
|
|
28
|
-
MSG = 'Do not use multiple top
|
28
|
+
MSG = 'Do not use multiple top-level example groups - '\
|
29
29
|
'try to nest them.'
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
31
|
+
def on_top_level_group(node)
|
32
|
+
top_level_example_groups =
|
33
|
+
top_level_groups.select(&method(:example_group?))
|
34
34
|
|
35
|
-
|
35
|
+
return if top_level_example_groups.one?
|
36
|
+
return unless top_level_example_groups.first.equal?(node)
|
37
|
+
|
38
|
+
add_offense(node.send_node)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
@@ -45,22 +45,18 @@ module RuboCop
|
|
45
45
|
# end
|
46
46
|
# end
|
47
47
|
#
|
48
|
-
class MultipleExpectations <
|
48
|
+
class MultipleExpectations < Base
|
49
49
|
include ConfigurableMax
|
50
50
|
|
51
51
|
MSG = 'Example has too many expectations [%<total>d/%<max>d].'
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
(send _ _ <(sym :aggregate_failures) ...>)
|
56
|
-
(send _ _ ... (hash <(pair (sym :aggregate_failures) true) ...>))
|
57
|
-
} ...)
|
58
|
-
PATTERN
|
53
|
+
ANYTHING = ->(_node) { true }
|
54
|
+
TRUE = ->(node) { node.true_type? }
|
59
55
|
|
60
|
-
def_node_matcher :
|
56
|
+
def_node_matcher :aggregate_failures?, <<-PATTERN
|
61
57
|
(block {
|
62
58
|
(send _ _ <(sym :aggregate_failures) ...>)
|
63
|
-
(send _ _ ... (hash <(pair (sym :aggregate_failures)
|
59
|
+
(send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
|
64
60
|
} ...)
|
65
61
|
PATTERN
|
66
62
|
|
@@ -89,12 +85,12 @@ module RuboCop
|
|
89
85
|
node_with_aggregate_failures = find_aggregate_failures(example_node)
|
90
86
|
return false unless node_with_aggregate_failures
|
91
87
|
|
92
|
-
aggregate_failures?(node_with_aggregate_failures)
|
88
|
+
aggregate_failures?(node_with_aggregate_failures, TRUE)
|
93
89
|
end
|
94
90
|
|
95
91
|
def find_aggregate_failures(example_node)
|
96
92
|
example_node.send_node.each_ancestor(:block)
|
97
|
-
.find { |block_node|
|
93
|
+
.find { |block_node| aggregate_failures?(block_node, ANYTHING) }
|
98
94
|
end
|
99
95
|
|
100
96
|
def find_expectation(node, &block)
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks if example groups contain too many `let` and `subject` calls.
|
7
|
+
#
|
8
|
+
# This cop is configurable using the `Max` option and the `AllowSubject`
|
9
|
+
# which will configure the cop to only register offenses on calls to
|
10
|
+
# `let` and not calls to `subject`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# describe MyClass do
|
15
|
+
# let(:foo) { [] }
|
16
|
+
# let(:bar) { [] }
|
17
|
+
# let!(:baz) { [] }
|
18
|
+
# let(:qux) { [] }
|
19
|
+
# let(:quux) { [] }
|
20
|
+
# let(:quuz) { {} }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# describe MyClass do
|
24
|
+
# let(:foo) { [] }
|
25
|
+
# let(:bar) { [] }
|
26
|
+
# let!(:baz) { [] }
|
27
|
+
#
|
28
|
+
# context 'when stuff' do
|
29
|
+
# let(:qux) { [] }
|
30
|
+
# let(:quux) { [] }
|
31
|
+
# let(:quuz) { {} }
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# describe MyClass do
|
37
|
+
# let(:bar) { [] }
|
38
|
+
# let!(:baz) { [] }
|
39
|
+
# let(:qux) { [] }
|
40
|
+
# let(:quux) { [] }
|
41
|
+
# let(:quuz) { {} }
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# describe MyClass do
|
45
|
+
# context 'when stuff' do
|
46
|
+
# let(:foo) { [] }
|
47
|
+
# let(:bar) { [] }
|
48
|
+
# let!(:booger) { [] }
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# context 'when other stuff' do
|
52
|
+
# let(:qux) { [] }
|
53
|
+
# let(:quux) { [] }
|
54
|
+
# let(:quuz) { {} }
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# @example when disabling AllowSubject configuration
|
59
|
+
#
|
60
|
+
# # rubocop.yml
|
61
|
+
# # RSpec/MultipleMemoizedHelpers:
|
62
|
+
# # AllowSubject: false
|
63
|
+
#
|
64
|
+
# # bad - `subject` counts towards memoized helpers
|
65
|
+
# describe MyClass do
|
66
|
+
# subject { {} }
|
67
|
+
# let(:foo) { [] }
|
68
|
+
# let(:bar) { [] }
|
69
|
+
# let!(:baz) { [] }
|
70
|
+
# let(:qux) { [] }
|
71
|
+
# let(:quux) { [] }
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# @example with Max configuration
|
75
|
+
#
|
76
|
+
# # rubocop.yml
|
77
|
+
# # RSpec/MultipleMemoizedHelpers:
|
78
|
+
# # Max: 1
|
79
|
+
#
|
80
|
+
# # bad
|
81
|
+
# describe MyClass do
|
82
|
+
# let(:foo) { [] }
|
83
|
+
# let(:bar) { [] }
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
class MultipleMemoizedHelpers < Base
|
87
|
+
include ConfigurableMax
|
88
|
+
include RuboCop::RSpec::Variable
|
89
|
+
|
90
|
+
MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
|
91
|
+
|
92
|
+
def on_block(node)
|
93
|
+
return unless spec_group?(node)
|
94
|
+
|
95
|
+
count = all_helpers(node).uniq.count
|
96
|
+
|
97
|
+
return if count <= max
|
98
|
+
|
99
|
+
self.max = count
|
100
|
+
add_offense(node, message: format(MSG, count: count, max: max))
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_new_investigation
|
104
|
+
@example_group_memoized_helpers = {}
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
attr_reader :example_group_memoized_helpers
|
110
|
+
|
111
|
+
def all_helpers(node)
|
112
|
+
[
|
113
|
+
*helpers(node),
|
114
|
+
*node.each_ancestor(:block).flat_map(&method(:helpers))
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
def helpers(node)
|
119
|
+
@example_group_memoized_helpers[node] ||=
|
120
|
+
variable_nodes(node).map do |variable_node|
|
121
|
+
if variable_node.block_type?
|
122
|
+
variable_definition?(variable_node.send_node)
|
123
|
+
else # block-pass (`let(:foo, &bar)`)
|
124
|
+
variable_definition?(variable_node)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def variable_nodes(node)
|
130
|
+
example_group = RuboCop::RSpec::ExampleGroup.new(node)
|
131
|
+
if allow_subject?
|
132
|
+
example_group.lets
|
133
|
+
else
|
134
|
+
example_group.lets + example_group.subjects
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def max
|
139
|
+
cop_config['Max']
|
140
|
+
end
|
141
|
+
|
142
|
+
def allow_subject?
|
143
|
+
cop_config['AllowSubject']
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
# - If subjects are defined with `subject!` then we don't autocorrect.
|
34
34
|
# This is enough of an edge case that people can just move this to
|
35
35
|
# a `before` hook on their own
|
36
|
-
class MultipleSubjects <
|
36
|
+
class MultipleSubjects < Base
|
37
37
|
extend AutoCorrector
|
38
38
|
include RangeHelp
|
39
39
|
|
@@ -85,9 +85,9 @@ module RuboCop
|
|
85
85
|
# end
|
86
86
|
# end
|
87
87
|
#
|
88
|
-
class NestedGroups <
|
88
|
+
class NestedGroups < Base
|
89
89
|
include ConfigurableMax
|
90
|
-
include RuboCop::RSpec::
|
90
|
+
include RuboCop::RSpec::TopLevelGroup
|
91
91
|
|
92
92
|
MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
|
93
93
|
|
@@ -97,8 +97,8 @@ module RuboCop
|
|
97
97
|
"Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \
|
98
98
|
'deprecated in favor of `Max`. Please use that instead.'
|
99
99
|
|
100
|
-
def
|
101
|
-
find_nested_example_groups(node
|
100
|
+
def on_top_level_group(node)
|
101
|
+
find_nested_example_groups(node) do |example_group, nesting|
|
102
102
|
self.max = nesting
|
103
103
|
add_offense(
|
104
104
|
example_group.send_node,
|
@@ -21,7 +21,7 @@ module RuboCop
|
|
21
21
|
# let(:foo) { bar }
|
22
22
|
# let(:baz) { baz }
|
23
23
|
# let!(:other) { other }
|
24
|
-
class OverwritingSetup <
|
24
|
+
class OverwritingSetup < Base
|
25
25
|
MSG = '`%<name>s` is already defined.'
|
26
26
|
|
27
27
|
def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern
|
@@ -208,18 +208,20 @@ module RuboCop
|
|
208
208
|
'is_a?'
|
209
209
|
when 'be_an_instance_of', 'be_instance_of', 'an_instance_of'
|
210
210
|
'instance_of?'
|
211
|
-
when 'include'
|
212
|
-
|
211
|
+
when 'include'
|
212
|
+
'include?'
|
213
|
+
when 'respond_to'
|
214
|
+
'respond_to?'
|
213
215
|
when /^have_(.+)/
|
214
216
|
"has_#{Regexp.last_match(1)}?"
|
215
217
|
else
|
216
|
-
matcher[/^be_(.+)/, 1]
|
218
|
+
"#{matcher[/^be_(.+)/, 1]}?"
|
217
219
|
end
|
218
220
|
end
|
219
221
|
# rubocop:enable Metrics/MethodLength
|
220
222
|
|
221
223
|
def replacement_matcher(node)
|
222
|
-
case [cop_config['Strict'], node.
|
224
|
+
case [cop_config['Strict'], node.method?(:to)]
|
223
225
|
when [true, true]
|
224
226
|
'be(true)'
|
225
227
|
when [true, false]
|
@@ -269,7 +271,7 @@ module RuboCop
|
|
269
271
|
#
|
270
272
|
# # good - the above code is rewritten to it by this cop
|
271
273
|
# expect(foo.something?).to be_truthy
|
272
|
-
class PredicateMatcher <
|
274
|
+
class PredicateMatcher < Base
|
273
275
|
extend AutoCorrector
|
274
276
|
include ConfigurableEnforcedStyle
|
275
277
|
include InflectedHelper
|
@@ -288,15 +290,6 @@ module RuboCop
|
|
288
290
|
check_explicit(node) if style == :explicit
|
289
291
|
end
|
290
292
|
|
291
|
-
def autocorrect(node)
|
292
|
-
case style
|
293
|
-
when :inflected
|
294
|
-
autocorrect_inflected(node)
|
295
|
-
when :explicit
|
296
|
-
autocorrect_explicit(node)
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
293
|
private
|
301
294
|
|
302
295
|
# returns args location with whitespace
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
# expect(foo).to receive(:bar).at_most(:once)
|
24
24
|
# expect(foo).to receive(:bar).at_most(:twice).times
|
25
25
|
#
|
26
|
-
class ReceiveCounts <
|
26
|
+
class ReceiveCounts < Base
|
27
27
|
extend AutoCorrector
|
28
28
|
|
29
29
|
MSG = 'Use `%<alternative>s` instead of `%<original>s`.'
|
@@ -13,14 +13,14 @@ module RuboCop
|
|
13
13
|
# # good
|
14
14
|
# expect(foo).not_to receive(:bar)
|
15
15
|
#
|
16
|
-
class ReceiveNever <
|
16
|
+
class ReceiveNever < Base
|
17
17
|
extend AutoCorrector
|
18
18
|
MSG = 'Use `not_to receive` instead of `never`.'
|
19
19
|
|
20
20
|
def_node_search :method_on_stub?, '(send nil? :receive ...)'
|
21
21
|
|
22
22
|
def on_send(node)
|
23
|
-
return unless node.
|
23
|
+
return unless node.method?(:never) && method_on_stub?(node)
|
24
24
|
|
25
25
|
add_offense(node.loc.selector) do |corrector|
|
26
26
|
autocorrect(corrector, node)
|
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
# expect(user).to be_valid
|
16
16
|
# end
|
17
17
|
#
|
18
|
-
class RepeatedExample <
|
18
|
+
class RepeatedExample < Base
|
19
19
|
MSG = "Don't repeat examples within an example group."
|
20
20
|
|
21
21
|
def on_block(node)
|
@@ -41,7 +41,7 @@ module RuboCop
|
|
41
41
|
def example_signature(example)
|
42
42
|
key_parts = [example.metadata, example.implementation]
|
43
43
|
|
44
|
-
if example.definition.
|
44
|
+
if example.definition.method?(:its)
|
45
45
|
key_parts << example.definition.arguments
|
46
46
|
end
|
47
47
|
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
# it { is_expected.to respond_to :each }
|
44
44
|
# end
|
45
45
|
#
|
46
|
-
class RepeatedExampleGroupBody <
|
46
|
+
class RepeatedExampleGroupBody < Base
|
47
47
|
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
|
48
48
|
|
49
49
|
def_node_matcher :several_example_groups?, <<-PATTERN
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
# # example group
|
44
44
|
# end
|
45
45
|
#
|
46
|
-
class RepeatedExampleGroupDescription <
|
46
|
+
class RepeatedExampleGroupDescription < Base
|
47
47
|
MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
|
48
48
|
|
49
49
|
def_node_matcher :several_example_groups?, <<-PATTERN
|