rubocop-rspec 1.7.0 → 3.0.2
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 +5 -5
- data/CHANGELOG.md +955 -79
- data/CODE_OF_CONDUCT.md +17 -0
- data/MIT-LICENSE.md +1 -2
- data/README.md +35 -35
- data/config/default.yml +940 -52
- data/config/obsoletion.yml +30 -0
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +49 -0
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +49 -0
- data/lib/rubocop/cop/rspec/any_instance.rb +10 -13
- data/lib/rubocop/cop/rspec/around_block.rb +97 -0
- data/lib/rubocop/cop/rspec/base.rb +26 -0
- data/lib/rubocop/cop/rspec/be.rb +39 -0
- data/lib/rubocop/cop/rspec/be_empty.rb +45 -0
- data/lib/rubocop/cop/rspec/be_eq.rb +47 -0
- data/lib/rubocop/cop/rspec/be_eql.rb +18 -15
- data/lib/rubocop/cop/rspec/be_nil.rb +74 -0
- data/lib/rubocop/cop/rspec/before_after_all.rb +45 -0
- data/lib/rubocop/cop/rspec/change_by_zero.rb +184 -0
- data/lib/rubocop/cop/rspec/class_check.rb +101 -0
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_method.rb +57 -0
- data/lib/rubocop/cop/rspec/context_wording.rb +117 -0
- data/lib/rubocop/cop/rspec/describe_class.rb +52 -21
- data/lib/rubocop/cop/rspec/describe_method.rb +26 -11
- data/lib/rubocop/cop/rspec/describe_symbol.rb +37 -0
- data/lib/rubocop/cop/rspec/described_class.rb +181 -34
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +38 -0
- data/lib/rubocop/cop/rspec/dialect.rb +84 -0
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +58 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +134 -47
- data/lib/rubocop/cop/rspec/empty_hook.rb +49 -0
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +82 -0
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +42 -0
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +40 -0
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +82 -0
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +36 -0
- data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
- data/lib/rubocop/cop/rspec/empty_output.rb +47 -0
- data/lib/rubocop/cop/rspec/eq.rb +47 -0
- data/lib/rubocop/cop/rspec/example_length.rb +38 -20
- data/lib/rubocop/cop/rspec/example_without_description.rb +98 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +117 -27
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +110 -0
- data/lib/rubocop/cop/rspec/expect_actual.rb +46 -20
- data/lib/rubocop/cop/rspec/expect_change.rb +86 -0
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +50 -0
- data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
- data/lib/rubocop/cop/rspec/expect_output.rb +50 -0
- data/lib/rubocop/cop/rspec/focus.rb +79 -25
- data/lib/rubocop/cop/rspec/hook_argument.rb +48 -36
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +81 -0
- data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +37 -0
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +68 -0
- data/lib/rubocop/cop/rspec/implicit_expect.rb +100 -0
- data/lib/rubocop/cop/rspec/implicit_subject.rb +167 -0
- data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
- data/lib/rubocop/cop/rspec/instance_spy.rb +74 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +28 -14
- data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +49 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +74 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +57 -29
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +127 -0
- data/lib/rubocop/cop/rspec/let_before_examples.rb +101 -0
- data/lib/rubocop/cop/rspec/let_setup.rb +32 -16
- data/lib/rubocop/cop/rspec/match_array.rb +59 -0
- data/lib/rubocop/cop/rspec/message_chain.rb +10 -15
- data/lib/rubocop/cop/rspec/message_expectation.rb +12 -9
- data/lib/rubocop/cop/rspec/message_spies.rb +88 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
- data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
- data/lib/rubocop/cop/rspec/mixin/comments_help.rb +38 -0
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +59 -0
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
- data/lib/rubocop/cop/rspec/mixin/inside_example_group.rb +29 -0
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +63 -0
- data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +39 -0
- data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
- data/lib/rubocop/cop/rspec/mixin/variable.rb +21 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +14 -12
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +86 -26
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +146 -0
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +97 -0
- data/lib/rubocop/cop/rspec/named_subject.rb +107 -27
- data/lib/rubocop/cop/rspec/nested_groups.rb +84 -47
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +102 -0
- data/lib/rubocop/cop/rspec/not_to_not.rb +30 -27
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +74 -0
- data/lib/rubocop/cop/rspec/pending.rb +80 -0
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +159 -0
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +341 -0
- data/lib/rubocop/cop/rspec/receive_counts.rb +89 -0
- data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
- data/lib/rubocop/cop/rspec/receive_never.rb +41 -0
- data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
- data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
- data/lib/rubocop/cop/rspec/remove_const.rb +39 -0
- data/lib/rubocop/cop/rspec/repeated_description.rb +98 -0
- data/lib/rubocop/cop/rspec/repeated_example.rb +53 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +100 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +96 -0
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +105 -0
- data/lib/rubocop/cop/rspec/repeated_subject_call.rb +125 -0
- data/lib/rubocop/cop/rspec/return_from_stub.rb +169 -0
- data/lib/rubocop/cop/rspec/scattered_let.rb +59 -0
- data/lib/rubocop/cop/rspec/scattered_setup.rb +92 -0
- data/lib/rubocop/cop/rspec/shared_context.rb +107 -0
- data/lib/rubocop/cop/rspec/shared_examples.rb +125 -0
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +93 -0
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
- data/lib/rubocop/cop/rspec/sort_metadata.rb +71 -0
- 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 +176 -0
- data/lib/rubocop/cop/rspec/subject_declaration.rb +46 -0
- data/lib/rubocop/cop/rspec/subject_stub.rb +93 -74
- data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +69 -0
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +67 -0
- data/lib/rubocop/cop/rspec/variable_definition.rb +77 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +68 -0
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +111 -0
- data/lib/rubocop/cop/rspec/verified_doubles.rb +28 -14
- data/lib/rubocop/cop/rspec/void_expect.rb +60 -0
- data/lib/rubocop/cop/rspec/yield.rb +82 -0
- data/lib/rubocop/cop/rspec_cops.rb +112 -0
- data/lib/rubocop/rspec/align_let_brace.rb +63 -0
- data/lib/rubocop/rspec/concept.rb +33 -0
- data/lib/rubocop/rspec/config_formatter.rb +27 -4
- data/lib/rubocop/rspec/cop/generator.rb +25 -0
- data/lib/rubocop/rspec/corrector/move_node.rb +51 -0
- data/lib/rubocop/rspec/description_extractor.rb +60 -18
- data/lib/rubocop/rspec/example.rb +37 -0
- data/lib/rubocop/rspec/example_group.rb +67 -0
- data/lib/rubocop/rspec/hook.rb +79 -0
- data/lib/rubocop/rspec/inject.rb +3 -1
- data/lib/rubocop/rspec/language.rb +184 -41
- data/lib/rubocop/rspec/node.rb +19 -0
- data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +29 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop/rspec/wording.rb +61 -19
- data/lib/rubocop/rspec.rb +6 -2
- data/lib/rubocop-rspec.rb +45 -34
- metadata +130 -195
- data/Gemfile +0 -13
- data/Rakefile +0 -48
- data/lib/rubocop/cop/rspec/file_path.rb +0 -83
- data/lib/rubocop/rspec/language/node_pattern.rb +0 -16
- data/lib/rubocop/rspec/spec_only.rb +0 -61
- data/lib/rubocop/rspec/top_level_describe.rb +0 -61
- data/lib/rubocop/rspec/util.rb +0 -19
- data/rubocop-rspec.gemspec +0 -42
- data/spec/expect_violation/expectation_spec.rb +0 -85
- data/spec/project/changelog_spec.rb +0 -81
- data/spec/project/default_config_spec.rb +0 -52
- data/spec/project/project_requires_spec.rb +0 -8
- data/spec/rubocop/cop/rspec/any_instance_spec.rb +0 -30
- data/spec/rubocop/cop/rspec/be_eql_spec.rb +0 -59
- data/spec/rubocop/cop/rspec/describe_class_spec.rb +0 -113
- data/spec/rubocop/cop/rspec/describe_method_spec.rb +0 -32
- data/spec/rubocop/cop/rspec/described_class_spec.rb +0 -219
- data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +0 -79
- data/spec/rubocop/cop/rspec/example_length_spec.rb +0 -117
- data/spec/rubocop/cop/rspec/example_wording_spec.rb +0 -82
- data/spec/rubocop/cop/rspec/expect_actual_spec.rb +0 -136
- data/spec/rubocop/cop/rspec/file_path_spec.rb +0 -236
- data/spec/rubocop/cop/rspec/focus_spec.rb +0 -130
- data/spec/rubocop/cop/rspec/hook_argument_spec.rb +0 -189
- data/spec/rubocop/cop/rspec/instance_variable_spec.rb +0 -75
- data/spec/rubocop/cop/rspec/leading_subject_spec.rb +0 -54
- data/spec/rubocop/cop/rspec/let_setup_spec.rb +0 -66
- data/spec/rubocop/cop/rspec/message_chain_spec.rb +0 -21
- data/spec/rubocop/cop/rspec/message_expectation_spec.rb +0 -63
- data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +0 -28
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +0 -84
- data/spec/rubocop/cop/rspec/named_subject_spec.rb +0 -62
- data/spec/rubocop/cop/rspec/nested_groups_spec.rb +0 -55
- data/spec/rubocop/cop/rspec/not_to_not_spec.rb +0 -57
- data/spec/rubocop/cop/rspec/subject_stub_spec.rb +0 -183
- data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +0 -71
- data/spec/rubocop/rspec/config_formatter_spec.rb +0 -48
- data/spec/rubocop/rspec/description_extractor_spec.rb +0 -35
- data/spec/rubocop/rspec/language/selector_set_spec.rb +0 -29
- data/spec/rubocop/rspec/spec_only_spec.rb +0 -97
- data/spec/rubocop/rspec/util/one_spec.rb +0 -21
- data/spec/rubocop/rspec/wording_spec.rb +0 -44
- data/spec/shared/rspec_only_cop_behavior.rb +0 -68
- data/spec/spec_helper.rb +0 -41
- data/spec/support/expect_violation.rb +0 -166
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for usage of implicit subject (`is_expected` / `should`).
|
7
|
+
#
|
8
|
+
# This cop can be configured using the `EnforcedStyle` option
|
9
|
+
#
|
10
|
+
# @example `EnforcedStyle: single_line_only` (default)
|
11
|
+
# # bad
|
12
|
+
# it do
|
13
|
+
# is_expected.to be_truthy
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# it { is_expected.to be_truthy }
|
18
|
+
# it do
|
19
|
+
# expect(subject).to be_truthy
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @example `EnforcedStyle: single_statement_only`
|
23
|
+
# # bad
|
24
|
+
# it do
|
25
|
+
# foo = 1
|
26
|
+
# is_expected.to be_truthy
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# it do
|
31
|
+
# foo = 1
|
32
|
+
# expect(subject).to be_truthy
|
33
|
+
# end
|
34
|
+
# it do
|
35
|
+
# is_expected.to be_truthy
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @example `EnforcedStyle: disallow`
|
39
|
+
# # bad
|
40
|
+
# it { is_expected.to be_truthy }
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# it { expect(subject).to be_truthy }
|
44
|
+
#
|
45
|
+
# @example `EnforcedStyle: require_implicit`
|
46
|
+
# # bad
|
47
|
+
# it { expect(subject).to be_truthy }
|
48
|
+
#
|
49
|
+
# # good
|
50
|
+
# it { is_expected.to be_truthy }
|
51
|
+
#
|
52
|
+
# # bad
|
53
|
+
# it do
|
54
|
+
# expect(subject).to be_truthy
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# # good
|
58
|
+
# it do
|
59
|
+
# is_expected.to be_truthy
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# # good
|
63
|
+
# it { expect(named_subject).to be_truthy }
|
64
|
+
#
|
65
|
+
class ImplicitSubject < Base
|
66
|
+
extend AutoCorrector
|
67
|
+
include ConfigurableEnforcedStyle
|
68
|
+
|
69
|
+
MSG_REQUIRE_EXPLICIT = "Don't use implicit subject."
|
70
|
+
|
71
|
+
MSG_REQUIRE_IMPLICIT = "Don't use explicit subject."
|
72
|
+
|
73
|
+
RESTRICT_ON_SEND = %i[
|
74
|
+
expect
|
75
|
+
is_expected
|
76
|
+
should
|
77
|
+
should_not
|
78
|
+
].freeze
|
79
|
+
|
80
|
+
# @!method explicit_unnamed_subject?(node)
|
81
|
+
def_node_matcher :explicit_unnamed_subject?, <<~PATTERN
|
82
|
+
(send nil? :expect (send nil? :subject))
|
83
|
+
PATTERN
|
84
|
+
|
85
|
+
# @!method implicit_subject?(node)
|
86
|
+
def_node_matcher :implicit_subject?, <<~PATTERN
|
87
|
+
(send nil? {:should :should_not :is_expected} ...)
|
88
|
+
PATTERN
|
89
|
+
|
90
|
+
def on_send(node)
|
91
|
+
return unless invalid?(node)
|
92
|
+
|
93
|
+
add_offense(node) do |corrector|
|
94
|
+
autocorrect(corrector, node)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def autocorrect(corrector, node)
|
101
|
+
case node.method_name
|
102
|
+
when :expect
|
103
|
+
corrector.replace(node, 'is_expected')
|
104
|
+
when :is_expected
|
105
|
+
corrector.replace(node.location.selector, 'expect(subject)')
|
106
|
+
when :should
|
107
|
+
corrector.replace(node.location.selector, 'expect(subject).to')
|
108
|
+
when :should_not
|
109
|
+
corrector.replace(node.location.selector, 'expect(subject).not_to')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def message(_node)
|
114
|
+
case style
|
115
|
+
when :require_implicit
|
116
|
+
MSG_REQUIRE_IMPLICIT
|
117
|
+
else
|
118
|
+
MSG_REQUIRE_EXPLICIT
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def invalid?(node)
|
123
|
+
case style
|
124
|
+
when :require_implicit
|
125
|
+
explicit_unnamed_subject?(node)
|
126
|
+
when :disallow
|
127
|
+
implicit_subject_in_non_its?(node)
|
128
|
+
when :single_line_only
|
129
|
+
implicit_subject_in_non_its_and_non_single_line?(node)
|
130
|
+
when :single_statement_only
|
131
|
+
implicit_subject_in_non_its_and_non_single_statement?(node)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def implicit_subject_in_non_its?(node)
|
136
|
+
implicit_subject?(node) && !its?(node)
|
137
|
+
end
|
138
|
+
|
139
|
+
def implicit_subject_in_non_its_and_non_single_line?(node)
|
140
|
+
implicit_subject_in_non_its?(node) && !single_line?(node)
|
141
|
+
end
|
142
|
+
|
143
|
+
def implicit_subject_in_non_its_and_non_single_statement?(node)
|
144
|
+
implicit_subject_in_non_its?(node) && !single_statement?(node)
|
145
|
+
end
|
146
|
+
|
147
|
+
def its?(node)
|
148
|
+
example_of(node)&.method?(:its)
|
149
|
+
end
|
150
|
+
|
151
|
+
def single_line?(node)
|
152
|
+
example_of(node)&.single_line?
|
153
|
+
end
|
154
|
+
|
155
|
+
def single_statement?(node)
|
156
|
+
!example_of(node)&.body&.begin_type?
|
157
|
+
end
|
158
|
+
|
159
|
+
def example_of(node)
|
160
|
+
node.each_ancestor.find do |ancestor|
|
161
|
+
example?(ancestor)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -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
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for `instance_double` used with `have_received`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# it do
|
11
|
+
# foo = instance_double(Foo).as_null_object
|
12
|
+
# expect(foo).to have_received(:bar)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# it do
|
17
|
+
# foo = instance_spy(Foo)
|
18
|
+
# expect(foo).to have_received(:bar)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class InstanceSpy < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
MSG = 'Use `instance_spy` when you check your double ' \
|
25
|
+
'with `have_received`.'
|
26
|
+
|
27
|
+
# @!method null_double(node)
|
28
|
+
def_node_search :null_double, <<~PATTERN
|
29
|
+
(lvasgn $_
|
30
|
+
(send
|
31
|
+
$(send nil? :instance_double
|
32
|
+
...) :as_null_object))
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
# @!method have_received_usage(node)
|
36
|
+
def_node_search :have_received_usage, <<~PATTERN
|
37
|
+
(send
|
38
|
+
(send nil? :expect
|
39
|
+
(lvar $_)) :to
|
40
|
+
(send nil? :have_received
|
41
|
+
...)
|
42
|
+
...)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
46
|
+
return unless example?(node)
|
47
|
+
|
48
|
+
null_double(node) do |var, receiver|
|
49
|
+
have_received_usage(node) do |expected|
|
50
|
+
next if expected != var
|
51
|
+
|
52
|
+
add_offense(receiver) do |corrector|
|
53
|
+
autocorrect(corrector, receiver)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def autocorrect(corrector, node)
|
62
|
+
replacement = 'instance_spy'
|
63
|
+
corrector.replace(node.loc.selector, replacement)
|
64
|
+
|
65
|
+
double_source_map = node.parent.loc
|
66
|
+
as_null_object_range = double_source_map
|
67
|
+
.dot
|
68
|
+
.join(double_source_map.selector)
|
69
|
+
corrector.remove(as_null_object_range)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -24,10 +24,9 @@ module RuboCop
|
|
24
24
|
# end
|
25
25
|
#
|
26
26
|
# @example with AssignmentOnly configuration
|
27
|
-
#
|
28
27
|
# # rubocop.yml
|
29
|
-
# RSpec/InstanceVariable:
|
30
|
-
#
|
28
|
+
# # RSpec/InstanceVariable:
|
29
|
+
# # AssignmentOnly: true
|
31
30
|
#
|
32
31
|
# # bad
|
33
32
|
# describe MyClass do
|
@@ -46,33 +45,48 @@ module RuboCop
|
|
46
45
|
# it { expect(foo).to be_empty }
|
47
46
|
# end
|
48
47
|
#
|
49
|
-
class InstanceVariable <
|
50
|
-
include
|
48
|
+
class InstanceVariable < Base
|
49
|
+
include TopLevelGroup
|
51
50
|
|
52
|
-
|
51
|
+
MSG = 'Avoid instance variables - use let, ' \
|
52
|
+
'a method call, or a local variable (if possible).'
|
53
53
|
|
54
|
-
|
54
|
+
# @!method dynamic_class?(node)
|
55
|
+
def_node_matcher :dynamic_class?, <<~PATTERN
|
56
|
+
(block (send (const nil? :Class) :new ...) ...)
|
57
|
+
PATTERN
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
# @!method custom_matcher?(node)
|
60
|
+
def_node_matcher :custom_matcher?, <<~PATTERN
|
61
|
+
(block {
|
62
|
+
(send nil? :matcher sym)
|
63
|
+
(send (const (const nil? :RSpec) :Matchers) :define sym)
|
64
|
+
} ...)
|
58
65
|
PATTERN
|
59
66
|
|
67
|
+
# @!method ivar_usage(node)
|
60
68
|
def_node_search :ivar_usage, '$(ivar $_)'
|
61
69
|
|
70
|
+
# @!method ivar_assigned?(node)
|
62
71
|
def_node_search :ivar_assigned?, '(ivasgn % ...)'
|
63
72
|
|
64
|
-
def
|
65
|
-
return unless spec_group?(node)
|
66
|
-
|
73
|
+
def on_top_level_group(node)
|
67
74
|
ivar_usage(node) do |ivar, name|
|
68
|
-
|
75
|
+
next if valid_usage?(ivar)
|
76
|
+
next if assignment_only? && !ivar_assigned?(node, name)
|
69
77
|
|
70
|
-
add_offense(ivar
|
78
|
+
add_offense(ivar)
|
71
79
|
end
|
72
80
|
end
|
73
81
|
|
74
82
|
private
|
75
83
|
|
84
|
+
def valid_usage?(node)
|
85
|
+
node.each_ancestor(:block).any? do |block|
|
86
|
+
dynamic_class?(block) || custom_matcher?(block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
76
90
|
def assignment_only?
|
77
91
|
cop_config['AssignmentOnly']
|
78
92
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Check for `specify` with `is_expected` and one-liner expectations.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# specify { is_expected.to be_truthy }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# it { is_expected.to be_truthy }
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# specify do
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# specify { expect(sqrt(4)).to eq(2) }
|
20
|
+
#
|
21
|
+
class IsExpectedSpecify < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
RESTRICT_ON_SEND = %i[specify].freeze
|
25
|
+
IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
|
26
|
+
MSG = 'Use `it` instead of `specify`.'
|
27
|
+
|
28
|
+
# @!method offense?(node)
|
29
|
+
def_node_matcher :offense?, <<~PATTERN
|
30
|
+
(block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
block_node = node.parent
|
35
|
+
return unless block_node&.single_line? && offense?(block_node)
|
36
|
+
|
37
|
+
selector = node.loc.selector
|
38
|
+
add_offense(selector) do |corrector|
|
39
|
+
corrector.replace(selector, 'it')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that only one `it_behaves_like` style is used.
|
7
|
+
#
|
8
|
+
# @example `EnforcedStyle: it_behaves_like` (default)
|
9
|
+
# # bad
|
10
|
+
# it_should_behave_like 'a foo'
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# it_behaves_like 'a foo'
|
14
|
+
#
|
15
|
+
# @example `EnforcedStyle: it_should_behave_like`
|
16
|
+
# # bad
|
17
|
+
# it_behaves_like 'a foo'
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# it_should_behave_like 'a foo'
|
21
|
+
#
|
22
|
+
class ItBehavesLike < Base
|
23
|
+
extend AutoCorrector
|
24
|
+
include ConfigurableEnforcedStyle
|
25
|
+
|
26
|
+
MSG = 'Prefer `%<replacement>s` over `%<original>s` when including ' \
|
27
|
+
'examples in a nested context.'
|
28
|
+
RESTRICT_ON_SEND = %i[it_behaves_like it_should_behave_like].freeze
|
29
|
+
|
30
|
+
# @!method example_inclusion_offense(node)
|
31
|
+
def_node_matcher :example_inclusion_offense, '(send _ % ...)'
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
example_inclusion_offense(node, alternative_style) do
|
35
|
+
add_offense(node) do |corrector|
|
36
|
+
corrector.replace(node.loc.selector, style.to_s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def message(_node)
|
44
|
+
format(MSG, replacement: style, original: alternative_style)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Check that `all` matcher is used instead of iterating over an array.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# it 'validates users' do
|
11
|
+
# [user1, user2, user3].each { |user| expect(user).to be_valid }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# it 'validates users' do
|
16
|
+
# expect([user1, user2, user3]).to all(be_valid)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
class IteratedExpectation < Base
|
20
|
+
MSG = 'Prefer using the `all` matcher instead ' \
|
21
|
+
'of iterating over an array.'
|
22
|
+
|
23
|
+
# @!method each?(node)
|
24
|
+
def_node_matcher :each?, <<~PATTERN
|
25
|
+
(block
|
26
|
+
(send ... :each)
|
27
|
+
(args (arg $_))
|
28
|
+
$(...)
|
29
|
+
)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
# @!method each_numblock?(node)
|
33
|
+
def_node_matcher :each_numblock?, <<~PATTERN
|
34
|
+
(numblock
|
35
|
+
(send ... :each) _ $(...)
|
36
|
+
)
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
# @!method expectation?(node)
|
40
|
+
def_node_matcher :expectation?, <<~PATTERN
|
41
|
+
(send (send nil? :expect (lvar %)) :to ...)
|
42
|
+
PATTERN
|
43
|
+
|
44
|
+
def on_block(node)
|
45
|
+
each?(node) do |arg, body|
|
46
|
+
if single_expectation?(body, arg) || only_expectations?(body, arg)
|
47
|
+
add_offense(node.send_node)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_numblock(node)
|
53
|
+
each_numblock?(node) do |body|
|
54
|
+
if single_expectation?(body, :_1) || only_expectations?(body, :_1)
|
55
|
+
add_offense(node.send_node)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def single_expectation?(body, arg)
|
63
|
+
expectation?(body, arg)
|
64
|
+
end
|
65
|
+
|
66
|
+
def only_expectations?(body, arg)
|
67
|
+
return false unless body.each_child_node.any?
|
68
|
+
|
69
|
+
body.each_child_node.all? { |child| expectation?(child, arg) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|