rubocop-rspec 1.7.0 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +955 -79
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/MIT-LICENSE.md +1 -2
  5. data/README.md +35 -35
  6. data/config/default.yml +940 -52
  7. data/config/obsoletion.yml +30 -0
  8. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +49 -0
  9. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +49 -0
  10. data/lib/rubocop/cop/rspec/any_instance.rb +10 -13
  11. data/lib/rubocop/cop/rspec/around_block.rb +97 -0
  12. data/lib/rubocop/cop/rspec/base.rb +26 -0
  13. data/lib/rubocop/cop/rspec/be.rb +39 -0
  14. data/lib/rubocop/cop/rspec/be_empty.rb +45 -0
  15. data/lib/rubocop/cop/rspec/be_eq.rb +47 -0
  16. data/lib/rubocop/cop/rspec/be_eql.rb +18 -15
  17. data/lib/rubocop/cop/rspec/be_nil.rb +74 -0
  18. data/lib/rubocop/cop/rspec/before_after_all.rb +45 -0
  19. data/lib/rubocop/cop/rspec/change_by_zero.rb +184 -0
  20. data/lib/rubocop/cop/rspec/class_check.rb +101 -0
  21. data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
  22. data/lib/rubocop/cop/rspec/context_method.rb +57 -0
  23. data/lib/rubocop/cop/rspec/context_wording.rb +117 -0
  24. data/lib/rubocop/cop/rspec/describe_class.rb +52 -21
  25. data/lib/rubocop/cop/rspec/describe_method.rb +26 -11
  26. data/lib/rubocop/cop/rspec/describe_symbol.rb +37 -0
  27. data/lib/rubocop/cop/rspec/described_class.rb +181 -34
  28. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +38 -0
  29. data/lib/rubocop/cop/rspec/dialect.rb +84 -0
  30. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +58 -0
  31. data/lib/rubocop/cop/rspec/empty_example_group.rb +134 -47
  32. data/lib/rubocop/cop/rspec/empty_hook.rb +49 -0
  33. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +82 -0
  34. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +42 -0
  35. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +40 -0
  36. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +82 -0
  37. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +36 -0
  38. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  39. data/lib/rubocop/cop/rspec/empty_output.rb +47 -0
  40. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  41. data/lib/rubocop/cop/rspec/example_length.rb +38 -20
  42. data/lib/rubocop/cop/rspec/example_without_description.rb +98 -0
  43. data/lib/rubocop/cop/rspec/example_wording.rb +117 -27
  44. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +110 -0
  45. data/lib/rubocop/cop/rspec/expect_actual.rb +46 -20
  46. data/lib/rubocop/cop/rspec/expect_change.rb +86 -0
  47. data/lib/rubocop/cop/rspec/expect_in_hook.rb +50 -0
  48. data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
  49. data/lib/rubocop/cop/rspec/expect_output.rb +50 -0
  50. data/lib/rubocop/cop/rspec/focus.rb +79 -25
  51. data/lib/rubocop/cop/rspec/hook_argument.rb +48 -36
  52. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +81 -0
  53. data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +37 -0
  54. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +68 -0
  55. data/lib/rubocop/cop/rspec/implicit_expect.rb +100 -0
  56. data/lib/rubocop/cop/rspec/implicit_subject.rb +167 -0
  57. data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
  58. data/lib/rubocop/cop/rspec/instance_spy.rb +74 -0
  59. data/lib/rubocop/cop/rspec/instance_variable.rb +28 -14
  60. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  61. data/lib/rubocop/cop/rspec/it_behaves_like.rb +49 -0
  62. data/lib/rubocop/cop/rspec/iterated_expectation.rb +74 -0
  63. data/lib/rubocop/cop/rspec/leading_subject.rb +57 -29
  64. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +127 -0
  65. data/lib/rubocop/cop/rspec/let_before_examples.rb +101 -0
  66. data/lib/rubocop/cop/rspec/let_setup.rb +32 -16
  67. data/lib/rubocop/cop/rspec/match_array.rb +59 -0
  68. data/lib/rubocop/cop/rspec/message_chain.rb +10 -15
  69. data/lib/rubocop/cop/rspec/message_expectation.rb +12 -9
  70. data/lib/rubocop/cop/rspec/message_spies.rb +88 -0
  71. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  72. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
  73. data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
  74. data/lib/rubocop/cop/rspec/mixin/comments_help.rb +38 -0
  75. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +59 -0
  76. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  77. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  78. data/lib/rubocop/cop/rspec/mixin/inside_example_group.rb +29 -0
  79. data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
  80. data/lib/rubocop/cop/rspec/mixin/metadata.rb +63 -0
  81. data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
  82. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +39 -0
  83. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  84. data/lib/rubocop/cop/rspec/mixin/variable.rb +21 -0
  85. data/lib/rubocop/cop/rspec/multiple_describes.rb +14 -12
  86. data/lib/rubocop/cop/rspec/multiple_expectations.rb +86 -26
  87. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +146 -0
  88. data/lib/rubocop/cop/rspec/multiple_subjects.rb +97 -0
  89. data/lib/rubocop/cop/rspec/named_subject.rb +107 -27
  90. data/lib/rubocop/cop/rspec/nested_groups.rb +84 -47
  91. data/lib/rubocop/cop/rspec/no_expectation_example.rb +102 -0
  92. data/lib/rubocop/cop/rspec/not_to_not.rb +30 -27
  93. data/lib/rubocop/cop/rspec/overwriting_setup.rb +74 -0
  94. data/lib/rubocop/cop/rspec/pending.rb +80 -0
  95. data/lib/rubocop/cop/rspec/pending_without_reason.rb +159 -0
  96. data/lib/rubocop/cop/rspec/predicate_matcher.rb +341 -0
  97. data/lib/rubocop/cop/rspec/receive_counts.rb +89 -0
  98. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  99. data/lib/rubocop/cop/rspec/receive_never.rb +41 -0
  100. data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
  101. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  102. data/lib/rubocop/cop/rspec/remove_const.rb +39 -0
  103. data/lib/rubocop/cop/rspec/repeated_description.rb +98 -0
  104. data/lib/rubocop/cop/rspec/repeated_example.rb +53 -0
  105. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +100 -0
  106. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +96 -0
  107. data/lib/rubocop/cop/rspec/repeated_include_example.rb +105 -0
  108. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +125 -0
  109. data/lib/rubocop/cop/rspec/return_from_stub.rb +169 -0
  110. data/lib/rubocop/cop/rspec/scattered_let.rb +59 -0
  111. data/lib/rubocop/cop/rspec/scattered_setup.rb +92 -0
  112. data/lib/rubocop/cop/rspec/shared_context.rb +107 -0
  113. data/lib/rubocop/cop/rspec/shared_examples.rb +125 -0
  114. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +93 -0
  115. data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
  116. data/lib/rubocop/cop/rspec/sort_metadata.rb +71 -0
  117. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  118. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  119. data/lib/rubocop/cop/rspec/stubbed_mock.rb +176 -0
  120. data/lib/rubocop/cop/rspec/subject_declaration.rb +46 -0
  121. data/lib/rubocop/cop/rspec/subject_stub.rb +93 -74
  122. data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +69 -0
  123. data/lib/rubocop/cop/rspec/unspecified_exception.rb +67 -0
  124. data/lib/rubocop/cop/rspec/variable_definition.rb +77 -0
  125. data/lib/rubocop/cop/rspec/variable_name.rb +68 -0
  126. data/lib/rubocop/cop/rspec/verified_double_reference.rb +111 -0
  127. data/lib/rubocop/cop/rspec/verified_doubles.rb +28 -14
  128. data/lib/rubocop/cop/rspec/void_expect.rb +60 -0
  129. data/lib/rubocop/cop/rspec/yield.rb +82 -0
  130. data/lib/rubocop/cop/rspec_cops.rb +112 -0
  131. data/lib/rubocop/rspec/align_let_brace.rb +63 -0
  132. data/lib/rubocop/rspec/concept.rb +33 -0
  133. data/lib/rubocop/rspec/config_formatter.rb +27 -4
  134. data/lib/rubocop/rspec/cop/generator.rb +25 -0
  135. data/lib/rubocop/rspec/corrector/move_node.rb +51 -0
  136. data/lib/rubocop/rspec/description_extractor.rb +60 -18
  137. data/lib/rubocop/rspec/example.rb +37 -0
  138. data/lib/rubocop/rspec/example_group.rb +67 -0
  139. data/lib/rubocop/rspec/hook.rb +79 -0
  140. data/lib/rubocop/rspec/inject.rb +3 -1
  141. data/lib/rubocop/rspec/language.rb +184 -41
  142. data/lib/rubocop/rspec/node.rb +19 -0
  143. data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +29 -0
  144. data/lib/rubocop/rspec/version.rb +1 -1
  145. data/lib/rubocop/rspec/wording.rb +61 -19
  146. data/lib/rubocop/rspec.rb +6 -2
  147. data/lib/rubocop-rspec.rb +45 -34
  148. metadata +130 -195
  149. data/Gemfile +0 -13
  150. data/Rakefile +0 -48
  151. data/lib/rubocop/cop/rspec/file_path.rb +0 -83
  152. data/lib/rubocop/rspec/language/node_pattern.rb +0 -16
  153. data/lib/rubocop/rspec/spec_only.rb +0 -61
  154. data/lib/rubocop/rspec/top_level_describe.rb +0 -61
  155. data/lib/rubocop/rspec/util.rb +0 -19
  156. data/rubocop-rspec.gemspec +0 -42
  157. data/spec/expect_violation/expectation_spec.rb +0 -85
  158. data/spec/project/changelog_spec.rb +0 -81
  159. data/spec/project/default_config_spec.rb +0 -52
  160. data/spec/project/project_requires_spec.rb +0 -8
  161. data/spec/rubocop/cop/rspec/any_instance_spec.rb +0 -30
  162. data/spec/rubocop/cop/rspec/be_eql_spec.rb +0 -59
  163. data/spec/rubocop/cop/rspec/describe_class_spec.rb +0 -113
  164. data/spec/rubocop/cop/rspec/describe_method_spec.rb +0 -32
  165. data/spec/rubocop/cop/rspec/described_class_spec.rb +0 -219
  166. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +0 -79
  167. data/spec/rubocop/cop/rspec/example_length_spec.rb +0 -117
  168. data/spec/rubocop/cop/rspec/example_wording_spec.rb +0 -82
  169. data/spec/rubocop/cop/rspec/expect_actual_spec.rb +0 -136
  170. data/spec/rubocop/cop/rspec/file_path_spec.rb +0 -236
  171. data/spec/rubocop/cop/rspec/focus_spec.rb +0 -130
  172. data/spec/rubocop/cop/rspec/hook_argument_spec.rb +0 -189
  173. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +0 -75
  174. data/spec/rubocop/cop/rspec/leading_subject_spec.rb +0 -54
  175. data/spec/rubocop/cop/rspec/let_setup_spec.rb +0 -66
  176. data/spec/rubocop/cop/rspec/message_chain_spec.rb +0 -21
  177. data/spec/rubocop/cop/rspec/message_expectation_spec.rb +0 -63
  178. data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +0 -28
  179. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +0 -84
  180. data/spec/rubocop/cop/rspec/named_subject_spec.rb +0 -62
  181. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +0 -55
  182. data/spec/rubocop/cop/rspec/not_to_not_spec.rb +0 -57
  183. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +0 -183
  184. data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +0 -71
  185. data/spec/rubocop/rspec/config_formatter_spec.rb +0 -48
  186. data/spec/rubocop/rspec/description_extractor_spec.rb +0 -35
  187. data/spec/rubocop/rspec/language/selector_set_spec.rb +0 -29
  188. data/spec/rubocop/rspec/spec_only_spec.rb +0 -97
  189. data/spec/rubocop/rspec/util/one_spec.rb +0 -21
  190. data/spec/rubocop/rspec/wording_spec.rb +0 -44
  191. data/spec/shared/rspec_only_cop_behavior.rb +0 -68
  192. data/spec/spec_helper.rb +0 -41
  193. 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
