rubocop-rspec 2.16.0 → 2.24.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|