rubocop-rspec 2.16.0 → 2.24.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 +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
@@ -4,69 +4,32 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module Capybara
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# expect(page).to
|
23
|
-
#
|
24
|
-
# #
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# expect(page).to
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
button
|
34
|
-
checked_field
|
35
|
-
css
|
36
|
-
field
|
37
|
-
link
|
38
|
-
select
|
39
|
-
selector
|
40
|
-
table
|
41
|
-
unchecked_field
|
42
|
-
xpath
|
43
|
-
].flat_map do |element|
|
44
|
-
["have_#{element}".to_sym, "have_no_#{element}".to_sym]
|
45
|
-
end
|
46
|
-
|
47
|
-
RESTRICT_ON_SEND = CAPYBARA_MATCHER_METHODS
|
48
|
-
|
49
|
-
# @!method visible_true?(node)
|
50
|
-
def_node_matcher :visible_true?, <<~PATTERN
|
51
|
-
(send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) true) ...>))
|
52
|
-
PATTERN
|
53
|
-
|
54
|
-
# @!method visible_false?(node)
|
55
|
-
def_node_matcher :visible_false?, <<~PATTERN
|
56
|
-
(send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) false) ...>))
|
57
|
-
PATTERN
|
58
|
-
|
59
|
-
def on_send(node)
|
60
|
-
visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) }
|
61
|
-
visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) }
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def capybara_matcher?(method_name)
|
67
|
-
CAPYBARA_MATCHER_METHODS.include? method_name
|
68
|
-
end
|
69
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks for boolean visibility in Capybara finders.
|
9
|
+
# #
|
10
|
+
# # Capybara lets you find elements that match a certain visibility
|
11
|
+
# # using the `:visible` option. `:visible` accepts both boolean and
|
12
|
+
# # symbols as values, however using booleans can have unwanted
|
13
|
+
# # effects. `visible: false` does not find just invisible elements,
|
14
|
+
# # but both visible and invisible elements. For expressiveness and
|
15
|
+
# # clarity, use one of the # symbol values, `:all`, `:hidden` or
|
16
|
+
# # `:visible`.
|
17
|
+
# # Read more in
|
18
|
+
# # https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all[the documentation].
|
19
|
+
# #
|
20
|
+
# # @example
|
21
|
+
# # # bad
|
22
|
+
# # expect(page).to have_selector('.foo', visible: false)
|
23
|
+
# # expect(page).to have_css('.foo', visible: true)
|
24
|
+
# # expect(page).to have_link('my link', visible: false)
|
25
|
+
# #
|
26
|
+
# # # good
|
27
|
+
# # expect(page).to have_selector('.foo', visible: :visible)
|
28
|
+
# # expect(page).to have_css('.foo', visible: :all)
|
29
|
+
# # expect(page).to have_link('my link', visible: :hidden)
|
30
|
+
# #
|
31
|
+
# class VisibilityMatcher < ::RuboCop::Cop::Base; end
|
32
|
+
VisibilityMatcher = ::RuboCop::Cop::Capybara::VisibilityMatcher
|
70
33
|
end
|
71
34
|
end
|
72
35
|
end
|
@@ -59,15 +59,16 @@ module RuboCop
|
|
59
59
|
#
|
60
60
|
class ChangeByZero < Base
|
61
61
|
extend AutoCorrector
|
62
|
-
MSG = 'Prefer `not_to change` over `to
|
62
|
+
MSG = 'Prefer `not_to change` over `to %<method>s.by(0)`.'
|
63
63
|
MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
|
64
|
-
'over
|
65
|
-
|
64
|
+
'over `%<method>s.by(0)`.'
|
65
|
+
CHANGE_METHODS = Set[:change, :a_block_changing, :changing].freeze
|
66
|
+
RESTRICT_ON_SEND = CHANGE_METHODS.freeze
|
66
67
|
|
67
68
|
# @!method expect_change_with_arguments(node)
|
68
69
|
def_node_matcher :expect_change_with_arguments, <<-PATTERN
|
69
70
|
(send
|
70
|
-
(send nil?
|
71
|
+
$(send nil? CHANGE_METHODS ...) :by
|
71
72
|
(int 0))
|
72
73
|
PATTERN
|
73
74
|
|
@@ -75,49 +76,62 @@ module RuboCop
|
|
75
76
|
def_node_matcher :expect_change_with_block, <<-PATTERN
|
76
77
|
(send
|
77
78
|
(block
|
78
|
-
(send nil?
|
79
|
+
$(send nil? CHANGE_METHODS)
|
79
80
|
(args)
|
80
|
-
(send (...)
|
81
|
+
(send (...) _)) :by
|
81
82
|
(int 0))
|
82
83
|
PATTERN
|
83
84
|
|
84
85
|
# @!method change_nodes(node)
|
85
86
|
def_node_search :change_nodes, <<-PATTERN
|
86
|
-
$(send nil?
|
87
|
+
$(send nil? CHANGE_METHODS ...)
|
87
88
|
PATTERN
|
88
89
|
|
89
90
|
def on_send(node)
|
90
|
-
expect_change_with_arguments(node.parent) do
|
91
|
-
|
91
|
+
expect_change_with_arguments(node.parent) do |change|
|
92
|
+
register_offense(node.parent, change)
|
92
93
|
end
|
93
94
|
|
94
|
-
expect_change_with_block(node.parent.parent) do
|
95
|
-
|
95
|
+
expect_change_with_block(node.parent.parent) do |change|
|
96
|
+
register_offense(node.parent.parent, change)
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
100
|
private
|
100
101
|
|
101
|
-
|
102
|
-
|
102
|
+
# rubocop:disable Metrics/MethodLength
|
103
|
+
def register_offense(node, change_node)
|
103
104
|
if compound_expectations?(node)
|
104
|
-
add_offense(
|
105
|
+
add_offense(node.source_range,
|
106
|
+
message: message_compound(change_node)) do |corrector|
|
105
107
|
autocorrect_compound(corrector, node)
|
106
108
|
end
|
107
109
|
else
|
108
|
-
add_offense(
|
109
|
-
|
110
|
+
add_offense(node.source_range,
|
111
|
+
message: message(change_node)) do |corrector|
|
112
|
+
autocorrect(corrector, node, change_node)
|
110
113
|
end
|
111
114
|
end
|
112
115
|
end
|
116
|
+
# rubocop:enable Metrics/MethodLength
|
113
117
|
|
114
118
|
def compound_expectations?(node)
|
115
119
|
%i[and or & |].include?(node.parent.method_name)
|
116
120
|
end
|
117
121
|
|
118
|
-
def
|
122
|
+
def message(change_node)
|
123
|
+
format(MSG, method: change_node.method_name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def message_compound(change_node)
|
127
|
+
format(MSG_COMPOUND, preferred: preferred_method,
|
128
|
+
method: change_node.method_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
def autocorrect(corrector, node, change_node)
|
119
132
|
corrector.replace(node.parent.loc.selector, 'not_to')
|
120
|
-
|
133
|
+
corrector.replace(change_node.loc.selector, 'change')
|
134
|
+
range = node.loc.dot.with(end_pos: node.source_range.end_pos)
|
121
135
|
corrector.remove(range)
|
122
136
|
end
|
123
137
|
|
@@ -126,7 +140,7 @@ module RuboCop
|
|
126
140
|
|
127
141
|
change_nodes(node) do |change_node|
|
128
142
|
corrector.replace(change_node.loc.selector, negated_matcher)
|
129
|
-
range = node.loc.dot.with(end_pos: node.
|
143
|
+
range = node.loc.dot.with(end_pos: node.source_range.end_pos)
|
130
144
|
corrector.remove(range)
|
131
145
|
end
|
132
146
|
end
|
@@ -135,10 +149,6 @@ module RuboCop
|
|
135
149
|
cop_config['NegatedMatcher']
|
136
150
|
end
|
137
151
|
|
138
|
-
def message_compound
|
139
|
-
format(MSG_COMPOUND, preferred: preferred_method)
|
140
|
-
end
|
141
|
-
|
142
152
|
def preferred_method
|
143
153
|
negated_matcher ? "`#{negated_matcher}`" : 'negated matchers'
|
144
154
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks where `contain_exactly` is used.
|
7
|
+
#
|
8
|
+
# This cop checks for the following:
|
9
|
+
# - Prefer `match_array` when matching array values.
|
10
|
+
# - Prefer `be_empty` when using `contain_exactly` with no arguments.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# it { is_expected.to contain_exactly(*array1, *array2) }
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# it { is_expected.to match_array(array1 + array2) }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# it { is_expected.to contain_exactly(content, *array) }
|
21
|
+
#
|
22
|
+
class ContainExactly < Base
|
23
|
+
extend AutoCorrector
|
24
|
+
|
25
|
+
MSG = 'Prefer `match_array` when matching array values.'
|
26
|
+
RESTRICT_ON_SEND = %i[contain_exactly].freeze
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
return if node.arguments.empty?
|
30
|
+
|
31
|
+
check_populated_collection(node)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def check_populated_collection(node)
|
37
|
+
return unless node.each_child_node.all?(&:splat_type?)
|
38
|
+
|
39
|
+
add_offense(node) do |corrector|
|
40
|
+
autocorrect_for_populated_array(node, corrector)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def autocorrect_for_populated_array(node, corrector)
|
45
|
+
arrays = node.arguments.map do |splat_node|
|
46
|
+
splat_node.children.first
|
47
|
+
end
|
48
|
+
corrector.replace(
|
49
|
+
node,
|
50
|
+
"match_array(#{arrays.map(&:source).join(' + ')})"
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -31,7 +31,11 @@ module RuboCop
|
|
31
31
|
|
32
32
|
# @!method context_method(node)
|
33
33
|
def_node_matcher :context_method, <<-PATTERN
|
34
|
-
(block
|
34
|
+
(block
|
35
|
+
(send #rspec? :context
|
36
|
+
${(str #method_name?) (dstr (str #method_name?) ...)}
|
37
|
+
...)
|
38
|
+
...)
|
35
39
|
PATTERN
|
36
40
|
|
37
41
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
@@ -10,7 +10,6 @@ module RuboCop
|
|
10
10
|
# include `if`, `unless`, `for`, `before`, `after`, or `during`.
|
11
11
|
# They may consist of multiple words if desired.
|
12
12
|
#
|
13
|
-
# @see https://rspec.rubystyle.guide/#context-descriptions
|
14
13
|
# @see http://www.betterspecs.org/#contexts
|
15
14
|
#
|
16
15
|
# @example `Prefixes` configuration
|
@@ -63,12 +62,12 @@ module RuboCop
|
|
63
62
|
|
64
63
|
# @!method context_wording(node)
|
65
64
|
def_node_matcher :context_wording, <<-PATTERN
|
66
|
-
(block (send #rspec? { :context :shared_context } $(str
|
65
|
+
(block (send #rspec? { :context :shared_context } $({str dstr xstr} ...) ...) ...)
|
67
66
|
PATTERN
|
68
67
|
|
69
68
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
70
|
-
context_wording(node) do |context
|
71
|
-
if bad_pattern?(
|
69
|
+
context_wording(node) do |context|
|
70
|
+
if bad_pattern?(context)
|
72
71
|
message = format(MSG, patterns: expect_patterns)
|
73
72
|
add_offense(context, message: message)
|
74
73
|
end
|
@@ -85,10 +84,18 @@ module RuboCop
|
|
85
84
|
@prefix_regexes ||= prefixes.map { |pre| /^#{Regexp.escape(pre)}\b/ }
|
86
85
|
end
|
87
86
|
|
88
|
-
def bad_pattern?(
|
87
|
+
def bad_pattern?(node)
|
89
88
|
return false if allowed_patterns.empty?
|
90
89
|
|
91
|
-
!matches_allowed_pattern?(description)
|
90
|
+
!matches_allowed_pattern?(description(node))
|
91
|
+
end
|
92
|
+
|
93
|
+
def description(context)
|
94
|
+
if context.xstr_type?
|
95
|
+
context.value.value
|
96
|
+
else
|
97
|
+
context.value
|
98
|
+
end
|
92
99
|
end
|
93
100
|
|
94
101
|
def expect_patterns
|
@@ -23,20 +23,28 @@ module RuboCop
|
|
23
23
|
MSG = 'The second argument to describe should be the method ' \
|
24
24
|
"being tested. '#instance' or '.class'."
|
25
25
|
|
26
|
-
# @!method
|
27
|
-
def_node_matcher :
|
26
|
+
# @!method second_string_literal_argument(node)
|
27
|
+
def_node_matcher :second_string_literal_argument, <<~PATTERN
|
28
28
|
(block
|
29
|
-
(send #rspec? :describe _first_argument $
|
30
|
-
)
|
29
|
+
(send #rspec? :describe _first_argument ${str dstr} ...)
|
30
|
+
...)
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
# @!method method_name?(node)
|
34
|
+
def_node_matcher :method_name?, <<~PATTERN
|
35
|
+
{(str #method_name_prefix?) (dstr (str #method_name_prefix?) ...)}
|
31
36
|
PATTERN
|
32
37
|
|
33
38
|
def on_top_level_group(node)
|
34
|
-
|
39
|
+
second_string_literal_argument(node) do |argument|
|
40
|
+
add_offense(argument) unless method_name?(argument)
|
41
|
+
end
|
42
|
+
end
|
35
43
|
|
36
|
-
|
37
|
-
return if second_argument.str_content.start_with?('#', '.')
|
44
|
+
private
|
38
45
|
|
39
|
-
|
46
|
+
def method_name_prefix?(description)
|
47
|
+
description.start_with?('.', '#')
|
40
48
|
end
|
41
49
|
end
|
42
50
|
end
|
@@ -68,7 +68,8 @@ module RuboCop
|
|
68
68
|
PATTERN
|
69
69
|
|
70
70
|
# @!method rspec_block?(node)
|
71
|
-
def_node_matcher :rspec_block?,
|
71
|
+
def_node_matcher :rspec_block?,
|
72
|
+
'({block numblock} (send #rspec? #ALL.all ...) ...)'
|
72
73
|
|
73
74
|
# @!method scope_changing_syntax?(node)
|
74
75
|
def_node_matcher :scope_changing_syntax?, '{def class module}'
|
@@ -22,13 +22,15 @@ module RuboCop
|
|
22
22
|
class DescribedClassModuleWrapping < Base
|
23
23
|
MSG = 'Avoid opening modules and defining specs within them.'
|
24
24
|
|
25
|
-
# @!method
|
26
|
-
def_node_search :
|
25
|
+
# @!method include_rspec_blocks?(node)
|
26
|
+
def_node_search :include_rspec_blocks?, <<~PATTERN
|
27
|
+
({block numblock} (send #explicit_rspec? #ExampleGroups.all ...) ...)
|
28
|
+
PATTERN
|
27
29
|
|
28
30
|
def on_module(node)
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
return unless include_rspec_blocks?(node)
|
32
|
+
|
33
|
+
add_offense(node)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
@@ -49,7 +49,7 @@ module RuboCop
|
|
49
49
|
MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
|
50
50
|
|
51
51
|
# @!method rspec_method?(node)
|
52
|
-
def_node_matcher :rspec_method?,
|
52
|
+
def_node_matcher :rspec_method?, '(send #rspec? #ALL.all ...)'
|
53
53
|
|
54
54
|
def on_send(node)
|
55
55
|
return unless rspec_method?(node)
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
19
19
|
|
20
20
|
MSG = 'Avoid duplicated metadata.'
|
21
21
|
|
22
|
-
def on_metadata(symbols,
|
22
|
+
def on_metadata(symbols, _hash)
|
23
23
|
symbols.each do |symbol|
|
24
24
|
on_metadata_symbol(symbol)
|
25
25
|
end
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
corrector.remove(
|
40
40
|
range_with_surrounding_comma(
|
41
41
|
range_with_surrounding_space(
|
42
|
-
node.
|
42
|
+
node.source_range,
|
43
43
|
side: :left
|
44
44
|
),
|
45
45
|
:left
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
53
53
|
# @param node [RuboCop::AST::Node]
|
54
54
|
# @yield [RuboCop::AST::Node] example group body
|
55
55
|
def_node_matcher :example_group_body, <<~PATTERN
|
56
|
-
(block
|
56
|
+
(block (send #rspec? #ExampleGroups.all ...) args $_)
|
57
57
|
PATTERN
|
58
58
|
|
59
59
|
# @!method example_or_group_or_include?(node)
|
@@ -72,10 +72,10 @@ module RuboCop
|
|
72
72
|
# @return [Array<RuboCop::AST::Node>] matching nodes
|
73
73
|
def_node_matcher :example_or_group_or_include?, <<~PATTERN
|
74
74
|
{
|
75
|
-
|
76
|
-
|
77
|
-
)
|
78
|
-
|
75
|
+
(block
|
76
|
+
(send #rspec? {#Examples.all #ExampleGroups.all #Includes.all} ...)
|
77
|
+
...)
|
78
|
+
(send nil? {#Examples.all #Includes.all} ...)
|
79
79
|
}
|
80
80
|
PATTERN
|
81
81
|
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
95
95
|
# @param node [RuboCop::AST::Node]
|
96
96
|
# @return [Array<RuboCop::AST::Node>] matching nodes
|
97
97
|
def_node_matcher :examples_inside_block?, <<~PATTERN
|
98
|
-
(block
|
98
|
+
(block !(send nil? #Hooks.all ...) _ #examples?)
|
99
99
|
PATTERN
|
100
100
|
|
101
101
|
# @!method examples_directly_or_in_block?(node)
|
@@ -131,6 +131,7 @@ module RuboCop
|
|
131
131
|
{
|
132
132
|
#examples_directly_or_in_block?
|
133
133
|
(begin <#examples_directly_or_in_block? ...>)
|
134
|
+
(begin <#examples_in_branches? ...>)
|
134
135
|
}
|
135
136
|
PATTERN
|
136
137
|
|
@@ -169,12 +170,14 @@ module RuboCop
|
|
169
170
|
end
|
170
171
|
|
171
172
|
def examples_in_branches?(condition_node)
|
173
|
+
return if !condition_node.if_type? && !condition_node.case_type?
|
174
|
+
|
172
175
|
condition_node.branches.any? { |branch| examples?(branch) }
|
173
176
|
end
|
174
177
|
|
175
178
|
def removed_range(node)
|
176
179
|
range_by_whole_lines(
|
177
|
-
node.
|
180
|
+
node.source_range,
|
178
181
|
include_final_newline: true
|
179
182
|
)
|
180
183
|
end
|
@@ -31,14 +31,14 @@ module RuboCop
|
|
31
31
|
|
32
32
|
# @!method empty_hook?(node)
|
33
33
|
def_node_matcher :empty_hook?, <<~PATTERN
|
34
|
-
(block
|
34
|
+
(block $(send nil? #Hooks.all ...) _ nil?)
|
35
35
|
PATTERN
|
36
36
|
|
37
37
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
38
38
|
empty_hook?(node) do |hook|
|
39
39
|
add_offense(hook) do |corrector|
|
40
40
|
corrector.remove(
|
41
|
-
range_with_surrounding_space(node.
|
41
|
+
range_with_surrounding_space(node.source_range, side: :left)
|
42
42
|
)
|
43
43
|
end
|
44
44
|
end
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
30
30
|
MSG = 'Add an empty line after `%<example_group>s`.'
|
31
31
|
|
32
32
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
33
|
-
return unless
|
33
|
+
return unless spec_group?(node)
|
34
34
|
|
35
35
|
missing_separating_line_offense(node) do |method|
|
36
36
|
format(MSG, example_group: method)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Avoid empty metadata hash.
|
7
|
+
#
|
8
|
+
# @example EnforcedStyle: symbol (default)
|
9
|
+
# # bad
|
10
|
+
# describe 'Something', {}
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# describe 'Something'
|
14
|
+
class EmptyMetadata < Base
|
15
|
+
extend AutoCorrector
|
16
|
+
|
17
|
+
include Metadata
|
18
|
+
include RangeHelp
|
19
|
+
|
20
|
+
MSG = 'Avoid empty metadata hash.'
|
21
|
+
|
22
|
+
def on_metadata(_symbols, hash)
|
23
|
+
return unless hash&.pairs&.empty?
|
24
|
+
|
25
|
+
add_offense(hash) do |corrector|
|
26
|
+
remove_empty_metadata(corrector, hash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def remove_empty_metadata(corrector, node)
|
33
|
+
corrector.remove(
|
34
|
+
range_with_surrounding_comma(
|
35
|
+
range_with_surrounding_space(
|
36
|
+
node.source_range,
|
37
|
+
side: :left
|
38
|
+
),
|
39
|
+
:left
|
40
|
+
)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Use `eq` instead of `be ==` to compare objects.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# expect(foo).to be == 42
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# expect(foo).to eq 42
|
14
|
+
#
|
15
|
+
class Eq < Base
|
16
|
+
extend AutoCorrector
|
17
|
+
include RangeHelp
|
18
|
+
|
19
|
+
MSG = 'Use `eq` instead of `be ==` to compare objects.'
|
20
|
+
RESTRICT_ON_SEND = Runners.all
|
21
|
+
|
22
|
+
# @!method be_equals(node)
|
23
|
+
def_node_matcher :be_equals, <<~PATTERN
|
24
|
+
(send _ #Runners.all $(send (send nil? :be) :== _))
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
be_equals(node) do |matcher|
|
29
|
+
range = offense_range(matcher)
|
30
|
+
add_offense(range) do |corrector|
|
31
|
+
corrector.replace(range, 'eq')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def offense_range(matcher)
|
39
|
+
range_between(
|
40
|
+
matcher.source_range.begin_pos,
|
41
|
+
matcher.loc.selector.end_pos
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -52,14 +52,23 @@ module RuboCop
|
|
52
52
|
|
53
53
|
# @param text [String]
|
54
54
|
def excessive_whitespace?(text)
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
text.match?(/
|
56
|
+
# Leading space
|
57
|
+
\A[[:blank:]]
|
58
|
+
|
|
59
|
+
# Trailing space
|
60
|
+
[[:blank:]]\z
|
61
|
+
|
|
62
|
+
# Two or more consecutive spaces, except if they are leading spaces
|
63
|
+
[^[[:space:]]][[:blank:]]{2,}[^[[:blank:]]]
|
64
|
+
/x)
|
58
65
|
end
|
59
66
|
|
60
67
|
# @param text [String]
|
61
68
|
def strip_excessive_whitespace(text)
|
62
|
-
text
|
69
|
+
text
|
70
|
+
.gsub(/[[:blank:]]{2,}/, ' ')
|
71
|
+
.gsub(/\A[[:blank:]]|[[:blank:]]\z/, '')
|
63
72
|
end
|
64
73
|
|
65
74
|
# @param node [RuboCop::AST::Node]
|
@@ -74,7 +83,7 @@ module RuboCop
|
|
74
83
|
end
|
75
84
|
|
76
85
|
def docstring(node)
|
77
|
-
expr = node.
|
86
|
+
expr = node.source_range
|
78
87
|
|
79
88
|
Parser::Source::Range.new(
|
80
89
|
expr.source_buffer,
|