- # AssignmentOnly: false
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 < Cop
50
- include RuboCop::RSpec::SpecOnly, RuboCop::RSpec::Language
48
+ class InstanceVariable < Base
49
+ include TopLevelGroup
51
50
 
52
- MESSAGE = 'Use `let` instead of an instance variable'.freeze
51
+ MSG = 'Avoid instance variables - use let, ' \
52
+ 'a method call, or a local variable (if possible).'
53
53
 
54
- EXAMPLE_GROUP_METHODS = ExampleGroups::ALL + SharedGroups::ALL
54
+ # @!method dynamic_class?(node)
55
+ def_node_matcher :dynamic_class?, <<~PATTERN
56
+ (block (send (const nil? :Class) :new ...) ...)
57
+ PATTERN
55
58
 
56
- def_node_matcher :spec_group?, <<-PATTERN
57
- (block (send _ {#{EXAMPLE_GROUP_METHODS.to_node_pattern}} ...) ...)
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 on_block(node)
65
- return unless spec_group?(node)
66
-
73
+ def on_top_level_group(node)
67
74
  ivar_usage(node) do |ivar, name|
68
- return if assignment_only? && !ivar_assigned?(node, name)
75
+ next if valid_usage?(ivar)
76
+ next if assignment_only? && !ivar_assigned?(node, name)
69
77
 
70
- add_offense(ivar, :expression, MESSAGE)
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