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
@@ -61,10 +61,9 @@ module RuboCop
|
|
61
61
|
PATTERN
|
62
62
|
|
63
63
|
# @!method focused_block?(node)
|
64
|
-
def_node_matcher :focused_block?,
|
65
|
-
|
66
|
-
|
67
|
-
PATTERN
|
64
|
+
def_node_matcher :focused_block?, <<~PATTERN
|
65
|
+
(send #rspec? {#ExampleGroups.focused #Examples.focused} ...)
|
66
|
+
PATTERN
|
68
67
|
|
69
68
|
def on_send(node)
|
70
69
|
focus_metadata(node) do |focus|
|
@@ -88,7 +87,7 @@ module RuboCop
|
|
88
87
|
|
89
88
|
def with_surrounding(focus)
|
90
89
|
range_with_space =
|
91
|
-
range_with_surrounding_space(focus.
|
90
|
+
range_with_surrounding_space(focus.source_range, side: :left)
|
92
91
|
|
93
92
|
range_with_surrounding_comma(range_with_space, :left)
|
94
93
|
end
|
@@ -83,8 +83,7 @@ module RuboCop
|
|
83
83
|
style_detected(scope_name)
|
84
84
|
msg = explicit_message(scope_name)
|
85
85
|
add_offense(method_send, message: msg) do |corrector|
|
86
|
-
|
87
|
-
corrector.replace(argument_range(method_send), scope)
|
86
|
+
autocorrect(corrector, node, method_send)
|
88
87
|
end
|
89
88
|
end
|
90
89
|
end
|
@@ -93,6 +92,13 @@ module RuboCop
|
|
93
92
|
|
94
93
|
private
|
95
94
|
|
95
|
+
def autocorrect(corrector, _node, method_send)
|
96
|
+
scope = implicit_style? ? '' : "(#{style.inspect})"
|
97
|
+
corrector.replace(
|
98
|
+
LocationHelp.arguments_with_whitespace(method_send), scope
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
96
102
|
def check_implicit(method_send)
|
97
103
|
style_detected(:implicit)
|
98
104
|
return if implicit_style?
|
@@ -100,7 +106,10 @@ module RuboCop
|
|
100
106
|
msg = explicit_message(nil)
|
101
107
|
add_offense(method_send.loc.selector, message: msg) do |corrector|
|
102
108
|
scope = "(#{style.inspect})"
|
103
|
-
corrector.replace(
|
109
|
+
corrector.replace(
|
110
|
+
LocationHelp.arguments_with_whitespace(method_send),
|
111
|
+
scope
|
112
|
+
)
|
104
113
|
end
|
105
114
|
end
|
106
115
|
|
@@ -119,12 +128,6 @@ module RuboCop
|
|
119
128
|
def hook(node, &block)
|
120
129
|
scoped_hook(node, &block) || unscoped_hook(node, &block)
|
121
130
|
end
|
122
|
-
|
123
|
-
def argument_range(send_node)
|
124
|
-
send_node.loc.selector.end.with(
|
125
|
-
end_pos: send_node.loc.expression.end_pos
|
126
|
-
)
|
127
|
-
end
|
128
131
|
end
|
129
132
|
end
|
130
133
|
end
|
@@ -30,9 +30,11 @@ module RuboCop
|
|
30
30
|
# @!method example_or_group?(node)
|
31
31
|
def_node_matcher :example_or_group?, <<-PATTERN
|
32
32
|
{
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
({block numblock} {
|
34
|
+
(send #rspec? #ExampleGroups.all ...)
|
35
|
+
(send nil? #Examples.all ...)
|
36
|
+
} ...)
|
37
|
+
(send nil? #Includes.examples ...)
|
36
38
|
}
|
37
39
|
PATTERN
|
38
40
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Do not set up test data using indexes (e.g., `item_1`, `item_2`).
|
7
|
+
#
|
8
|
+
# It makes reading the test harder because it's not clear what exactly
|
9
|
+
# is tested by this particular example.
|
10
|
+
#
|
11
|
+
# @example `Max: 1 (default)`
|
12
|
+
# # bad
|
13
|
+
# let(:item_1) { create(:item) }
|
14
|
+
# let(:item_2) { create(:item) }
|
15
|
+
#
|
16
|
+
# let(:item1) { create(:item) }
|
17
|
+
# let(:item2) { create(:item) }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
#
|
21
|
+
# let(:visible_item) { create(:item, visible: true) }
|
22
|
+
# let(:invisible_item) { create(:item, visible: false) }
|
23
|
+
#
|
24
|
+
# @example `Max: 2`
|
25
|
+
# # bad
|
26
|
+
# let(:item_1) { create(:item) }
|
27
|
+
# let(:item_2) { create(:item) }
|
28
|
+
# let(:item_3) { create(:item) }
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# let(:item_1) { create(:item) }
|
32
|
+
# let(:item_2) { create(:item) }
|
33
|
+
#
|
34
|
+
class IndexedLet < Base
|
35
|
+
MSG = 'This `let` statement uses index in its name. Please give it ' \
|
36
|
+
'a meaningful name.'
|
37
|
+
|
38
|
+
# @!method let_name(node)
|
39
|
+
def_node_matcher :let_name, <<~PATTERN
|
40
|
+
{
|
41
|
+
(block (send nil? #Helpers.all ({str sym} $_) ...) ...)
|
42
|
+
(send nil? #Helpers.all ({str sym} $_) block_pass)
|
43
|
+
}
|
44
|
+
PATTERN
|
45
|
+
|
46
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
47
|
+
return unless spec_group?(node)
|
48
|
+
|
49
|
+
children = node.body&.child_nodes
|
50
|
+
return unless children
|
51
|
+
|
52
|
+
filter_indexed_lets(children).each do |let_node|
|
53
|
+
add_offense(let_node)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
INDEX_REGEX = /_?\d+/.freeze
|
60
|
+
|
61
|
+
def filter_indexed_lets(candidates)
|
62
|
+
candidates
|
63
|
+
.filter { |node| indexed_let?(node) }
|
64
|
+
.group_by { |node| let_name(node).to_s.gsub(INDEX_REGEX, '') }
|
65
|
+
.values
|
66
|
+
.filter { |lets| lets.length > cop_config['Max'] }
|
67
|
+
.flatten
|
68
|
+
end
|
69
|
+
|
70
|
+
def indexed_let?(node)
|
71
|
+
let?(node) && INDEX_REGEX.match?(let_name(node))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -38,16 +38,16 @@ module RuboCop
|
|
38
38
|
# @!method example_or_group?(node)
|
39
39
|
def_node_matcher :example_or_group?, <<-PATTERN
|
40
40
|
{
|
41
|
-
|
42
|
-
|
41
|
+
(block (send nil? {#ExampleGroups.all #Examples.all} ...) ...)
|
42
|
+
(send nil? #Includes.examples ...)
|
43
43
|
}
|
44
44
|
PATTERN
|
45
45
|
|
46
46
|
# @!method include_examples?(node)
|
47
47
|
def_node_matcher :include_examples?, <<~PATTERN
|
48
48
|
{
|
49
|
-
|
50
|
-
|
49
|
+
(block (send nil? :include_examples ...) ...)
|
50
|
+
(send nil? :include_examples ...)
|
51
51
|
}
|
52
52
|
PATTERN
|
53
53
|
|
@@ -29,14 +29,12 @@ module RuboCop
|
|
29
29
|
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
|
30
30
|
|
31
31
|
# @!method example_or_shared_group_or_including?(node)
|
32
|
-
def_node_matcher :example_or_shared_group_or_including?,
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
39
|
-
PATTERN
|
32
|
+
def_node_matcher :example_or_shared_group_or_including?, <<~PATTERN
|
33
|
+
(block {
|
34
|
+
(send #rspec? {#SharedGroups.all #ExampleGroups.all} ...)
|
35
|
+
(send nil? #Includes.all ...)
|
36
|
+
} ...)
|
37
|
+
PATTERN
|
40
38
|
|
41
39
|
# @!method let_bang(node)
|
42
40
|
def_node_matcher :let_bang, <<-PATTERN
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks where `match_array` is used.
|
7
|
+
#
|
8
|
+
# This cop checks for the following:
|
9
|
+
# - Prefer `contain_exactly` when matching an array with values.
|
10
|
+
# - Prefer `eq` when using `match_array` with an empty array literal.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# it { is_expected.to match_array([content1, content2]) }
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# it { is_expected.to contain_exactly(content1, content2) }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# it { is_expected.to match_array([content] + array) }
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# it { is_expected.to match_array(%w(tremble in fear foolish mortals)) }
|
24
|
+
#
|
25
|
+
class MatchArray < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
MSG = 'Prefer `contain_exactly` when matching an array literal.'
|
29
|
+
RESTRICT_ON_SEND = %i[match_array].freeze
|
30
|
+
|
31
|
+
# @!method match_array_with_empty_array?(node)
|
32
|
+
def_node_matcher :match_array_with_empty_array?, <<~PATTERN
|
33
|
+
(send nil? :match_array (array))
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_send(node)
|
37
|
+
return unless node.first_argument.array_type?
|
38
|
+
return if match_array_with_empty_array?(node)
|
39
|
+
|
40
|
+
check_populated_array(node)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def check_populated_array(node)
|
46
|
+
return if node.first_argument.percent_literal?
|
47
|
+
|
48
|
+
add_offense(node) do |corrector|
|
49
|
+
array_contents = node.arguments.flat_map(&:to_a)
|
50
|
+
corrector.replace(
|
51
|
+
node,
|
52
|
+
"contain_exactly(#{array_contents.map(&:source).join(', ')})"
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -25,8 +25,7 @@ module RuboCop
|
|
25
25
|
|
26
26
|
def missing_separating_line(node)
|
27
27
|
line = final_end_line = final_end_location(node).line
|
28
|
-
|
29
|
-
while comment_line?(processed_source[line])
|
28
|
+
while processed_source.line_with_comment?(line + 1)
|
30
29
|
line += 1
|
31
30
|
comment = processed_source.comment_at_line(line)
|
32
31
|
if DirectiveComment.new(comment).enabled?
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Helper methods to location.
|
7
|
+
module LocationHelp
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# @param node [RuboCop::AST::SendNode]
|
11
|
+
# @return [Parser::Source::Range]
|
12
|
+
# @example
|
13
|
+
# foo 1, 2
|
14
|
+
# ^^^^^
|
15
|
+
def arguments_with_whitespace(node)
|
16
|
+
node.loc.selector.end.with(
|
17
|
+
end_pos: node.source_range.end_pos
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param node [RuboCop::AST::SendNode]
|
22
|
+
# @return [Parser::Source::Range]
|
23
|
+
# @example
|
24
|
+
# foo { bar }
|
25
|
+
# ^^^^^^^^
|
26
|
+
def block_with_whitespace(node)
|
27
|
+
return unless (parent = node.parent)
|
28
|
+
return unless parent.block_type?
|
29
|
+
|
30
|
+
node.source_range.end.with(
|
31
|
+
end_pos: parent.source_range.end_pos
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -10,13 +10,29 @@ module RuboCop
|
|
10
10
|
# @!method skipped_in_metadata?(node)
|
11
11
|
def_node_matcher :skipped_in_metadata?, <<-PATTERN
|
12
12
|
{
|
13
|
-
(send _ _
|
14
|
-
(send _ _ ... (hash <(pair
|
13
|
+
(send _ _ <(sym {:skip :pending}) ...>)
|
14
|
+
(send _ _ ... (hash <(pair (sym {:skip :pending}) { true str dstr }) ...>))
|
15
15
|
}
|
16
16
|
PATTERN
|
17
17
|
|
18
|
-
# @!method
|
19
|
-
|
18
|
+
# @!method skip_or_pending_inside_block?(node)
|
19
|
+
# Match skip/pending statements inside a block (e.g. `context`)
|
20
|
+
#
|
21
|
+
# @example source that matches
|
22
|
+
# context 'when color is blue' do
|
23
|
+
# skip 'not implemented yet'
|
24
|
+
# pending 'not implemented yet'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @example source that does not match
|
28
|
+
# skip 'not implemented yet'
|
29
|
+
# pending 'not implemented yet'
|
30
|
+
#
|
31
|
+
# @param node [RuboCop::AST::Node]
|
32
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
33
|
+
def_node_matcher :skip_or_pending_inside_block?, <<-PATTERN
|
34
|
+
(block <(send nil? {:skip :pending} ...) ...>)
|
35
|
+
PATTERN
|
20
36
|
end
|
21
37
|
end
|
22
38
|
end
|
@@ -78,7 +78,8 @@ module RuboCop
|
|
78
78
|
PATTERN
|
79
79
|
|
80
80
|
# @!method expect?(node)
|
81
|
-
def_node_matcher :expect?,
|
81
|
+
def_node_matcher :expect?, '(send nil? #Expectations.all ...)'
|
82
|
+
|
82
83
|
# @!method aggregate_failures_block?(node)
|
83
84
|
def_node_matcher :aggregate_failures_block?, <<-PATTERN
|
84
85
|
(block (send nil? :aggregate_failures ...) ...)
|
@@ -82,12 +82,14 @@ module RuboCop
|
|
82
82
|
MSG = 'Name your test subject if you need to reference it explicitly.'
|
83
83
|
|
84
84
|
# @!method example_or_hook_block?(node)
|
85
|
-
def_node_matcher :example_or_hook_block?,
|
86
|
-
|
85
|
+
def_node_matcher :example_or_hook_block?, <<~PATTERN
|
86
|
+
(block (send nil? {#Examples.all #Hooks.all} ...) ...)
|
87
|
+
PATTERN
|
87
88
|
|
88
89
|
# @!method shared_example?(node)
|
89
|
-
def_node_matcher :shared_example?,
|
90
|
-
|
90
|
+
def_node_matcher :shared_example?, <<~PATTERN
|
91
|
+
(block (send #rspec? #SharedGroups.examples ...) ...)
|
92
|
+
PATTERN
|
91
93
|
|
92
94
|
# @!method subject_usage(node)
|
93
95
|
def_node_search :subject_usage, '$(send nil? :subject)'
|
@@ -65,10 +65,7 @@ module RuboCop
|
|
65
65
|
# @param [RuboCop::AST::Node] node
|
66
66
|
# @return [Boolean]
|
67
67
|
def_node_matcher :regular_or_focused_example?, <<~PATTERN
|
68
|
-
{
|
69
|
-
#{block_pattern('{#Examples.regular | #Examples.focused}')}
|
70
|
-
#{numblock_pattern('{#Examples.regular | #Examples.focused}')}
|
71
|
-
}
|
68
|
+
({block numblock} (send nil? {#Examples.regular #Examples.focused} ...) ...)
|
72
69
|
PATTERN
|
73
70
|
|
74
71
|
# @!method includes_expectation?(node)
|
@@ -76,7 +73,7 @@ module RuboCop
|
|
76
73
|
# @return [Boolean]
|
77
74
|
def_node_search :includes_expectation?, <<~PATTERN
|
78
75
|
{
|
79
|
-
|
76
|
+
(send nil? #Expectations.all ...)
|
80
77
|
(send nil? `#matches_allowed_pattern? ...)
|
81
78
|
}
|
82
79
|
PATTERN
|
@@ -26,7 +26,9 @@ module RuboCop
|
|
26
26
|
MSG = '`%<name>s` is already defined.'
|
27
27
|
|
28
28
|
# @!method setup?(node)
|
29
|
-
def_node_matcher :setup?,
|
29
|
+
def_node_matcher :setup?, <<~PATTERN
|
30
|
+
(block (send nil? {#Helpers.all #Subjects.all} ...) ...)
|
31
|
+
PATTERN
|
30
32
|
|
31
33
|
# @!method first_argument_name(node)
|
32
34
|
def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
|
@@ -38,20 +38,20 @@ module RuboCop
|
|
38
38
|
MSG = 'Pending spec found.'
|
39
39
|
|
40
40
|
# @!method skippable?(node)
|
41
|
-
def_node_matcher :skippable?,
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
def_node_matcher :skippable?, <<~PATTERN
|
42
|
+
{
|
43
|
+
(send #rspec? #ExampleGroups.regular ...)
|
44
|
+
(send nil? #Examples.regular ...)
|
45
|
+
}
|
46
|
+
PATTERN
|
45
47
|
|
46
48
|
# @!method pending_block?(node)
|
47
|
-
def_node_matcher :pending_block?,
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
}
|
54
|
-
PATTERN
|
49
|
+
def_node_matcher :pending_block?, <<~PATTERN
|
50
|
+
{
|
51
|
+
(send #rspec? #ExampleGroups.skipped ...)
|
52
|
+
(send nil? {#Examples.skipped #Examples.pending} ...)
|
53
|
+
}
|
54
|
+
PATTERN
|
55
55
|
|
56
56
|
def on_send(node)
|
57
57
|
return unless pending_block?(node) || skipped?(node)
|
@@ -59,61 +59,99 @@ module RuboCop
|
|
59
59
|
class PendingWithoutReason < Base
|
60
60
|
MSG = 'Give the reason for pending or skip.'
|
61
61
|
|
62
|
-
# @!method
|
63
|
-
def_node_matcher :
|
64
|
-
|
62
|
+
# @!method skipped_in_example?(node)
|
63
|
+
def_node_matcher :skipped_in_example?, <<~PATTERN
|
64
|
+
{
|
65
|
+
(send nil? ${#Examples.skipped #Examples.pending})
|
66
|
+
(block (send nil? ${#Examples.skipped}) ...)
|
67
|
+
(numblock (send nil? ${#Examples.skipped}) ...)
|
68
|
+
}
|
65
69
|
PATTERN
|
66
70
|
|
67
|
-
# @!method
|
68
|
-
def_node_matcher :
|
69
|
-
(send
|
71
|
+
# @!method skipped_by_example_method?(node)
|
72
|
+
def_node_matcher :skipped_by_example_method?, <<~PATTERN
|
73
|
+
(send nil? ${#Examples.skipped #Examples.pending})
|
70
74
|
PATTERN
|
71
75
|
|
72
|
-
# @!method
|
73
|
-
def_node_matcher :
|
74
|
-
#Examples.skipped
|
76
|
+
# @!method skipped_by_example_method_with_block?(node)
|
77
|
+
def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
|
78
|
+
({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
|
79
|
+
PATTERN
|
80
|
+
|
81
|
+
# @!method metadata_without_reason?(node)
|
82
|
+
def_node_matcher :metadata_without_reason?, <<~PATTERN
|
83
|
+
(send #rspec?
|
84
|
+
{#ExampleGroups.all #Examples.all} ...
|
85
|
+
{
|
86
|
+
<(sym ${:pending :skip}) ...>
|
87
|
+
(hash <(pair (sym ${:pending :skip}) true) ...>)
|
88
|
+
}
|
89
|
+
)
|
75
90
|
PATTERN
|
76
91
|
|
77
92
|
# @!method skipped_by_example_group_method?(node)
|
78
|
-
def_node_matcher
|
79
|
-
|
80
|
-
block_pattern(<<~PATTERN)
|
81
|
-
#ExampleGroups.skipped
|
82
|
-
PATTERN
|
83
|
-
)
|
84
|
-
|
85
|
-
# @!method skipped_by_metadata_without_reason?(node)
|
86
|
-
def_node_matcher :skipped_by_metadata_without_reason?, <<~PATTERN
|
87
|
-
(send #rspec? {#ExampleGroups.all #Examples.all} ... {<(sym :skip) ...> (hash <(pair (sym :skip) true) ...>)})
|
93
|
+
def_node_matcher :skipped_by_example_group_method?, <<~PATTERN
|
94
|
+
(send #rspec? ${#ExampleGroups.skipped} ...)
|
88
95
|
PATTERN
|
89
96
|
|
90
|
-
# @!method
|
91
|
-
def_node_matcher :
|
92
|
-
(send nil?
|
97
|
+
# @!method pending_step_without_reason?(node)
|
98
|
+
def_node_matcher :pending_step_without_reason?, <<~PATTERN
|
99
|
+
(send nil? {:skip :pending})
|
93
100
|
PATTERN
|
94
101
|
|
95
102
|
def on_send(node)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
+
on_pending_by_metadata(node)
|
104
|
+
return unless (parent = parent_node(node))
|
105
|
+
|
106
|
+
if example_group?(parent) || block_node_example_group?(node)
|
107
|
+
on_skipped_by_example_method(node)
|
108
|
+
on_skipped_by_example_group_method(node)
|
109
|
+
elsif example?(parent)
|
110
|
+
on_skipped_by_in_example_method(node)
|
103
111
|
end
|
104
112
|
end
|
105
113
|
|
106
114
|
private
|
107
115
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
116
|
+
def parent_node(node)
|
117
|
+
node_or_block = node.block_node || node
|
118
|
+
return unless (parent = node_or_block.parent)
|
119
|
+
|
120
|
+
parent.begin_type? && parent.parent ? parent.parent : parent
|
121
|
+
end
|
122
|
+
|
123
|
+
def block_node_example_group?(node)
|
124
|
+
node.block_node &&
|
125
|
+
example_group?(node.block_node) &&
|
126
|
+
explicit_rspec?(node.receiver)
|
127
|
+
end
|
128
|
+
|
129
|
+
def on_skipped_by_in_example_method(node)
|
130
|
+
skipped_in_example?(node) do |pending|
|
131
|
+
add_offense(node, message: "Give the reason for #{pending}.")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def on_pending_by_metadata(node)
|
136
|
+
metadata_without_reason?(node) do |pending|
|
137
|
+
add_offense(node, message: "Give the reason for #{pending}.")
|
138
|
+
end
|
111
139
|
end
|
112
140
|
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
141
|
+
def on_skipped_by_example_method(node)
|
142
|
+
skipped_by_example_method?(node) do |pending|
|
143
|
+
add_offense(node, message: "Give the reason for #{pending}.")
|
144
|
+
end
|
145
|
+
|
146
|
+
skipped_by_example_method_with_block?(node.parent) do |pending|
|
147
|
+
add_offense(node, message: "Give the reason for #{pending}.")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def on_skipped_by_example_group_method(node)
|
152
|
+
skipped_by_example_group_method?(node) do
|
153
|
+
add_offense(node, message: 'Give the reason for skip.')
|
154
|
+
end
|
117
155
|
end
|
118
156
|
end
|
119
157
|
end
|
@@ -84,22 +84,22 @@ module RuboCop
|
|
84
84
|
|
85
85
|
def remove_predicate(corrector, predicate)
|
86
86
|
range = predicate.loc.dot.with(
|
87
|
-
end_pos: predicate.
|
87
|
+
end_pos: predicate.source_range.end_pos
|
88
88
|
)
|
89
89
|
|
90
90
|
corrector.remove(range)
|
91
91
|
|
92
|
-
block_range =
|
92
|
+
block_range = LocationHelp.block_with_whitespace(predicate)
|
93
93
|
corrector.remove(block_range) if block_range
|
94
94
|
end
|
95
95
|
|
96
96
|
def rewrite_matcher(corrector, predicate, matcher)
|
97
|
-
args =
|
98
|
-
block_loc =
|
97
|
+
args = LocationHelp.arguments_with_whitespace(predicate).source
|
98
|
+
block_loc = LocationHelp.block_with_whitespace(predicate)
|
99
99
|
block = block_loc ? block_loc.source : ''
|
100
100
|
|
101
101
|
corrector.replace(
|
102
|
-
matcher
|
102
|
+
matcher,
|
103
103
|
to_predicate_matcher(predicate.method_name) + args + block
|
104
104
|
)
|
105
105
|
end
|
@@ -214,20 +214,19 @@ module RuboCop
|
|
214
214
|
|
215
215
|
def corrector_explicit(corrector, to_node, actual, matcher, block_child)
|
216
216
|
replacement_matcher = replacement_matcher(to_node)
|
217
|
-
corrector.replace(matcher
|
217
|
+
corrector.replace(matcher, replacement_matcher)
|
218
218
|
move_predicate(corrector, actual, matcher, block_child)
|
219
219
|
corrector.replace(to_node.loc.selector, 'to')
|
220
220
|
end
|
221
221
|
|
222
222
|
def move_predicate(corrector, actual, matcher, block_child)
|
223
223
|
predicate = to_predicate_method(matcher.method_name)
|
224
|
-
args =
|
225
|
-
block_loc =
|
224
|
+
args = LocationHelp.arguments_with_whitespace(matcher).source
|
225
|
+
block_loc = LocationHelp.block_with_whitespace(block_child)
|
226
226
|
block = block_loc ? block_loc.source : ''
|
227
227
|
|
228
228
|
corrector.remove(block_loc) if block_loc
|
229
|
-
corrector.insert_after(actual
|
230
|
-
".#{predicate}" + args + block)
|
229
|
+
corrector.insert_after(actual, ".#{predicate}" + args + block)
|
231
230
|
end
|
232
231
|
|
233
232
|
# rubocop:disable Metrics/MethodLength
|
@@ -332,31 +331,6 @@ module RuboCop
|
|
332
331
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
333
332
|
check_explicit(node) if style == :explicit
|
334
333
|
end
|
335
|
-
|
336
|
-
private
|
337
|
-
|
338
|
-
# returns args location with whitespace
|
339
|
-
# @example
|
340
|
-
# foo 1, 2
|
341
|
-
# ^^^^^
|
342
|
-
def args_loc(send_node)
|
343
|
-
send_node.loc.selector.end.with(
|
344
|
-
end_pos: send_node.loc.expression.end_pos
|
345
|
-
)
|
346
|
-
end
|
347
|
-
|
348
|
-
# returns block location with whitespace
|
349
|
-
# @example
|
350
|
-
# foo { bar }
|
351
|
-
# ^^^^^^^^
|
352
|
-
def block_loc(send_node)
|
353
|
-
parent = send_node.parent
|
354
|
-
return unless parent.block_type?
|
355
|
-
|
356
|
-
send_node.loc.expression.end.with(
|
357
|
-
end_pos: parent.loc.expression.end_pos
|
358
|
-
)
|
359
|
-
end
|
360
334
|
end
|
361
335
|
end
|
362
336
|
end
|