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
@@ -6,10 +6,14 @@ module RuboCop
|
|
6
6
|
# Checks that tests use `described_class`.
|
7
7
|
#
|
8
8
|
# If the first argument of describe is a class, the class is exposed to
|
9
|
-
# each example via described_class
|
10
|
-
# repeating the class.
|
9
|
+
# each example via described_class.
|
11
10
|
#
|
12
|
-
#
|
11
|
+
# This cop can be configured using the `EnforcedStyle`, `SkipBlocks`
|
12
|
+
# and `OnlyStaticConstants` options.
|
13
|
+
# `OnlyStaticConstants` is only relevant when `EnforcedStyle` is
|
14
|
+
# `described_class`.
|
15
|
+
#
|
16
|
+
# @example `EnforcedStyle: described_class` (default)
|
13
17
|
# # bad
|
14
18
|
# describe MyClass do
|
15
19
|
# subject { MyClass.do_something }
|
@@ -19,64 +23,207 @@ module RuboCop
|
|
19
23
|
# describe MyClass do
|
20
24
|
# subject { described_class.do_something }
|
21
25
|
# end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
#
|
27
|
+
# @example `OnlyStaticConstants: true` (default)
|
28
|
+
# # good
|
29
|
+
# describe MyClass do
|
30
|
+
# subject { MyClass::CONSTANT }
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @example `OnlyStaticConstants: false`
|
34
|
+
# # bad
|
35
|
+
# describe MyClass do
|
36
|
+
# subject { MyClass::CONSTANT }
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# @example `EnforcedStyle: explicit`
|
40
|
+
# # bad
|
41
|
+
# describe MyClass do
|
42
|
+
# subject { described_class.do_something }
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# describe MyClass do
|
47
|
+
# subject { MyClass.do_something }
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# There's a known caveat with rspec-rails's `controller` helper that
|
51
|
+
# runs its block in a different context, and `described_class` is not
|
52
|
+
# available to it. `SkipBlocks` option excludes detection in all
|
53
|
+
# non-RSpec related blocks.
|
54
|
+
#
|
55
|
+
# To narrow down this setting to only a specific directory, it is
|
56
|
+
# possible to use an overriding configuration file local to that
|
57
|
+
# directory.
|
58
|
+
#
|
59
|
+
# @example `SkipBlocks: true`
|
60
|
+
# # spec/controllers/.rubocop.yml
|
61
|
+
# # RSpec/DescribedClass:
|
62
|
+
# # SkipBlocks: true
|
63
|
+
#
|
64
|
+
# # acceptable
|
65
|
+
# describe MyConcern do
|
66
|
+
# controller(ApplicationController) do
|
67
|
+
# include MyConcern
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
class DescribedClass < Base # rubocop:disable Metrics/ClassLength
|
72
|
+
extend AutoCorrector
|
73
|
+
include ConfigurableEnforcedStyle
|
74
|
+
include Namespace
|
27
75
|
|
28
|
-
|
76
|
+
DESCRIBED_CLASS = 'described_class'
|
77
|
+
MSG = 'Use `%<replacement>s` instead of `%<src>s`.'
|
29
78
|
|
30
|
-
|
31
|
-
|
79
|
+
# @!method common_instance_exec_closure?(node)
|
80
|
+
def_node_matcher :common_instance_exec_closure?, <<~PATTERN
|
81
|
+
(block (send (const nil? {:Class :Module :Struct}) :new ...) ...)
|
32
82
|
PATTERN
|
33
83
|
|
34
|
-
|
35
|
-
|
36
|
-
|
84
|
+
# @!method rspec_block?(node)
|
85
|
+
def_node_matcher :rspec_block?,
|
86
|
+
'({block numblock} (send #rspec? #ALL.all ...) ...)'
|
37
87
|
|
88
|
+
# @!method scope_changing_syntax?(node)
|
38
89
|
def_node_matcher :scope_changing_syntax?, '{def class module}'
|
39
90
|
|
40
|
-
|
41
|
-
|
42
|
-
|
91
|
+
# @!method described_constant(node)
|
92
|
+
def_node_matcher :described_constant, <<~PATTERN
|
93
|
+
(block (send _ :describe $(const ...) ...) (args) $_)
|
94
|
+
PATTERN
|
95
|
+
|
96
|
+
# @!method contains_described_class?(node)
|
97
|
+
def_node_search :contains_described_class?,
|
98
|
+
'(send nil? :described_class)'
|
43
99
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
100
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
101
|
+
# In case the explicit style is used, we need to remember what's
|
102
|
+
# being described.
|
103
|
+
@described_class, body = described_constant(node)
|
104
|
+
|
105
|
+
return unless body
|
48
106
|
|
49
|
-
|
50
|
-
|
51
|
-
|
107
|
+
find_usage(body) do |match|
|
108
|
+
msg = message(match.const_name)
|
109
|
+
add_offense(match, message: msg) do |corrector|
|
110
|
+
autocorrect(corrector, match)
|
111
|
+
end
|
52
112
|
end
|
53
113
|
end
|
54
114
|
|
55
115
|
private
|
56
116
|
|
57
|
-
def
|
58
|
-
|
117
|
+
def autocorrect(corrector, match)
|
118
|
+
replacement = if style == :described_class
|
119
|
+
DESCRIBED_CLASS
|
120
|
+
else
|
121
|
+
@described_class.const_name
|
122
|
+
end
|
123
|
+
|
124
|
+
corrector.replace(match, replacement)
|
125
|
+
end
|
126
|
+
|
127
|
+
def find_usage(node, &block)
|
128
|
+
yield(node) if offensive?(node)
|
129
|
+
return if scope_change?(node) || allowed?(node)
|
59
130
|
|
60
|
-
|
61
|
-
|
131
|
+
node.each_child_node do |child|
|
132
|
+
find_usage(child, &block)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def allowed?(node)
|
137
|
+
node.const_type? && only_static_constants?
|
138
|
+
end
|
62
139
|
|
63
|
-
|
64
|
-
|
140
|
+
def message(offense)
|
141
|
+
if style == :described_class
|
142
|
+
format(MSG, replacement: DESCRIBED_CLASS, src: offense)
|
143
|
+
else
|
144
|
+
format(MSG, replacement: @described_class.const_name,
|
145
|
+
src: DESCRIBED_CLASS)
|
65
146
|
end
|
66
147
|
end
|
67
148
|
|
68
149
|
def scope_change?(node)
|
69
|
-
scope_changing_syntax?(node)
|
150
|
+
scope_changing_syntax?(node) ||
|
70
151
|
common_instance_exec_closure?(node) ||
|
71
152
|
skippable_block?(node)
|
72
153
|
end
|
73
154
|
|
74
155
|
def skippable_block?(node)
|
75
|
-
node.block_type? && !rspec_block?(node) &&
|
156
|
+
node.block_type? && !rspec_block?(node) && cop_config['SkipBlocks']
|
157
|
+
end
|
158
|
+
|
159
|
+
def only_static_constants?
|
160
|
+
cop_config.fetch('OnlyStaticConstants', true)
|
161
|
+
end
|
162
|
+
|
163
|
+
def offensive?(node)
|
164
|
+
if style == :described_class
|
165
|
+
offensive_described_class?(node)
|
166
|
+
else
|
167
|
+
node.send_type? && node.method?(:described_class)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def offensive_described_class?(node)
|
172
|
+
return false unless node.const_type?
|
173
|
+
|
174
|
+
# E.g. `described_class::CONSTANT`
|
175
|
+
return false if contains_described_class?(node)
|
176
|
+
|
177
|
+
nearest_described_class, = node.each_ancestor(:block)
|
178
|
+
.map { |ancestor| described_constant(ancestor) }.find(&:itself)
|
179
|
+
|
180
|
+
return false if nearest_described_class.equal?(node)
|
181
|
+
|
182
|
+
full_const_name(nearest_described_class) == full_const_name(node)
|
183
|
+
end
|
184
|
+
|
185
|
+
def full_const_name(node)
|
186
|
+
symbolized_namespace = namespace(node).map(&:to_sym)
|
187
|
+
collapse_namespace(symbolized_namespace, const_name(node))
|
76
188
|
end
|
77
189
|
|
78
|
-
|
79
|
-
|
190
|
+
# @param namespace [Array<Symbol>]
|
191
|
+
# @param const [Array<Symbol>]
|
192
|
+
# @return [Array<Symbol>]
|
193
|
+
# @example
|
194
|
+
# # nil represents base constant
|
195
|
+
# collapse_namespace([], [:C]) # => [:C]
|
196
|
+
# collapse_namespace([:A, :B], [:C]) # => [:A, :B, :C]
|
197
|
+
# collapse_namespace([:A, :B], [:B, :C]) # => [:A, :B, :C]
|
198
|
+
# collapse_namespace([:A, :B], [nil, :C]) # => [nil, :C]
|
199
|
+
# collapse_namespace([:A, :B], [nil, :B, :C]) # => [nil, :B, :C]
|
200
|
+
def collapse_namespace(namespace, const)
|
201
|
+
return const if namespace.empty? || const.first.nil?
|
202
|
+
|
203
|
+
start = [0, (namespace.length - const.length)].max
|
204
|
+
max = namespace.length
|
205
|
+
intersection = (start..max).find do |shift|
|
206
|
+
namespace[shift, max - shift] == const[0, max - shift]
|
207
|
+
end
|
208
|
+
[*namespace[0, intersection], *const]
|
209
|
+
end
|
210
|
+
|
211
|
+
# @param node [RuboCop::AST::Node]
|
212
|
+
# @return [Array<Symbol>]
|
213
|
+
# @example
|
214
|
+
# const_name(s(:const, nil, :C)) # => [:C]
|
215
|
+
# const_name(s(:const, s(:const, nil, :M), :C)) # => [:M, :C]
|
216
|
+
# const_name(s(:const, s(:cbase), :C)) # => [nil, :C]
|
217
|
+
def const_name(node)
|
218
|
+
namespace = node.namespace
|
219
|
+
name = node.short_name
|
220
|
+
if !namespace
|
221
|
+
[name]
|
222
|
+
elsif namespace.const_type?
|
223
|
+
[*const_name(namespace), name]
|
224
|
+
elsif %i[lvar cbase send].include?(namespace.type)
|
225
|
+
[nil, name]
|
226
|
+
end
|
80
227
|
end
|
81
228
|
end
|
82
229
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Avoid opening modules and defining specs within them.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# module MyModule
|
11
|
+
# RSpec.describe MyClass do
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# RSpec.describe MyModule::MyClass do
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @see https://github.com/rubocop/rubocop-rspec/issues/735
|
22
|
+
class DescribedClassModuleWrapping < Base
|
23
|
+
MSG = 'Avoid opening modules and defining specs within them.'
|
24
|
+
|
25
|
+
# @!method include_rspec_blocks?(node)
|
26
|
+
def_node_search :include_rspec_blocks?, <<~PATTERN
|
27
|
+
({block numblock} (send #explicit_rspec? #ExampleGroups.all ...) ...)
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def on_module(node)
|
31
|
+
return unless include_rspec_blocks?(node)
|
32
|
+
|
33
|
+
add_offense(node)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Enforces custom RSpec dialects.
|
7
|
+
#
|
8
|
+
# A dialect can be based on the following RSpec methods:
|
9
|
+
#
|
10
|
+
# - describe, context, feature, example_group
|
11
|
+
# - xdescribe, xcontext, xfeature
|
12
|
+
# - fdescribe, fcontext, ffeature
|
13
|
+
# - shared_examples, shared_examples_for, shared_context
|
14
|
+
# - it, specify, example, scenario, its
|
15
|
+
# - fit, fspecify, fexample, fscenario, focus
|
16
|
+
# - xit, xspecify, xexample, xscenario, skip
|
17
|
+
# - pending
|
18
|
+
# - prepend_before, before, append_before,
|
19
|
+
# - around
|
20
|
+
# - prepend_after, after, append_after
|
21
|
+
# - let, let!
|
22
|
+
# - subject, subject!
|
23
|
+
# - expect, is_expected, expect_any_instance_of
|
24
|
+
#
|
25
|
+
# By default all of the RSpec methods and aliases are allowed. By setting
|
26
|
+
# a config like:
|
27
|
+
#
|
28
|
+
# RSpec/Dialect:
|
29
|
+
# PreferredMethods:
|
30
|
+
# context: describe
|
31
|
+
#
|
32
|
+
# If you were previously using the `RSpec/Capybara/FeatureMethods` cop and
|
33
|
+
# want to keep disabling all Capybara-specific methods that have the same
|
34
|
+
# native RSpec method (e.g. are just aliases), use the following config:
|
35
|
+
#
|
36
|
+
# RSpec/Dialect:
|
37
|
+
# PreferredMethods:
|
38
|
+
# background: :before
|
39
|
+
# scenario: :it
|
40
|
+
# xscenario: :xit
|
41
|
+
# given: :let
|
42
|
+
# given!: :let!
|
43
|
+
# feature: :describe
|
44
|
+
#
|
45
|
+
# You can expect the following behavior:
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# # bad
|
49
|
+
# context 'display name presence' do
|
50
|
+
# # ...
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # good
|
54
|
+
# describe 'display name presence' do
|
55
|
+
# # ...
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
class Dialect < Base
|
59
|
+
extend AutoCorrector
|
60
|
+
include MethodPreference
|
61
|
+
|
62
|
+
MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
|
63
|
+
|
64
|
+
# @!method rspec_method?(node)
|
65
|
+
def_node_matcher :rspec_method?, '(send #rspec? #ALL.all ...)'
|
66
|
+
|
67
|
+
def on_send(node)
|
68
|
+
return unless rspec_method?(node)
|
69
|
+
return unless preferred_methods[node.method_name]
|
70
|
+
|
71
|
+
msg = format(MSG, prefer: preferred_method(node.method_name),
|
72
|
+
current: node.method_name)
|
73
|
+
|
74
|
+
add_offense(node, message: msg) do |corrector|
|
75
|
+
current = node.loc.selector
|
76
|
+
preferred = preferred_method(current.source)
|
77
|
+
|
78
|
+
corrector.replace(current, preferred)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Avoid duplicated metadata.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# describe 'Something', :a, :a
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# describe 'Something', :a
|
14
|
+
class DuplicatedMetadata < Base
|
15
|
+
extend AutoCorrector
|
16
|
+
|
17
|
+
include Metadata
|
18
|
+
include RangeHelp
|
19
|
+
|
20
|
+
MSG = 'Avoid duplicated metadata.'
|
21
|
+
|
22
|
+
def on_metadata(symbols, _hash)
|
23
|
+
symbols.each do |symbol|
|
24
|
+
on_metadata_symbol(symbol)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def on_metadata_symbol(node)
|
31
|
+
return unless duplicated?(node)
|
32
|
+
|
33
|
+
add_offense(node) do |corrector|
|
34
|
+
autocorrect(corrector, node)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def autocorrect(corrector, node)
|
39
|
+
corrector.remove(
|
40
|
+
range_with_surrounding_comma(
|
41
|
+
range_with_surrounding_space(
|
42
|
+
node.source_range,
|
43
|
+
side: :left
|
44
|
+
),
|
45
|
+
:left
|
46
|
+
)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def duplicated?(node)
|
51
|
+
node.left_siblings.any? do |sibling|
|
52
|
+
sibling.eql?(node)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -5,10 +5,7 @@ module RuboCop
|
|
5
5
|
module RSpec
|
6
6
|
# Checks if an example group does not include any tests.
|
7
7
|
#
|
8
|
-
# This cop is configurable using the `CustomIncludeMethods` option
|
9
|
-
#
|
10
8
|
# @example usage
|
11
|
-
#
|
12
9
|
# # bad
|
13
10
|
# describe Bacon do
|
14
11
|
# let(:bacon) { Bacon.new(chunkiness) }
|
@@ -33,66 +30,156 @@ module RuboCop
|
|
33
30
|
# end
|
34
31
|
# end
|
35
32
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# # .rubocop.yml
|
39
|
-
# RSpec/EmptyExampleGroup:
|
40
|
-
# CustomIncludeMethods:
|
41
|
-
# - include_tests
|
42
|
-
#
|
43
|
-
# # spec_helper.rb
|
44
|
-
# RSpec.configure do |config|
|
45
|
-
# config.alias_it_behaves_like_to(:include_tests)
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# # bacon_spec.rb
|
33
|
+
# # good
|
49
34
|
# describe Bacon do
|
50
|
-
#
|
51
|
-
# let(:chunkiness) { false }
|
52
|
-
#
|
53
|
-
# context 'extra chunky' do # not flagged by rubocop
|
54
|
-
# let(:chunkiness) { true }
|
55
|
-
#
|
56
|
-
# include_tests 'shared tests'
|
57
|
-
# end
|
35
|
+
# pending 'will add tests later'
|
58
36
|
# end
|
59
37
|
#
|
60
|
-
class EmptyExampleGroup <
|
61
|
-
|
62
|
-
|
63
|
-
|
38
|
+
class EmptyExampleGroup < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
|
41
|
+
include RangeHelp
|
42
|
+
|
43
|
+
MSG = 'Empty example group detected.'
|
44
|
+
|
45
|
+
# @!method example_group_body(node)
|
46
|
+
# Match example group blocks and yield their body
|
47
|
+
#
|
48
|
+
# @example source that matches
|
49
|
+
# describe 'example group' do
|
50
|
+
# it { is_expected.to be }
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @param node [RuboCop::AST::Node]
|
54
|
+
# @yield [RuboCop::AST::Node] example group body
|
55
|
+
def_node_matcher :example_group_body, <<~PATTERN
|
56
|
+
(block (send #rspec? #ExampleGroups.all ...) args $_)
|
57
|
+
PATTERN
|
64
58
|
|
65
|
-
|
59
|
+
# @!method example_or_group_or_include?(node)
|
60
|
+
# Match examples, example groups and includes
|
61
|
+
#
|
62
|
+
# @example source that matches
|
63
|
+
# it { is_expected.to fly }
|
64
|
+
# describe('non-empty example groups too') { }
|
65
|
+
# it_behaves_like 'an animal'
|
66
|
+
# it_behaves_like('a cat') { let(:food) { 'milk' } }
|
67
|
+
# it_has_root_access
|
68
|
+
# skip
|
69
|
+
# it 'will be implemented later'
|
70
|
+
#
|
71
|
+
# @param node [RuboCop::AST::Node]
|
72
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
73
|
+
def_node_matcher :example_or_group_or_include?, <<~PATTERN
|
74
|
+
{
|
75
|
+
(block
|
76
|
+
(send #rspec? {#Examples.all #ExampleGroups.all #Includes.all} ...)
|
77
|
+
...)
|
78
|
+
(send nil? {#Examples.all #Includes.all} ...)
|
79
|
+
}
|
80
|
+
PATTERN
|
81
|
+
|
82
|
+
# @!method examples_inside_block?(node)
|
83
|
+
# Match examples defined inside a block which is not a hook
|
84
|
+
#
|
85
|
+
# @example source that matches
|
86
|
+
# %w(r g b).each do |color|
|
87
|
+
# it { is_expected.to have_color(color) }
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @example source that does not match
|
91
|
+
# before do
|
92
|
+
# it { is_expected.to fall_into_oblivion }
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# @param node [RuboCop::AST::Node]
|
96
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
97
|
+
def_node_matcher :examples_inside_block?, <<~PATTERN
|
98
|
+
(block !(send nil? #Hooks.all ...) _ #examples?)
|
99
|
+
PATTERN
|
100
|
+
|
101
|
+
# @!method examples_directly_or_in_block?(node)
|
102
|
+
# Match examples or examples inside blocks
|
103
|
+
#
|
104
|
+
# @example source that matches
|
105
|
+
# it { expect(drink).to be_cold }
|
106
|
+
# context('when winter') { it { expect(drink).to be_hot } }
|
107
|
+
# (1..5).each { |divisor| it { is_expected.to divide_by(divisor) } }
|
108
|
+
#
|
109
|
+
# @param node [RuboCop::AST::Node]
|
110
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
111
|
+
def_node_matcher :examples_directly_or_in_block?, <<~PATTERN
|
112
|
+
{
|
113
|
+
#example_or_group_or_include?
|
114
|
+
#examples_inside_block?
|
115
|
+
}
|
116
|
+
PATTERN
|
66
117
|
|
67
|
-
|
118
|
+
# @!method examples?(node)
|
119
|
+
# Matches examples defined in scopes where they could run
|
120
|
+
#
|
121
|
+
# @example source that matches
|
122
|
+
# it { expect(myself).to be_run }
|
123
|
+
# describe { it { i_run_as_well } }
|
124
|
+
#
|
125
|
+
# @example source that does not match
|
126
|
+
# before { it { whatever here won't run anyway } }
|
127
|
+
#
|
128
|
+
# @param node [RuboCop::AST::Node]
|
129
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
130
|
+
def_node_matcher :examples?, <<~PATTERN
|
68
131
|
{
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
:it_should_behave_like
|
73
|
-
:include_context
|
74
|
-
:include_examples
|
75
|
-
} ...)
|
76
|
-
(send _ #custom_include? ...)
|
132
|
+
#examples_directly_or_in_block?
|
133
|
+
(begin <#examples_directly_or_in_block? ...>)
|
134
|
+
(begin <#examples_in_branches? ...>)
|
77
135
|
}
|
78
136
|
PATTERN
|
79
137
|
|
80
|
-
def on_block(node)
|
81
|
-
return
|
138
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
139
|
+
return if node.each_ancestor(:def, :defs).any?
|
140
|
+
return if node.each_ancestor(:block).any? { |block| example?(block) }
|
82
141
|
|
83
|
-
|
142
|
+
example_group_body(node) do |body|
|
143
|
+
next unless offensive?(body)
|
144
|
+
|
145
|
+
add_offense(node.send_node) do |corrector|
|
146
|
+
corrector.remove(removed_range(node))
|
147
|
+
end
|
148
|
+
end
|
84
149
|
end
|
85
150
|
|
86
151
|
private
|
87
152
|
|
88
|
-
def
|
89
|
-
|
153
|
+
def offensive?(body)
|
154
|
+
return true unless body
|
155
|
+
return false if conditionals_with_examples?(body)
|
156
|
+
|
157
|
+
if body.if_type? || body.case_type?
|
158
|
+
!examples_in_branches?(body)
|
159
|
+
else
|
160
|
+
!examples?(body)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def conditionals_with_examples?(body)
|
165
|
+
return false unless body.begin_type? || body.case_type?
|
166
|
+
|
167
|
+
body.each_descendant(:if, :case).any? do |condition_node|
|
168
|
+
examples_in_branches?(condition_node)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def examples_in_branches?(condition_node)
|
173
|
+
return false if !condition_node.if_type? && !condition_node.case_type?
|
174
|
+
|
175
|
+
condition_node.branches.any? { |branch| examples?(branch) }
|
90
176
|
end
|
91
177
|
|
92
|
-
def
|
93
|
-
|
94
|
-
.
|
95
|
-
|
178
|
+
def removed_range(node)
|
179
|
+
range_by_whole_lines(
|
180
|
+
node.source_range,
|
181
|
+
include_final_newline: true
|
182
|
+
)
|
96
183
|
end
|
97
184
|
end
|
98
185
|
end
|