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.
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