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
@@ -2,72 +2,215 @@
2
2
 
3
3
  module RuboCop
4
4
  module RSpec
5
- # RSpec public API methods that are commonly used in cops
5
+ # Contains node matchers for common RSpec DSL.
6
+ #
7
+ # RSpec allows for configuring aliases for commonly used DSL elements, e.g.
8
+ # example groups and hooks. It is possible to configure RuboCop RSpec to
9
+ # be able to properly detect these elements in the `RSpec/Language` section
10
+ # of the RuboCop YAML configuration file.
11
+ #
12
+ # In addition to providing useful matchers, this class is responsible for
13
+ # using the configured aliases.
6
14
  module Language
7
- # Set of method selectors
8
- class SelectorSet
9
- def initialize(selectors)
10
- @selectors = selectors
11
- end
15
+ extend RuboCop::NodePattern::Macros
16
+
17
+ class << self
18
+ attr_accessor :config
19
+ end
20
+
21
+ # @!method rspec?(node)
22
+ def_node_matcher :rspec?, '{#explicit_rspec? nil?}'
23
+
24
+ # @!method explicit_rspec?(node)
25
+ def_node_matcher :explicit_rspec?, '(const {nil? cbase} :RSpec)'
26
+
27
+ # @!method example_group?(node)
28
+ def_node_matcher :example_group?, <<~PATTERN
29
+ ({block numblock} (send #rspec? #ExampleGroups.all ...) ...)
30
+ PATTERN
31
+
32
+ # @!method shared_group?(node)
33
+ def_node_matcher :shared_group?,
34
+ '(block (send #rspec? #SharedGroups.all ...) ...)'
35
+
36
+ # @!method spec_group?(node)
37
+ def_node_matcher :spec_group?, <<~PATTERN
38
+ ({block numblock} (send #rspec?
39
+ {#SharedGroups.all #ExampleGroups.all}
40
+ ...) ...)
41
+ PATTERN
42
+
43
+ # @!method example_group_with_body?(node)
44
+ def_node_matcher :example_group_with_body?, <<~PATTERN
45
+ (block (send #rspec? #ExampleGroups.all ...) args !nil?)
46
+ PATTERN
12
47
 
13
- def ==(other)
14
- selectors.eql?(other.selectors)
48
+ # @!method example?(node)
49
+ def_node_matcher :example?, '(block (send nil? #Examples.all ...) ...)'
50
+
51
+ # @!method hook?(node)
52
+ def_node_matcher :hook?, <<~PATTERN
53
+ {
54
+ (numblock (send nil? #Hooks.all ...) ...)
55
+ (block (send nil? #Hooks.all ...) ...)
56
+ }
57
+ PATTERN
58
+
59
+ # @!method let?(node)
60
+ def_node_matcher :let?, <<~PATTERN
61
+ {
62
+ (block (send nil? #Helpers.all ...) ...)
63
+ (send nil? #Helpers.all _ block_pass)
64
+ }
65
+ PATTERN
66
+
67
+ # @!method include?(node)
68
+ def_node_matcher :include?, <<~PATTERN
69
+ {
70
+ (block (send nil? #Includes.all ...) ...)
71
+ (send nil? #Includes.all ...)
72
+ }
73
+ PATTERN
74
+
75
+ # @!method subject?(node)
76
+ def_node_matcher :subject?, '(block (send nil? #Subjects.all ...) ...)'
77
+
78
+ module ExampleGroups # :nodoc:
79
+ class << self
80
+ def all(element)
81
+ regular(element) ||
82
+ skipped(element) ||
83
+ focused(element)
84
+ end
85
+
86
+ def regular(element)
87
+ Language.config['ExampleGroups']['Regular'].include?(element.to_s)
88
+ end
89
+
90
+ def focused(element)
91
+ Language.config['ExampleGroups']['Focused'].include?(element.to_s)
92
+ end
93
+
94
+ def skipped(element)
95
+ Language.config['ExampleGroups']['Skipped'].include?(element.to_s)
96
+ end
15
97
  end
98
+ end
16
99
 
17
- def +(other)
18
- self.class.new(selectors + other.selectors)
100
+ module Examples # :nodoc:
101
+ class << self
102
+ def all(element)
103
+ regular(element) ||
104
+ focused(element) ||
105
+ skipped(element) ||
106
+ pending(element)
107
+ end
108
+
109
+ def regular(element)
110
+ Language.config['Examples']['Regular'].include?(element.to_s)
111
+ end
112
+
113
+ def focused(element)
114
+ Language.config['Examples']['Focused'].include?(element.to_s)
115
+ end
116
+
117
+ def skipped(element)
118
+ Language.config['Examples']['Skipped'].include?(element.to_s)
119
+ end
120
+
121
+ def pending(element)
122
+ Language.config['Examples']['Pending'].include?(element.to_s)
123
+ end
19
124
  end
125
+ end
20
126
 
21
- def include?(selector)
22
- selectors.include?(selector)
127
+ module Expectations # :nodoc:
128
+ def self.all(element)
129
+ Language.config['Expectations'].include?(element.to_s)
23
130
  end
131
+ end
24
132
 
25
- def to_node_pattern
26
- selectors.map(&:inspect).join(' ')
133
+ module Helpers # :nodoc:
134
+ def self.all(element)
135
+ Language.config['Helpers'].include?(element.to_s)
27
136
  end
137
+ end
28
138
 
29
- protected
139
+ module Hooks # :nodoc:
140
+ def self.all(element)
141
+ Language.config['Hooks'].include?(element.to_s)
142
+ end
143
+ end
30
144
 
31
- attr_reader :selectors
145
+ module HookScopes # :nodoc:
146
+ ALL = %i[each example context all suite].freeze
147
+ def self.all(element)
148
+ ALL.include?(element)
149
+ end
32
150
  end
33
151
 
34
- module ExampleGroups
35
- GROUPS = SelectorSet.new(%i(describe context feature example_group))
36
- SKIPPED = SelectorSet.new(%i(xdescribe xcontext xfeature))
37
- FOCUSED = SelectorSet.new(%i(fdescribe fcontext ffeature))
152
+ module Includes # :nodoc:
153
+ class << self
154
+ def all(element)
155
+ examples(element) ||
156
+ context(element)
157
+ end
158
+
159
+ def examples(element)
160
+ Language.config['Includes']['Examples'].include?(element.to_s)
161
+ end
38
162
 
39
- ALL = GROUPS + SKIPPED + FOCUSED
163
+ def context(element)
164
+ Language.config['Includes']['Context'].include?(element.to_s)
165
+ end
166
+ end
40
167
  end
41
168
 
42
- module SharedGroups
43
- ALL = SelectorSet.new(
44
- %i(shared_examples shared_context shared_examples_for)
45
- )
169
+ module Runners # :nodoc:
170
+ ALL = %i[to to_not not_to].freeze
171
+ class << self
172
+ def all(element = nil)
173
+ return ALL if element.nil?
174
+
175
+ ALL.include?(element)
176
+ end
177
+ end
46
178
  end
47
179
 
48
- module Examples
49
- EXAMPLES = SelectorSet.new(%i(it specify example scenario its))
50
- FOCUSED = SelectorSet.new(%i(fit fspecify fexample fscenario focus))
51
- SKIPPED = SelectorSet.new(%i(xit xspecify xexample xscenario skip))
52
- PENDING = SelectorSet.new(%i(pending))
180
+ module SharedGroups # :nodoc:
181
+ class << self
182
+ def all(element)
183
+ examples(element) ||
184
+ context(element)
185
+ end
53
186
 
54
- ALL = EXAMPLES + FOCUSED + SKIPPED + PENDING
187
+ def examples(element)
188
+ Language.config['SharedGroups']['Examples'].include?(element.to_s)
189
+ end
190
+
191
+ def context(element)
192
+ Language.config['SharedGroups']['Context'].include?(element.to_s)
193
+ end
194
+ end
55
195
  end
56
196
 
57
- module Hooks
58
- ALL = SelectorSet.new(%i(after around before))
197
+ module Subjects # :nodoc:
198
+ def self.all(element)
199
+ Language.config['Subjects'].include?(element.to_s)
200
+ end
59
201
  end
60
202
 
61
- module Helpers
62
- ALL = SelectorSet.new(%i(let let!))
203
+ # This is used in Dialect and DescribeClass cops to detect RSpec blocks.
204
+ module ALL # :nodoc:
205
+ def self.all(element)
206
+ [ExampleGroups, Examples, Expectations, Helpers, Hooks, Includes,
207
+ Runners, SharedGroups, Subjects]
208
+ .find { |concept| concept.all(element) }
209
+ end
63
210
  end
64
211
 
65
- ALL =
66
- ExampleGroups::ALL +
67
- SharedGroups::ALL +
68
- Examples::ALL +
69
- Hooks::ALL +
70
- Helpers::ALL
212
+ private_constant :ExampleGroups, :Examples, :Expectations, :Hooks,
213
+ :Includes, :Runners, :SharedGroups, :ALL
71
214
  end
72
215
  end
73
216
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # RuboCop RSpec specific extensions of RuboCop::AST::Node
6
+ module Node
7
+ # In various cops we want to regard const as literal although it's not
8
+ # strictly literal.
9
+ def recursive_literal_or_const?
10
+ case type
11
+ when :begin, :pair, *AST::Node::COMPOSITE_LITERALS
12
+ children.compact.all?(&:recursive_literal_or_const?)
13
+ else
14
+ literal? || const_type?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'with default RSpec/Language config' do
4
+ include_context 'config'
5
+
6
+ # Deep duplication is needed to prevent config leakage between examples
7
+ let(:other_cops) do
8
+ default_language = RuboCop::ConfigLoader
9
+ .default_configuration['RSpec']['Language']
10
+ default_include = RuboCop::ConfigLoader
11
+ .default_configuration['RSpec']['Include']
12
+ { 'RSpec' =>
13
+ {
14
+ 'Include' => default_include,
15
+ 'Language' => deep_dup(default_language)
16
+ } }
17
+ end
18
+
19
+ def deep_dup(object)
20
+ case object
21
+ when Array
22
+ object.map { |item| deep_dup(item) }
23
+ when Hash
24
+ object.transform_values { |value| deep_dup(value) }
25
+ else
26
+ object # only collections undergo modifications and need duping
27
+ end
28
+ end
29
+ end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '1.7.0'.freeze
7
+ STRING = '3.0.2'
8
8
  end
9
9
  end
10
10
  end
@@ -4,44 +4,86 @@ module RuboCop
4
4
  module RSpec
5
5
  # RSpec example wording rewriter
6
6
  class Wording
7
+ SHOULDNT_PREFIX = /\Ashould(?:n't| not)\b/i.freeze
8
+ SHOULDNT_BE_PREFIX = /#{SHOULDNT_PREFIX} be\b/i.freeze
9
+ WILL_NOT_PREFIX = /\Awill not\b/i.freeze
10
+ WONT_PREFIX = /\Awon't\b/i.freeze
11
+ ES_SUFFIX_PATTERN = /(?:o|s|x|ch|sh|z)\z/i.freeze
12
+ IES_SUFFIX_PATTERN = /[^aeou]y\z/i.freeze
13
+
7
14
  def initialize(text, ignore:, replace:)
8
15
  @text = text
9
16
  @ignores = ignore
10
17
  @replacements = replace
11
18
  end
12
19
 
20
+ # rubocop:disable Metrics/MethodLength
13
21
  def rewrite
14
- text.split.tap do |words|
15
- first_word = words.shift
16
- words.unshift('not') if first_word.eql?("shouldn't")
17
-
18
- words.each_with_index do |value, key|
19
- next if ignores.include?(value)
20
- words[key] = simple_present(words.fetch(key))
21
- break
22
- end
23
- end.join(' ')
22
+ case text
23
+ when SHOULDNT_BE_PREFIX
24
+ replace_prefix(SHOULDNT_BE_PREFIX, 'is not')
25
+ when SHOULDNT_PREFIX
26
+ replace_prefix(SHOULDNT_PREFIX, 'does not')
27
+ when WILL_NOT_PREFIX
28
+ replace_prefix(WILL_NOT_PREFIX, 'does not')
29
+ when WONT_PREFIX
30
+ replace_prefix(WONT_PREFIX, 'does not')
31
+ else
32
+ remove_should_and_pluralize
33
+ end
24
34
  end
35
+ # rubocop:enable Metrics/MethodLength
25
36
 
26
37
  private
27
38
 
28
39
  attr_reader :text, :ignores, :replacements
29
40
 
30
- def simple_present(word)
31
- return replacements.fetch(word) if replacements.key?(word)
41
+ def replace_prefix(pattern, replacement)
42
+ text.sub(pattern) do |matched|
43
+ uppercase?(matched) ? replacement.upcase : replacement
44
+ end
45
+ end
46
+
47
+ def uppercase?(word)
48
+ word.upcase.eql?(word)
49
+ end
50
+
51
+ def remove_should_and_pluralize
52
+ _should, *words = text.split
53
+
54
+ words.each_with_index do |word, index|
55
+ next if ignored_word?(word)
32
56
 
33
- # ends with o s x ch sh or ss
34
- if %w(o s x ch sh).any?(&word.public_method(:end_with?))
35
- return "#{word}es"
57
+ words[index] = substitute(word)
58
+
59
+ break
36
60
  end
37
61
 
38
- # ends with y
39
- if word.end_with?('y') && !%w(a u o e).include?(word[-2])
40
- return "#{word[0..-2]}ies"
62
+ words.join(' ')
63
+ end
64
+
65
+ def ignored_word?(word)
66
+ ignores.any? { |ignore| ignore.casecmp(word).zero? }
67
+ end
68
+
69
+ def substitute(word)
70
+ # NOTE: Custom replacements are case sensitive.
71
+ return replacements.fetch(word) if replacements.key?(word)
72
+
73
+ case word
74
+ when ES_SUFFIX_PATTERN then append_suffix(word, 'es')
75
+ when IES_SUFFIX_PATTERN then append_suffix(word[0..-2], 'ies')
76
+ else append_suffix(word, 's')
41
77
  end
78
+ end
42
79
 
43
- "#{word}s"
80
+ def append_suffix(word, suffix)
81
+ suffix = suffix.upcase if uppercase?(word)
82
+
83
+ "#{word}#{suffix}"
44
84
  end
85
+
86
+ private_constant(*constants(false))
45
87
  end
46
88
  end
47
89
  end
data/lib/rubocop/rspec.rb CHANGED
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  # RuboCop RSpec project namespace
3
5
  module RSpec
4
6
  PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
5
7
  CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
6
- CONFIG = YAML.load(CONFIG_DEFAULT.read).freeze
7
8
 
8
- private_constant(*constants(false))
9
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
10
+
11
+ ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join('config',
12
+ 'obsoletion.yml')
9
13
  end
10
14
  end
data/lib/rubocop-rspec.rb CHANGED
@@ -1,42 +1,53 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'yaml'
3
5
 
4
6
  require 'rubocop'
5
7
 
6
- require 'rubocop/rspec'
7
- require 'rubocop/rspec/version'
8
- require 'rubocop/rspec/inject'
9
- require 'rubocop/rspec/top_level_describe'
10
- require 'rubocop/rspec/wording'
11
- require 'rubocop/rspec/util'
12
- require 'rubocop/rspec/language'
13
- require 'rubocop/rspec/language/node_pattern'
14
- require 'rubocop/rspec/spec_only'
8
+ require_relative 'rubocop/rspec'
9
+ require_relative 'rubocop/rspec/inject'
10
+ require_relative 'rubocop/rspec/language'
11
+ require_relative 'rubocop/rspec/node'
12
+ require_relative 'rubocop/rspec/version'
13
+ require_relative 'rubocop/rspec/wording'
14
+
15
+ require_relative 'rubocop/cop/rspec/mixin/file_help'
16
+ require_relative 'rubocop/cop/rspec/mixin/final_end_location'
17
+ require_relative 'rubocop/cop/rspec/mixin/inside_example_group'
18
+ require_relative 'rubocop/cop/rspec/mixin/location_help'
19
+ require_relative 'rubocop/cop/rspec/mixin/metadata'
20
+ require_relative 'rubocop/cop/rspec/mixin/namespace'
21
+ require_relative 'rubocop/cop/rspec/mixin/skip_or_pending'
22
+ require_relative 'rubocop/cop/rspec/mixin/top_level_group'
23
+ require_relative 'rubocop/cop/rspec/mixin/variable'
24
+
25
+ # Dependent on `RuboCop::Cop::RSpec::FinalEndLocation`.
26
+ require_relative 'rubocop/cop/rspec/mixin/comments_help'
27
+ require_relative 'rubocop/cop/rspec/mixin/empty_line_separation'
28
+
29
+ require_relative 'rubocop/cop/rspec/base'
30
+ require_relative 'rubocop/rspec/align_let_brace'
31
+ require_relative 'rubocop/rspec/concept'
32
+ require_relative 'rubocop/rspec/corrector/move_node'
33
+ require_relative 'rubocop/rspec/example'
34
+ require_relative 'rubocop/rspec/example_group'
35
+ require_relative 'rubocop/rspec/hook'
15
36
 
16
37
  RuboCop::RSpec::Inject.defaults!
17
38
 
18
- # cops
19
- require 'rubocop/cop/rspec/any_instance'
20
- require 'rubocop/cop/rspec/be_eql'
21
- require 'rubocop/cop/rspec/describe_class'
22
- require 'rubocop/cop/rspec/describe_method'
23
- require 'rubocop/cop/rspec/described_class'
24
- require 'rubocop/cop/rspec/empty_example_group'
25
- require 'rubocop/cop/rspec/example_length'
26
- require 'rubocop/cop/rspec/example_wording'
27
- require 'rubocop/cop/rspec/expect_actual'
28
- require 'rubocop/cop/rspec/file_path'
29
- require 'rubocop/cop/rspec/focus'
30
- require 'rubocop/cop/rspec/hook_argument'
31
- require 'rubocop/cop/rspec/instance_variable'
32
- require 'rubocop/cop/rspec/leading_subject'
33
- require 'rubocop/cop/rspec/let_setup'
34
- require 'rubocop/cop/rspec/message_chain'
35
- require 'rubocop/cop/rspec/message_expectation'
36
- require 'rubocop/cop/rspec/multiple_describes'
37
- require 'rubocop/cop/rspec/multiple_expectations'
38
- require 'rubocop/cop/rspec/named_subject'
39
- require 'rubocop/cop/rspec/nested_groups'
40
- require 'rubocop/cop/rspec/not_to_not'
41
- require 'rubocop/cop/rspec/subject_stub'
42
- require 'rubocop/cop/rspec/verified_doubles'
39
+ require_relative 'rubocop/cop/rspec_cops'
40
+
41
+ # We have to register our autocorrect incompatibilities in RuboCop's cops
42
+ # as well so we do not hit infinite loops
43
+
44
+ RuboCop::Cop::Layout::ExtraSpacing.singleton_class.prepend(
45
+ Module.new do
46
+ def autocorrect_incompatible_with
47
+ super.push(RuboCop::Cop::RSpec::AlignLeftLetBrace)
48
+ .push(RuboCop::Cop::RSpec::AlignRightLetBrace)
49
+ end
50
+ end
51
+ )
52
+
53
+ RuboCop::AST::Node.include(RuboCop::RSpec::Node)