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,52 +4,25 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# #
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \
|
27
|
-
'constant.'
|
28
|
-
ALLOWED_CONSTANTS = %w[Hash OpenStruct].freeze
|
29
|
-
RESTRICT_ON_SEND = %i[factory].freeze
|
30
|
-
|
31
|
-
# @!method class_name(node)
|
32
|
-
def_node_matcher :class_name, <<~PATTERN
|
33
|
-
(send _ :factory _ (hash <(pair (sym :class) $(const ...)) ...>))
|
34
|
-
PATTERN
|
35
|
-
|
36
|
-
def on_send(node)
|
37
|
-
class_name(node) do |cn|
|
38
|
-
next if allowed?(cn.const_name)
|
39
|
-
|
40
|
-
msg = format(MSG, class_name: cn.const_name)
|
41
|
-
add_offense(cn, message: msg) do |corrector|
|
42
|
-
corrector.replace(cn, "'#{cn.source}'")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def allowed?(const_name)
|
50
|
-
ALLOWED_CONSTANTS.include?(const_name)
|
51
|
-
end
|
52
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Use string value when setting the class attribute explicitly.
|
9
|
+
# #
|
10
|
+
# # This cop would promote faster tests by lazy-loading of
|
11
|
+
# # application files. Also, this could help you suppress potential
|
12
|
+
# # bugs in combination with external libraries by avoiding a preload
|
13
|
+
# # of application files from the factory files.
|
14
|
+
# #
|
15
|
+
# # @example
|
16
|
+
# # # bad
|
17
|
+
# # factory :foo, class: Foo do
|
18
|
+
# # end
|
19
|
+
# #
|
20
|
+
# # # good
|
21
|
+
# # factory :foo, class: 'Foo' do
|
22
|
+
# # end
|
23
|
+
# #
|
24
|
+
# class FactoryClassName < ::RuboCop::Cop::Base; end
|
25
|
+
FactoryClassName = ::RuboCop::Cop::FactoryBot::FactoryClassName
|
53
26
|
end
|
54
27
|
end
|
55
28
|
end
|
@@ -4,70 +4,29 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# #
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# #
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# #
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
include RuboCop::RSpec::FactoryBot::Language
|
31
|
-
|
32
|
-
MSG = 'Use %<prefer>s to refer to a factory.'
|
33
|
-
FACTORY_CALLS = RuboCop::RSpec::FactoryBot::Language::METHODS
|
34
|
-
RESTRICT_ON_SEND = FACTORY_CALLS
|
35
|
-
|
36
|
-
# @!method factory_call(node)
|
37
|
-
def_node_matcher :factory_call, <<-PATTERN
|
38
|
-
(send
|
39
|
-
{#factory_bot? nil?} %FACTORY_CALLS
|
40
|
-
${str sym} ...
|
41
|
-
)
|
42
|
-
PATTERN
|
43
|
-
|
44
|
-
def on_send(node)
|
45
|
-
factory_call(node) do |name|
|
46
|
-
if offense_for_symbol_style?(name)
|
47
|
-
register_offense(name, name.value.to_sym.inspect)
|
48
|
-
elsif offense_for_string_style?(name)
|
49
|
-
register_offense(name, name.value.to_s.inspect)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def offense_for_symbol_style?(name)
|
57
|
-
name.str_type? && style == :symbol
|
58
|
-
end
|
59
|
-
|
60
|
-
def offense_for_string_style?(name)
|
61
|
-
name.sym_type? && style == :string
|
62
|
-
end
|
63
|
-
|
64
|
-
def register_offense(name, prefer)
|
65
|
-
add_offense(name,
|
66
|
-
message: format(MSG, prefer: style.to_s)) do |corrector|
|
67
|
-
corrector.replace(name, prefer)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks for name style for argument of FactoryBot::Syntax::Methods.
|
9
|
+
# #
|
10
|
+
# # @example EnforcedStyle: symbol (default)
|
11
|
+
# # # bad
|
12
|
+
# # create('user')
|
13
|
+
# # build "user", username: "NAME"
|
14
|
+
# #
|
15
|
+
# # # good
|
16
|
+
# # create(:user)
|
17
|
+
# # build :user, username: "NAME"
|
18
|
+
# #
|
19
|
+
# # @example EnforcedStyle: string
|
20
|
+
# # # bad
|
21
|
+
# # create(:user)
|
22
|
+
# # build :user, username: "NAME"
|
23
|
+
# #
|
24
|
+
# # # good
|
25
|
+
# # create('user')
|
26
|
+
# # build "user", username: "NAME"
|
27
|
+
# #
|
28
|
+
# class FactoryNameStyle < ::RuboCop::Cop::Base; end
|
29
|
+
FactoryNameStyle = ::RuboCop::Cop::FactoryBot::FactoryNameStyle
|
71
30
|
end
|
72
31
|
end
|
73
32
|
end
|
@@ -4,85 +4,51 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# `
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# #
|
40
|
-
#
|
41
|
-
# FactoryBot.
|
42
|
-
# FactoryBot.
|
43
|
-
#
|
44
|
-
# #
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
include RangeHelp
|
53
|
-
include RuboCop::RSpec::FactoryBot::Language
|
54
|
-
|
55
|
-
MSG = 'Use `%<method>s` from `FactoryBot::Syntax::Methods`.'
|
56
|
-
|
57
|
-
RESTRICT_ON_SEND = RuboCop::RSpec::FactoryBot::Language::METHODS
|
58
|
-
|
59
|
-
def on_send(node)
|
60
|
-
return unless factory_bot?(node.receiver)
|
61
|
-
return unless inside_example_group?(node)
|
62
|
-
|
63
|
-
message = format(MSG, method: node.method_name)
|
64
|
-
|
65
|
-
add_offense(crime_scene(node), message: message) do |corrector|
|
66
|
-
corrector.remove(offense(node))
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def crime_scene(node)
|
73
|
-
range_between(
|
74
|
-
node.loc.expression.begin_pos,
|
75
|
-
node.loc.selector.end_pos
|
76
|
-
)
|
77
|
-
end
|
78
|
-
|
79
|
-
def offense(node)
|
80
|
-
range_between(
|
81
|
-
node.loc.expression.begin_pos,
|
82
|
-
node.loc.selector.begin_pos
|
83
|
-
)
|
84
|
-
end
|
85
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Use shorthands from `FactoryBot::Syntax::Methods` in your specs.
|
9
|
+
# #
|
10
|
+
# # @safety
|
11
|
+
# # The autocorrection is marked as unsafe because the cop
|
12
|
+
# # cannot verify whether you already include
|
13
|
+
# # `FactoryBot::Syntax::Methods` in your test suite.
|
14
|
+
# #
|
15
|
+
# # If you're using Rails, add the following configuration to
|
16
|
+
# # `spec/support/factory_bot.rb` and be sure to require that file
|
17
|
+
# # in `rails_helper.rb`:
|
18
|
+
# #
|
19
|
+
# # [source,ruby]
|
20
|
+
# # ----
|
21
|
+
# # RSpec.configure do |config|
|
22
|
+
# # config.include FactoryBot::Syntax::Methods
|
23
|
+
# # end
|
24
|
+
# # ----
|
25
|
+
# #
|
26
|
+
# # If you're not using Rails:
|
27
|
+
# #
|
28
|
+
# # [source,ruby]
|
29
|
+
# # ----
|
30
|
+
# # RSpec.configure do |config|
|
31
|
+
# # config.include FactoryBot::Syntax::Methods
|
32
|
+
# #
|
33
|
+
# # config.before(:suite) do
|
34
|
+
# # FactoryBot.find_definitions
|
35
|
+
# # end
|
36
|
+
# # end
|
37
|
+
# # ----
|
38
|
+
# #
|
39
|
+
# # @example
|
40
|
+
# # # bad
|
41
|
+
# # FactoryBot.create(:bar)
|
42
|
+
# # FactoryBot.build(:bar)
|
43
|
+
# # FactoryBot.attributes_for(:bar)
|
44
|
+
# #
|
45
|
+
# # # good
|
46
|
+
# # create(:bar)
|
47
|
+
# # build(:bar)
|
48
|
+
# # attributes_for(:bar)
|
49
|
+
# #
|
50
|
+
# class SyntaxMethods < ::RuboCop::Cop::Base; end
|
51
|
+
SyntaxMethods = ::RuboCop::Cop::FactoryBot::SyntaxMethods
|
86
52
|
end
|
87
53
|
end
|
88
54
|
end
|
@@ -5,8 +5,14 @@ module RuboCop
|
|
5
5
|
module RSpec
|
6
6
|
# Checks that spec file paths are consistent and well-formed.
|
7
7
|
#
|
8
|
+
# This cop is deprecated.
|
9
|
+
# We plan to remove it in the next major version update to 3.0.
|
10
|
+
# The migration targets are `RSpec/SpecFilePathSuffix`
|
11
|
+
# and `RSpec/SpecFilePathFormat`.
|
12
|
+
# If you are using this cop, please plan for migration.
|
13
|
+
#
|
8
14
|
# By default, this checks that spec file paths are consistent with the
|
9
|
-
# test subject and
|
15
|
+
# test subject and enforces that it reflects the described
|
10
16
|
# class/module and its optionally called out method.
|
11
17
|
#
|
12
18
|
# With the configuration option `IgnoreMethods` the called out method will
|
@@ -165,7 +171,7 @@ module RuboCop
|
|
165
171
|
end
|
166
172
|
|
167
173
|
def expanded_file_path
|
168
|
-
File.expand_path(processed_source.
|
174
|
+
File.expand_path(processed_source.file_path)
|
169
175
|
end
|
170
176
|
end
|
171
177
|
end
|
@@ -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
|
#
|
@@ -51,6 +63,7 @@ module RuboCop
|
|
51
63
|
#Examples.regular
|
52
64
|
#Examples.skipped
|
53
65
|
#Examples.pending
|
66
|
+
#SharedGroups.all
|
54
67
|
}
|
55
68
|
PATTERN
|
56
69
|
|
@@ -61,12 +74,13 @@ module RuboCop
|
|
61
74
|
PATTERN
|
62
75
|
|
63
76
|
# @!method focused_block?(node)
|
64
|
-
def_node_matcher :focused_block?,
|
65
|
-
|
66
|
-
|
67
|
-
PATTERN
|
77
|
+
def_node_matcher :focused_block?, <<~PATTERN
|
78
|
+
(send #rspec? {#ExampleGroups.focused #Examples.focused} ...)
|
79
|
+
PATTERN
|
68
80
|
|
69
81
|
def on_send(node)
|
82
|
+
return if node.chained? || node.each_ancestor(:def, :defs).any?
|
83
|
+
|
70
84
|
focus_metadata(node) do |focus|
|
71
85
|
add_offense(focus) do |corrector|
|
72
86
|
if focus.pair_type? || focus.str_type? || focus.sym_type?
|
@@ -88,7 +102,7 @@ module RuboCop
|
|
88
102
|
|
89
103
|
def with_surrounding(focus)
|
90
104
|
range_with_space =
|
91
|
-
range_with_surrounding_space(focus.
|
105
|
+
range_with_surrounding_space(focus.source_range, side: :left)
|
92
106
|
|
93
107
|
range_with_surrounding_comma(range_with_space, :left)
|
94
108
|
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,112 @@
|
|
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
|
+
# The configurable options `AllowedIdentifiers` and `AllowedPatterns`
|
12
|
+
# will also read those set in `Naming/VariableNumber`.
|
13
|
+
#
|
14
|
+
# @example `Max: 1 (default)`
|
15
|
+
# # bad
|
16
|
+
# let(:item_1) { create(:item) }
|
17
|
+
# let(:item_2) { create(:item) }
|
18
|
+
#
|
19
|
+
# let(:item1) { create(:item) }
|
20
|
+
# let(:item2) { create(:item) }
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
#
|
24
|
+
# let(:visible_item) { create(:item, visible: true) }
|
25
|
+
# let(:invisible_item) { create(:item, visible: false) }
|
26
|
+
#
|
27
|
+
# @example `Max: 2`
|
28
|
+
# # bad
|
29
|
+
# let(:item_1) { create(:item) }
|
30
|
+
# let(:item_2) { create(:item) }
|
31
|
+
# let(:item_3) { create(:item) }
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# let(:item_1) { create(:item) }
|
35
|
+
# let(:item_2) { create(:item) }
|
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
|
+
#
|
47
|
+
class IndexedLet < Base
|
48
|
+
include AllowedIdentifiers
|
49
|
+
include AllowedPattern
|
50
|
+
|
51
|
+
MSG = 'This `let` statement uses index in its name. Please give it ' \
|
52
|
+
'a meaningful name.'
|
53
|
+
|
54
|
+
# @!method let_name(node)
|
55
|
+
def_node_matcher :let_name, <<~PATTERN
|
56
|
+
{
|
57
|
+
(block (send nil? #Helpers.all ({str sym} $_) ...) ...)
|
58
|
+
(send nil? #Helpers.all ({str sym} $_) block_pass)
|
59
|
+
}
|
60
|
+
PATTERN
|
61
|
+
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
63
|
+
return unless spec_group?(node)
|
64
|
+
|
65
|
+
children = node.body&.child_nodes
|
66
|
+
return unless children
|
67
|
+
|
68
|
+
filter_indexed_lets(children).each do |let_node|
|
69
|
+
add_offense(let_node)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
SUFFIX_INDEX_REGEX = /_?\d+$/.freeze
|
76
|
+
INDEX_REGEX = /\d+/.freeze
|
77
|
+
|
78
|
+
def filter_indexed_lets(candidates)
|
79
|
+
candidates
|
80
|
+
.filter { |node| indexed_let?(node) }
|
81
|
+
.group_by { |node| let_name_stripped_index(node) }
|
82
|
+
.values
|
83
|
+
.filter { |lets| lets.length > cop_config['Max'] }
|
84
|
+
.flatten
|
85
|
+
end
|
86
|
+
|
87
|
+
def indexed_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)
|
92
|
+
end
|
93
|
+
|
94
|
+
def let_name_stripped_index(node)
|
95
|
+
let_name(node).to_s.gsub(INDEX_REGEX, '')
|
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
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -48,7 +48,7 @@ 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)
|
@@ -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
|
@@ -38,19 +38,23 @@ 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
|
|
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,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
|