rubocop-rspec 2.22.0 → 2.27.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 +94 -9
- data/README.md +1 -1
- data/config/default.yml +126 -21
- data/lib/rubocop/cop/rspec/around_block.rb +3 -3
- data/lib/rubocop/cop/rspec/be.rb +1 -1
- data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
- data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
- data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
- data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -2
- data/lib/rubocop/cop/rspec/change_by_zero.rb +30 -4
- data/lib/rubocop/cop/rspec/context_method.rb +2 -2
- data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
- data/lib/rubocop/cop/rspec/described_class.rb +33 -11
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_example_group.rb +4 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
- 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_length.rb +11 -5
- data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
- data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
- data/lib/rubocop/cop/rspec/expect_actual.rb +7 -4
- data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
- data/lib/rubocop/cop/rspec/file_path.rb +6 -0
- data/lib/rubocop/cop/rspec/focus.rb +17 -2
- data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
- data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
- data/lib/rubocop/cop/rspec/indexed_let.rb +32 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
- data/lib/rubocop/cop/rspec/instance_variable.rb +3 -3
- data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +5 -1
- data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/message_expectation.rb +1 -2
- data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
- data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +12 -7
- data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/pending.rb +12 -2
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +9 -9
- data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/rails/have_http_status.rb +34 -10
- data/lib/rubocop/cop/rspec/rails/http_status.rb +29 -18
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +314 -22
- data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -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_predicate_matcher.rb +67 -0
- data/lib/rubocop/cop/rspec/remove_const.rb +40 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_subject_call.rb +124 -0
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -3
- data/lib/rubocop/cop/rspec/sort_metadata.rb +2 -1
- 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 +4 -4
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
- data/lib/rubocop/cop/rspec/variable_definition.rb +4 -4
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +6 -6
- data/lib/rubocop/cop/rspec/verified_doubles.rb +2 -2
- data/lib/rubocop/cop/rspec/void_expect.rb +4 -3
- data/lib/rubocop/cop/rspec_cops.rb +11 -0
- data/lib/rubocop/rspec/language.rb +8 -8
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop/rspec/wording.rb +8 -0
- data/lib/rubocop-rspec.rb +1 -0
- metadata +20 -8
@@ -34,6 +34,18 @@ module RuboCop
|
|
34
34
|
# # good
|
35
35
|
# describe 'test' do; end
|
36
36
|
#
|
37
|
+
# # bad
|
38
|
+
# shared_examples 'test', focus: true do; end
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# shared_examples 'test' do; end
|
42
|
+
#
|
43
|
+
# # bad
|
44
|
+
# shared_context 'test', focus: true do; end
|
45
|
+
#
|
46
|
+
# # good
|
47
|
+
# shared_context 'test' do; end
|
48
|
+
#
|
37
49
|
# # bad (does not support autocorrection)
|
38
50
|
# focus 'test' do; end
|
39
51
|
#
|
@@ -44,18 +56,19 @@ module RuboCop
|
|
44
56
|
MSG = 'Focused spec found.'
|
45
57
|
|
46
58
|
# @!method focusable_selector?(node)
|
47
|
-
def_node_matcher :focusable_selector?,
|
59
|
+
def_node_matcher :focusable_selector?, <<~PATTERN
|
48
60
|
{
|
49
61
|
#ExampleGroups.regular
|
50
62
|
#ExampleGroups.skipped
|
51
63
|
#Examples.regular
|
52
64
|
#Examples.skipped
|
53
65
|
#Examples.pending
|
66
|
+
#SharedGroups.all
|
54
67
|
}
|
55
68
|
PATTERN
|
56
69
|
|
57
70
|
# @!method metadata(node)
|
58
|
-
def_node_matcher :metadata,
|
71
|
+
def_node_matcher :metadata, <<~PATTERN
|
59
72
|
{(send #rspec? #focusable_selector? <$(sym :focus) ...>)
|
60
73
|
(send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
|
61
74
|
PATTERN
|
@@ -66,6 +79,8 @@ module RuboCop
|
|
66
79
|
PATTERN
|
67
80
|
|
68
81
|
def on_send(node)
|
82
|
+
return if node.chained? || node.each_ancestor(:def, :defs).any?
|
83
|
+
|
69
84
|
focus_metadata(node) do |focus|
|
70
85
|
add_offense(focus) do |corrector|
|
71
86
|
if focus.pair_type? || focus.str_type? || focus.sym_type?
|
@@ -66,12 +66,12 @@ module RuboCop
|
|
66
66
|
EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
|
67
67
|
|
68
68
|
# @!method scoped_hook(node)
|
69
|
-
def_node_matcher :scoped_hook,
|
69
|
+
def_node_matcher :scoped_hook, <<~PATTERN
|
70
70
|
({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
|
71
71
|
PATTERN
|
72
72
|
|
73
73
|
# @!method unscoped_hook(node)
|
74
|
-
def_node_matcher :unscoped_hook,
|
74
|
+
def_node_matcher :unscoped_hook, <<~PATTERN
|
75
75
|
({block numblock} $(send _ #Hooks.all) ...)
|
76
76
|
PATTERN
|
77
77
|
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
MSG = 'Move `%<hook>s` above the examples in the group.'
|
29
29
|
|
30
30
|
# @!method example_or_group?(node)
|
31
|
-
def_node_matcher :example_or_group?,
|
31
|
+
def_node_matcher :example_or_group?, <<~PATTERN
|
32
32
|
{
|
33
33
|
({block numblock} {
|
34
34
|
(send #rspec? #ExampleGroups.all ...)
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
22
22
|
RESTRICT_ON_SEND = %i[is_expected should should_not].freeze
|
23
23
|
|
24
24
|
# @!method lambda?(node)
|
25
|
-
def_node_matcher :lambda?,
|
25
|
+
def_node_matcher :lambda?, <<~PATTERN
|
26
26
|
{
|
27
27
|
(send (const nil? :Proc) :new)
|
28
28
|
(send nil? {:proc :lambda})
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def_node_matcher :lambda_subject?, '(block #lambda? ...)'
|
34
34
|
|
35
35
|
# @!method implicit_expect(node)
|
36
|
-
def_node_matcher :implicit_expect,
|
36
|
+
def_node_matcher :implicit_expect, <<~PATTERN
|
37
37
|
$(send nil? {:is_expected :should :should_not} ...)
|
38
38
|
PATTERN
|
39
39
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
RESTRICT_ON_SEND = Runners.all + %i[should should_not]
|
32
32
|
|
33
33
|
# @!method implicit_expect(node)
|
34
|
-
def_node_matcher :implicit_expect,
|
34
|
+
def_node_matcher :implicit_expect, <<~PATTERN
|
35
35
|
{
|
36
36
|
(send nil? ${:should :should_not} ...)
|
37
37
|
(send (send nil? $:is_expected) #Runners.all ...)
|
@@ -78,12 +78,12 @@ module RuboCop
|
|
78
78
|
].freeze
|
79
79
|
|
80
80
|
# @!method explicit_unnamed_subject?(node)
|
81
|
-
def_node_matcher :explicit_unnamed_subject?,
|
81
|
+
def_node_matcher :explicit_unnamed_subject?, <<~PATTERN
|
82
82
|
(send nil? :expect (send nil? :subject))
|
83
83
|
PATTERN
|
84
84
|
|
85
85
|
# @!method implicit_subject?(node)
|
86
|
-
def_node_matcher :implicit_subject?,
|
86
|
+
def_node_matcher :implicit_subject?, <<~PATTERN
|
87
87
|
(send nil? {:should :should_not :is_expected} ...)
|
88
88
|
PATTERN
|
89
89
|
|
@@ -8,6 +8,9 @@ module RuboCop
|
|
8
8
|
# It makes reading the test harder because it's not clear what exactly
|
9
9
|
# is tested by this particular example.
|
10
10
|
#
|
11
|
+
# The configurable options `AllowedIdentifiers` and `AllowedPatterns`
|
12
|
+
# will also read those set in `Naming/VariableNumber`.
|
13
|
+
#
|
11
14
|
# @example `Max: 1 (default)`
|
12
15
|
# # bad
|
13
16
|
# let(:item_1) { create(:item) }
|
@@ -31,7 +34,20 @@ module RuboCop
|
|
31
34
|
# let(:item_1) { create(:item) }
|
32
35
|
# let(:item_2) { create(:item) }
|
33
36
|
#
|
37
|
+
# @example `AllowedIdentifiers: ['item_1', 'item_2']`
|
38
|
+
# # good
|
39
|
+
# let(:item_1) { create(:item) }
|
40
|
+
# let(:item_2) { create(:item) }
|
41
|
+
#
|
42
|
+
# @example `AllowedPatterns: ['item']`
|
43
|
+
# # good
|
44
|
+
# let(:item_1) { create(:item) }
|
45
|
+
# let(:item_2) { create(:item) }
|
46
|
+
#
|
34
47
|
class IndexedLet < Base
|
48
|
+
include AllowedIdentifiers
|
49
|
+
include AllowedPattern
|
50
|
+
|
35
51
|
MSG = 'This `let` statement uses index in its name. Please give it ' \
|
36
52
|
'a meaningful name.'
|
37
53
|
|
@@ -69,12 +85,27 @@ module RuboCop
|
|
69
85
|
end
|
70
86
|
|
71
87
|
def indexed_let?(node)
|
72
|
-
let?(node) &&
|
88
|
+
let?(node) &&
|
89
|
+
SUFFIX_INDEX_REGEX.match?(let_name(node)) &&
|
90
|
+
!allowed_identifier?(let_name(node).to_s) &&
|
91
|
+
!matches_allowed_pattern?(let_name(node).to_s)
|
73
92
|
end
|
74
93
|
|
75
94
|
def let_name_stripped_index(node)
|
76
95
|
let_name(node).to_s.gsub(INDEX_REGEX, '')
|
77
96
|
end
|
97
|
+
|
98
|
+
def cop_config_patterns_values
|
99
|
+
Array(config.for_cop('Naming/VariableNumber')
|
100
|
+
.fetch('AllowedPatterns', [])) +
|
101
|
+
Array(cop_config.fetch('AllowedPatterns', []))
|
102
|
+
end
|
103
|
+
|
104
|
+
def allowed_identifiers
|
105
|
+
Array(config.for_cop('Naming/VariableNumber')
|
106
|
+
.fetch('AllowedIdentifiers', [])) +
|
107
|
+
Array(cop_config.fetch('AllowedIdentifiers', []))
|
108
|
+
end
|
78
109
|
end
|
79
110
|
end
|
80
111
|
end
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
'with `have_received`.'
|
26
26
|
|
27
27
|
# @!method null_double(node)
|
28
|
-
def_node_search :null_double,
|
28
|
+
def_node_search :null_double, <<~PATTERN
|
29
29
|
(lvasgn $_
|
30
30
|
(send
|
31
31
|
$(send nil? :instance_double
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
PATTERN
|
34
34
|
|
35
35
|
# @!method have_received_usage(node)
|
36
|
-
def_node_search :have_received_usage,
|
36
|
+
def_node_search :have_received_usage, <<~PATTERN
|
37
37
|
(send
|
38
38
|
(send nil? :expect
|
39
39
|
(lvar $_)) :to
|
@@ -48,16 +48,16 @@ module RuboCop
|
|
48
48
|
class InstanceVariable < Base
|
49
49
|
include TopLevelGroup
|
50
50
|
|
51
|
-
MSG = 'Avoid instance variables
|
51
|
+
MSG = 'Avoid instance variables - use let, ' \
|
52
52
|
'a method call, or a local variable (if possible).'
|
53
53
|
|
54
54
|
# @!method dynamic_class?(node)
|
55
|
-
def_node_matcher :dynamic_class?,
|
55
|
+
def_node_matcher :dynamic_class?, <<~PATTERN
|
56
56
|
(block (send (const nil? :Class) :new ...) ...)
|
57
57
|
PATTERN
|
58
58
|
|
59
59
|
# @!method custom_matcher?(node)
|
60
|
-
def_node_matcher :custom_matcher?,
|
60
|
+
def_node_matcher :custom_matcher?, <<~PATTERN
|
61
61
|
(block {
|
62
62
|
(send nil? :matcher sym)
|
63
63
|
(send (const (const nil? :RSpec) :Matchers) :define sym)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Check for `specify` with `is_expected` and one-liner expectations.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# specify { is_expected.to be_truthy }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# it { is_expected.to be_truthy }
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# specify do
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# specify { expect(sqrt(4)).to eq(2) }
|
20
|
+
#
|
21
|
+
class IsExpectedSpecify < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
RESTRICT_ON_SEND = %i[specify].freeze
|
25
|
+
IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
|
26
|
+
MSG = 'Use `it` instead of `specify`.'
|
27
|
+
|
28
|
+
# @!method offense?(node)
|
29
|
+
def_node_matcher :offense?, <<~PATTERN
|
30
|
+
(block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
block_node = node.parent
|
35
|
+
return unless block_node&.single_line? && offense?(block_node)
|
36
|
+
|
37
|
+
selector = node.loc.selector
|
38
|
+
add_offense(selector) do |corrector|
|
39
|
+
corrector.replace(selector, 'it')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -21,7 +21,7 @@ module RuboCop
|
|
21
21
|
'of iterating over an array.'
|
22
22
|
|
23
23
|
# @!method each?(node)
|
24
|
-
def_node_matcher :each?,
|
24
|
+
def_node_matcher :each?, <<~PATTERN
|
25
25
|
(block
|
26
26
|
(send ... :each)
|
27
27
|
(args (arg $_))
|
@@ -30,14 +30,14 @@ module RuboCop
|
|
30
30
|
PATTERN
|
31
31
|
|
32
32
|
# @!method each_numblock?(node)
|
33
|
-
def_node_matcher :each_numblock?,
|
33
|
+
def_node_matcher :each_numblock?, <<~PATTERN
|
34
34
|
(numblock
|
35
35
|
(send ... :each) _ $(...)
|
36
36
|
)
|
37
37
|
PATTERN
|
38
38
|
|
39
39
|
# @!method expectation?(node)
|
40
|
-
def_node_matcher :expectation?,
|
40
|
+
def_node_matcher :expectation?, <<~PATTERN
|
41
41
|
(send (send nil? :expect (lvar %)) :to ...)
|
42
42
|
PATTERN
|
43
43
|
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
# Anonymous classes are fine, since they don't result in global
|
18
18
|
# namespace name clashes.
|
19
19
|
#
|
20
|
-
# @see https://
|
20
|
+
# @see https://rspec.info/features/3-12/rspec-mocks/mutating-constants
|
21
21
|
#
|
22
22
|
# @example Constants leak between examples
|
23
23
|
# # bad
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
MSG = 'Move `let` before the examples in the group.'
|
37
37
|
|
38
38
|
# @!method example_or_group?(node)
|
39
|
-
def_node_matcher :example_or_group?,
|
39
|
+
def_node_matcher :example_or_group?, <<~PATTERN
|
40
40
|
{
|
41
41
|
(block (send nil? {#ExampleGroups.all #Examples.all} ...) ...)
|
42
42
|
(send nil? #Includes.examples ...)
|
@@ -51,6 +51,10 @@ module RuboCop
|
|
51
51
|
}
|
52
52
|
PATTERN
|
53
53
|
|
54
|
+
def self.autocorrect_incompatible_with
|
55
|
+
[RSpec::ScatteredLet]
|
56
|
+
end
|
57
|
+
|
54
58
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
55
59
|
return unless example_group_with_body?(node)
|
56
60
|
|
@@ -29,11 +29,10 @@ module RuboCop
|
|
29
29
|
|
30
30
|
MSG = 'Prefer `%<style>s` for setting message expectations.'
|
31
31
|
|
32
|
-
SUPPORTED_STYLES = %w[allow expect].freeze
|
33
32
|
RESTRICT_ON_SEND = %i[to].freeze
|
34
33
|
|
35
34
|
# @!method message_expectation(node)
|
36
|
-
def_node_matcher :message_expectation,
|
35
|
+
def_node_matcher :message_expectation, <<~PATTERN
|
37
36
|
(send $(send nil? {:expect :allow} ...) :to #receive_message?)
|
38
37
|
PATTERN
|
39
38
|
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Use consistent metadata style.
|
7
|
+
#
|
8
|
+
# This cop does not support autocorrection in the case of
|
9
|
+
# `EnforcedStyle: hash` where the trailing metadata type is ambiguous.
|
10
|
+
# (e.g. `describe 'Something', :a, b`)
|
11
|
+
#
|
12
|
+
# @example EnforcedStyle: symbol (default)
|
13
|
+
# # bad
|
14
|
+
# describe 'Something', a: true
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# describe 'Something', :a
|
18
|
+
#
|
19
|
+
# @example EnforcedStyle: hash
|
20
|
+
# # bad
|
21
|
+
# describe 'Something', :a
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# describe 'Something', a: true
|
25
|
+
class MetadataStyle < Base # rubocop:disable Metrics/ClassLength
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
include ConfigurableEnforcedStyle
|
29
|
+
include Metadata
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
# @!method extract_metadata_hash(node)
|
33
|
+
def_node_matcher :extract_metadata_hash, <<~PATTERN
|
34
|
+
(send _ _ _ ... $hash)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
# @!method match_boolean_metadata_pair?(node)
|
38
|
+
def_node_matcher :match_boolean_metadata_pair?, <<~PATTERN
|
39
|
+
(pair sym true)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
# @!method match_ambiguous_trailing_metadata?(node)
|
43
|
+
def_node_matcher :match_ambiguous_trailing_metadata?, <<~PATTERN
|
44
|
+
(send _ _ _ ... !{hash sym})
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
def on_metadata(symbols, hash)
|
48
|
+
# RSpec example groups accept two string arguments. In such a case,
|
49
|
+
# the rspec_metadata matcher will interpret the second string
|
50
|
+
# argument as a metadata symbol.
|
51
|
+
symbols.shift if symbols.first&.str_type?
|
52
|
+
|
53
|
+
symbols.each do |symbol|
|
54
|
+
on_metadata_symbol(symbol)
|
55
|
+
end
|
56
|
+
|
57
|
+
return unless hash
|
58
|
+
|
59
|
+
hash.pairs.each do |pair|
|
60
|
+
on_metadata_pair(pair)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def autocorrect_pair(corrector, node)
|
67
|
+
remove_pair(corrector, node)
|
68
|
+
insert_symbol(corrector, node)
|
69
|
+
end
|
70
|
+
|
71
|
+
def autocorrect_symbol(corrector, node)
|
72
|
+
return if match_ambiguous_trailing_metadata?(node.parent)
|
73
|
+
|
74
|
+
remove_symbol(corrector, node)
|
75
|
+
insert_pair(corrector, node)
|
76
|
+
end
|
77
|
+
|
78
|
+
def bad_metadata_pair?(node)
|
79
|
+
style == :symbol && match_boolean_metadata_pair?(node)
|
80
|
+
end
|
81
|
+
|
82
|
+
def bad_metadata_symbol?(_node)
|
83
|
+
style == :hash
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_symbol_to_pair_source(node)
|
87
|
+
"#{node.value}: true"
|
88
|
+
end
|
89
|
+
|
90
|
+
def insert_pair(corrector, node)
|
91
|
+
hash_node = extract_metadata_hash(node.parent)
|
92
|
+
if hash_node.nil?
|
93
|
+
insert_pair_as_last_argument(corrector, node)
|
94
|
+
elsif hash_node.pairs.any?
|
95
|
+
insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
|
96
|
+
else
|
97
|
+
insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def insert_pair_as_last_argument(corrector, node)
|
102
|
+
corrector.insert_before(
|
103
|
+
node.parent.location.end || node.parent.source_range.with(
|
104
|
+
begin_pos: node.parent.source_range.end_pos
|
105
|
+
),
|
106
|
+
", #{format_symbol_to_pair_source(node)}"
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
|
111
|
+
corrector.insert_after(
|
112
|
+
hash_node.location.begin,
|
113
|
+
" #{format_symbol_to_pair_source(node)} "
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
|
118
|
+
corrector.insert_after(
|
119
|
+
hash_node.children.last,
|
120
|
+
", #{format_symbol_to_pair_source(node)}"
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
def insert_symbol(corrector, node)
|
125
|
+
corrector.insert_after(
|
126
|
+
node.parent.left_sibling,
|
127
|
+
", #{node.key.value.inspect}"
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def message_for_style
|
132
|
+
format(
|
133
|
+
'Use %<style>s style for metadata.',
|
134
|
+
style: style
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_metadata_pair(node)
|
139
|
+
return unless bad_metadata_pair?(node)
|
140
|
+
|
141
|
+
add_offense(node, message: message_for_style) do |corrector|
|
142
|
+
autocorrect_pair(corrector, node)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def on_metadata_symbol(node)
|
147
|
+
return unless bad_metadata_symbol?(node)
|
148
|
+
|
149
|
+
add_offense(node, message: message_for_style) do |corrector|
|
150
|
+
autocorrect_symbol(corrector, node)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def remove_pair(corrector, node)
|
155
|
+
if !node.parent.braces? || node.left_siblings.any?
|
156
|
+
remove_pair_following(corrector, node)
|
157
|
+
elsif node.right_siblings.any?
|
158
|
+
remove_pair_preceding(corrector, node)
|
159
|
+
else
|
160
|
+
corrector.remove(node)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def remove_pair_following(corrector, node)
|
165
|
+
corrector.remove(
|
166
|
+
range_with_surrounding_comma(
|
167
|
+
range_with_surrounding_space(
|
168
|
+
node.source_range,
|
169
|
+
side: :left
|
170
|
+
),
|
171
|
+
:left
|
172
|
+
)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def remove_pair_preceding(corrector, node)
|
177
|
+
corrector.remove(
|
178
|
+
range_with_surrounding_space(
|
179
|
+
range_with_surrounding_comma(
|
180
|
+
node.source_range,
|
181
|
+
:right
|
182
|
+
),
|
183
|
+
side: :right
|
184
|
+
)
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
def remove_symbol(corrector, node)
|
189
|
+
corrector.remove(
|
190
|
+
range_with_surrounding_comma(
|
191
|
+
range_with_surrounding_space(
|
192
|
+
node.source_range,
|
193
|
+
side: :left
|
194
|
+
),
|
195
|
+
:left
|
196
|
+
)
|
197
|
+
)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -13,7 +13,7 @@ module RuboCop
|
|
13
13
|
def_node_matcher :rspec_metadata, <<~PATTERN
|
14
14
|
(block
|
15
15
|
(send
|
16
|
-
#rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _
|
16
|
+
#rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ $...)
|
17
17
|
...)
|
18
18
|
PATTERN
|
19
19
|
|
@@ -24,25 +24,39 @@ module RuboCop
|
|
24
24
|
|
25
25
|
# @!method metadata_in_block(node)
|
26
26
|
def_node_search :metadata_in_block, <<~PATTERN
|
27
|
-
(send (lvar %) #Hooks.all _
|
27
|
+
(send (lvar %) #Hooks.all _ $...)
|
28
28
|
PATTERN
|
29
29
|
|
30
30
|
def on_block(node)
|
31
31
|
rspec_configure(node) do |block_var|
|
32
|
-
metadata_in_block(node, block_var) do |
|
33
|
-
|
32
|
+
metadata_in_block(node, block_var) do |metadata_arguments|
|
33
|
+
on_metadata_arguments(metadata_arguments)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
rspec_metadata(node) do |
|
38
|
-
|
37
|
+
rspec_metadata(node) do |metadata_arguments|
|
38
|
+
on_metadata_arguments(metadata_arguments)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
alias on_numblock on_block
|
42
42
|
|
43
|
-
def on_metadata(_symbols,
|
43
|
+
def on_metadata(_symbols, _hash)
|
44
44
|
raise ::NotImplementedError
|
45
45
|
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def on_metadata_arguments(metadata_arguments)
|
50
|
+
*symbols, last = metadata_arguments
|
51
|
+
hash = nil
|
52
|
+
case last&.type
|
53
|
+
when :hash
|
54
|
+
hash = last
|
55
|
+
when :sym
|
56
|
+
symbols << last
|
57
|
+
end
|
58
|
+
on_metadata(symbols, hash)
|
59
|
+
end
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
8
8
|
extend RuboCop::NodePattern::Macros
|
9
9
|
|
10
10
|
# @!method skipped_in_metadata?(node)
|
11
|
-
def_node_matcher :skipped_in_metadata?,
|
11
|
+
def_node_matcher :skipped_in_metadata?, <<~PATTERN
|
12
12
|
{
|
13
13
|
(send _ _ <(sym {:skip :pending}) ...>)
|
14
14
|
(send _ _ ... (hash <(pair (sym {:skip :pending}) { true str dstr }) ...>))
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
30
30
|
#
|
31
31
|
# @param node [RuboCop::AST::Node]
|
32
32
|
# @return [Array<RuboCop::AST::Node>] matching nodes
|
33
|
-
def_node_matcher :skip_or_pending_inside_block?,
|
33
|
+
def_node_matcher :skip_or_pending_inside_block?, <<~PATTERN
|
34
34
|
(block <(send nil? {:skip :pending} ...) ...>)
|
35
35
|
PATTERN
|
36
36
|
end
|