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
@@ -5,7 +5,8 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for nested example groups.
7
7
  #
8
- # This cop is configurable using the `MaxNesting` option
8
+ # This cop is configurable using the `Max` option
9
+ # and supports `--auto-gen-config`.
9
10
  #
10
11
  # @example
11
12
  # # bad
@@ -21,7 +22,7 @@ module RuboCop
21
22
  # let(:user_attributes) do
22
23
  # {
23
24
  # name: 'John',
24
- # age: 22
25
+ # age: 22,
25
26
  # role: role
26
27
  # }
27
28
  # end
@@ -35,7 +36,7 @@ module RuboCop
35
36
  # end
36
37
  # end
37
38
  #
38
- # # better
39
+ # # good
39
40
  # context 'using some feature as an admin' do
40
41
  # let(:some) { :various }
41
42
  # let(:feature) { :setup }
@@ -43,7 +44,7 @@ module RuboCop
43
44
  # let(:user) do
44
45
  # UserCreate.call(
45
46
  # name: 'John',
46
- # age: 22
47
+ # age: 22,
47
48
  # role: 'admin'
48
49
  # )
49
50
  # end
@@ -52,72 +53,108 @@ module RuboCop
52
53
  # it 'yada yada'
53
54
  # end
54
55
  #
55
- # @example configuration
56
- #
57
- # # .rubocop.yml
58
- # RSpec/NestedGroups:
59
- # MaxNesting: 2
60
- #
61
- # context 'when using some feature' do
62
- # let(:some) { :various }
63
- # let(:feature) { :setup }
64
- #
65
- # context 'when user is signed in' do
66
- # let(:user) do
67
- # UserCreate.call(user_attributes)
56
+ # @example `Max: 3` (default)
57
+ # # bad
58
+ # describe Foo do
59
+ # context 'foo' do
60
+ # context 'bar' do
61
+ # context 'baz' do # flagged by rubocop
62
+ # end
68
63
  # end
64
+ # end
65
+ # end
69
66
  #
70
- # let(:user_attributes) do
71
- # {
72
- # name: 'John',
73
- # age: 22
74
- # role: role
75
- # }
67
+ # @example `Max: 2`
68
+ # # bad
69
+ # describe Foo do
70
+ # context 'foo' do
71
+ # context 'bar' do # flagged by rubocop
72
+ # context 'baz' do # flagged by rubocop
73
+ # end
76
74
  # end
75
+ # end
76
+ # end
77
77
  #
78
- # context 'when user is an admin' do # flagged by rubocop
79
- # let(:role) { 'admin' }
78
+ # @example `AllowedGroups: [] (default)`
79
+ # describe Foo do # <-- nested groups 1
80
+ # context 'foo' do # <-- nested groups 2
81
+ # context 'bar' do # <-- nested groups 3
82
+ # end
83
+ # end
84
+ # end
80
85
  #
81
- # it 'blah blah'
82
- # it 'yada yada'
86
+ # @example `AllowedGroups: [path]`
87
+ # describe Foo do # <-- nested groups 1
88
+ # path '/foo' do # <-- nested groups 1 (not counted)
89
+ # context 'bar' do # <-- nested groups 2
83
90
  # end
84
91
  # end
85
92
  # end
86
93
  #
87
- class NestedGroups < Cop
88
- include RuboCop::RSpec::SpecOnly,
89
- RuboCop::RSpec::TopLevelDescribe,
90
- RuboCop::RSpec::Language
94
+ class NestedGroups < Base
95
+ include ConfigurableMax
96
+ include TopLevelGroup
91
97
 
92
- MSG = 'Maximum example group nesting exceeded'.freeze
98
+ MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
93
99
 
94
- def_node_search :find_contexts, <<-PATTERN
95
- (block (send nil {#{ExampleGroups::ALL.to_node_pattern}} ...) (args) ...)
96
- PATTERN
100
+ DEPRECATED_MAX_KEY = 'MaxNesting'
97
101
 
98
- def on_block(node)
99
- describe, = described_constant(node)
100
- return unless describe
102
+ DEPRECATION_WARNING =
103
+ "Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \
104
+ 'deprecated in favor of `Max`. Please use that instead.'
101
105
 
102
- find_nested_contexts(node) do |context|
103
- add_offense(context.children.first, :expression)
106
+ def on_top_level_group(node)
107
+ find_nested_example_groups(node) do |example_group, nesting|
108
+ self.max = nesting
109
+ add_offense(
110
+ example_group.send_node,
111
+ message: message(nesting)
112
+ )
104
113
  end
105
114
  end
106
115
 
107
116
  private
108
117
 
109
- def find_nested_contexts(node, nesting: 1, &block)
110
- find_contexts(node) do |nested_context|
111
- yield(nested_context) if nesting > max_nesting
118
+ def find_nested_example_groups(node, nesting: 1, &block)
119
+ example_group = example_group?(node)
120
+ yield node, nesting if example_group && nesting > max_nesting
121
+
122
+ next_nesting = if count_up_nesting?(node, example_group)
123
+ nesting + 1
124
+ else
125
+ nesting
126
+ end
112
127
 
113
- nested_context.each_child_node do |child|
114
- find_nested_contexts(child, nesting: nesting + 1, &block)
115
- end
128
+ node.each_child_node(:block, :begin) do |child|
129
+ find_nested_example_groups(child, nesting: next_nesting, &block)
116
130
  end
117
131
  end
118
132
 
133
+ def count_up_nesting?(node, example_group)
134
+ example_group &&
135
+ (node.block_type? &&
136
+ !allowed_groups.include?(node.method_name.to_s))
137
+ end
138
+
139
+ def message(nesting)
140
+ format(MSG, total: nesting, max: max_nesting)
141
+ end
142
+
119
143
  def max_nesting
120
- Integer(cop_config.fetch('MaxNesting', 2))
144
+ @max_nesting ||= Integer(max_nesting_config)
145
+ end
146
+
147
+ def max_nesting_config
148
+ if cop_config.key?(DEPRECATED_MAX_KEY)
149
+ warn DEPRECATION_WARNING
150
+ cop_config.fetch(DEPRECATED_MAX_KEY)
151
+ else
152
+ cop_config.fetch('Max', 3)
153
+ end
154
+ end
155
+
156
+ def allowed_groups
157
+ @allowed_groups ||= cop_config.fetch('AllowedGroups', [])
121
158
  end
122
159
  end
123
160
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks if an example contains any expectation.
7
+ #
8
+ # All RSpec's example and expectation methods are covered by default.
9
+ # If you are using your own custom methods,
10
+ # add the following configuration:
11
+ #
12
+ # RSpec:
13
+ # Language:
14
+ # Examples:
15
+ # Regular:
16
+ # - custom_it
17
+ # Expectations:
18
+ # - custom_expect
19
+ #
20
+ # @example
21
+ # # bad
22
+ # it do
23
+ # a?
24
+ # end
25
+ #
26
+ # # good
27
+ # it do
28
+ # expect(a?).to be(true)
29
+ # end
30
+ #
31
+ # This cop can be customized with an allowed expectation methods pattern
32
+ # with an `AllowedPatterns` option. ^expect_ and ^assert_ are allowed
33
+ # by default.
34
+ #
35
+ # @example `AllowedPatterns` configuration
36
+ #
37
+ # # .rubocop.yml
38
+ # # RSpec/NoExpectationExample:
39
+ # # AllowedPatterns:
40
+ # # - ^expect_
41
+ # # - ^assert_
42
+ #
43
+ # @example
44
+ # # bad
45
+ # it do
46
+ # not_expect_something
47
+ # end
48
+ #
49
+ # # good
50
+ # it do
51
+ # expect_something
52
+ # end
53
+ #
54
+ # it do
55
+ # assert_something
56
+ # end
57
+ #
58
+ class NoExpectationExample < Base
59
+ include AllowedPattern
60
+ include SkipOrPending
61
+
62
+ MSG = 'No expectation found in this example.'
63
+
64
+ # @!method regular_or_focused_example?(node)
65
+ # @param [RuboCop::AST::Node] node
66
+ # @return [Boolean]
67
+ def_node_matcher :regular_or_focused_example?, <<~PATTERN
68
+ ({block numblock} (send nil? {#Examples.regular #Examples.focused} ...) ...)
69
+ PATTERN
70
+
71
+ # @!method includes_expectation?(node)
72
+ # @param [RuboCop::AST::Node] node
73
+ # @return [Boolean]
74
+ def_node_search :includes_expectation?, <<~PATTERN
75
+ {
76
+ (send nil? #Expectations.all ...)
77
+ (send nil? `#matches_allowed_pattern? ...)
78
+ }
79
+ PATTERN
80
+
81
+ # @!method includes_skip_example?(node)
82
+ # @param [RuboCop::AST::Node] node
83
+ # @return [Boolean]
84
+ def_node_search :includes_skip_example?, <<~PATTERN
85
+ (send nil? {:pending :skip} ...)
86
+ PATTERN
87
+
88
+ # @param [RuboCop::AST::BlockNode] node
89
+ def on_block(node)
90
+ return unless regular_or_focused_example?(node)
91
+ return if includes_expectation?(node)
92
+ return if includes_skip_example?(node)
93
+ return if skipped_in_metadata?(node.send_node)
94
+
95
+ add_offense(node)
96
+ end
97
+
98
+ alias on_numblock on_block
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module Cop
3
5
  module RSpec
4
6
  # Checks for consistent method usage for negating expectations.
5
7
  #
6
- # @example
8
+ # @example `EnforcedStyle: not_to` (default)
7
9
  # # bad
8
10
  # it '...' do
9
11
  # expect(false).to_not be_true
@@ -13,39 +15,40 @@ module RuboCop
13
15
  # it '...' do
14
16
  # expect(false).not_to be_true
15
17
  # end
16
- class NotToNot < Cop
17
- include RuboCop::RSpec::SpecOnly,
18
- RuboCop::Cop::ConfigurableEnforcedStyle
18
+ #
19
+ # @example `EnforcedStyle: to_not`
20
+ # # bad
21
+ # it '...' do
22
+ # expect(false).not_to be_true
23
+ # end
24
+ #
25
+ # # good
26
+ # it '...' do
27
+ # expect(false).to_not be_true
28
+ # end
29
+ #
30
+ class NotToNot < Base
31
+ extend AutoCorrector
32
+ include ConfigurableEnforcedStyle
19
33
 
20
- MSG = 'Prefer `%s` over `%s`'.freeze
34
+ MSG = 'Prefer `%<replacement>s` over `%<original>s`.'
35
+ RESTRICT_ON_SEND = %i[not_to to_not].freeze
21
36
 
22
- METHOD_NAMES = [:not_to, :to_not].freeze
37
+ # @!method not_to_not_offense(node)
38
+ def_node_matcher :not_to_not_offense, '(send _ % ...)'
23
39
 
24
40
  def on_send(node)
25
- _receiver, method_name, *_args = *node
26
-
27
- return unless METHOD_NAMES.include?(method_name)
28
-
29
- return if style.equal?(method_name)
30
- add_offense(node, :expression)
31
- end
32
-
33
- def message(node)
34
- _receiver, method_name, *_args = *node
35
-
36
- if method_name.equal?(:not_to)
37
- format(MSG, 'to_not', 'not_to')
38
- else
39
- format(MSG, 'not_to', 'to_not')
41
+ not_to_not_offense(node, alternative_style) do
42
+ add_offense(node.loc.selector) do |corrector|
43
+ corrector.replace(node.loc.selector, style.to_s)
44
+ end
40
45
  end
41
46
  end
42
47
 
43
- def autocorrect(node)
44
- _receiver, method_name, *_args = *node
45
- lambda do |corrector|
46
- corrector.replace(node.loc.selector,
47
- method_name.equal?(:not_to) ? 'to_not' : 'not_to')
48
- end
48
+ private
49
+
50
+ def message(_node)
51
+ format(MSG, replacement: style, original: alternative_style)
49
52
  end
50
53
  end
51
54
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks if there is a let/subject that overwrites an existing one.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # let(:foo) { bar }
11
+ # let(:foo) { baz }
12
+ #
13
+ # subject(:foo) { bar }
14
+ # let(:foo) { baz }
15
+ #
16
+ # let(:foo) { bar }
17
+ # let!(:foo) { baz }
18
+ #
19
+ # # good
20
+ # subject(:test) { something }
21
+ # let(:foo) { bar }
22
+ # let(:baz) { baz }
23
+ # let!(:other) { other }
24
+ #
25
+ class OverwritingSetup < Base
26
+ MSG = '`%<name>s` is already defined.'
27
+
28
+ # @!method setup?(node)
29
+ def_node_matcher :setup?, <<~PATTERN
30
+ (block (send nil? {#Helpers.all #Subjects.all} ...) ...)
31
+ PATTERN
32
+
33
+ # @!method first_argument_name(node)
34
+ def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
35
+
36
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
37
+ return unless example_group_with_body?(node)
38
+
39
+ find_duplicates(node.body) do |duplicate, name|
40
+ add_offense(
41
+ duplicate,
42
+ message: format(MSG, name: name)
43
+ )
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def find_duplicates(node)
50
+ setup_expressions = Set.new
51
+ node.each_child_node(:block) do |child|
52
+ next unless common_setup?(child)
53
+
54
+ name = if child.send_node.arguments?
55
+ first_argument_name(child.send_node).to_sym
56
+ else
57
+ :subject
58
+ end
59
+
60
+ yield child, name unless setup_expressions.add?(name)
61
+ end
62
+ end
63
+
64
+ def common_setup?(node)
65
+ return false unless setup?(node)
66
+
67
+ # Search only for setup with basic_literal arguments (e.g. :sym, :str)
68
+ # or no arguments at all.
69
+ node.send_node.arguments.all?(&:basic_literal?)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for any pending or skipped examples.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # describe MyClass do
11
+ # it "should be true"
12
+ # end
13
+ #
14
+ # describe MyClass do
15
+ # it "should be true", skip: true do
16
+ # expect(1).to eq(2)
17
+ # end
18
+ # end
19
+ #
20
+ # describe MyClass do
21
+ # it "should be true" do
22
+ # pending
23
+ # end
24
+ # end
25
+ #
26
+ # describe MyClass do
27
+ # xit "should be true" do
28
+ # end
29
+ # end
30
+ #
31
+ # # good
32
+ # describe MyClass do
33
+ # end
34
+ #
35
+ class Pending < Base
36
+ include SkipOrPending
37
+
38
+ MSG = 'Pending spec found.'
39
+
40
+ # @!method skippable?(node)
41
+ def_node_matcher :skippable?, <<~PATTERN
42
+ {
43
+ (send #rspec? #ExampleGroups.regular ...)
44
+ #skippable_example?
45
+ }
46
+ PATTERN
47
+
48
+ # @!method skippable_example?(node)
49
+ def_node_matcher :skippable_example?, <<~PATTERN
50
+ (send nil? #Examples.regular ...)
51
+ PATTERN
52
+
53
+ # @!method pending_block?(node)
54
+ def_node_matcher :pending_block?, <<~PATTERN
55
+ {
56
+ (send #rspec? #ExampleGroups.skipped ...)
57
+ (send nil? {#Examples.skipped #Examples.pending} ...)
58
+ }
59
+ PATTERN
60
+
61
+ def on_send(node)
62
+ return unless pending_block?(node) || skipped?(node)
63
+
64
+ add_offense(node)
65
+ end
66
+
67
+ private
68
+
69
+ def skipped?(node)
70
+ (skippable?(node) && skipped_in_metadata?(node)) ||
71
+ skipped_regular_example_without_body?(node)
72
+ end
73
+
74
+ def skipped_regular_example_without_body?(node)
75
+ skippable_example?(node) && !node.block_node
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for pending or skipped examples without reason.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # pending 'does something' do
11
+ # end
12
+ #
13
+ # # bad
14
+ # it 'does something', :pending do
15
+ # end
16
+ #
17
+ # # bad
18
+ # it 'does something' do
19
+ # pending
20
+ # end
21
+ #
22
+ # # bad
23
+ # xdescribe 'something' do
24
+ # end
25
+ #
26
+ # # bad
27
+ # skip 'does something' do
28
+ # end
29
+ #
30
+ # # bad
31
+ # it 'does something', :skip do
32
+ # end
33
+ #
34
+ # # bad
35
+ # it 'does something' do
36
+ # skip
37
+ # end
38
+ #
39
+ # # bad
40
+ # it 'does something'
41
+ #
42
+ # # good
43
+ # it 'does something' do
44
+ # pending 'reason'
45
+ # end
46
+ #
47
+ # # good
48
+ # it 'does something' do
49
+ # skip 'reason'
50
+ # end
51
+ #
52
+ # # good
53
+ # it 'does something', pending: 'reason' do
54
+ # end
55
+ #
56
+ # # good
57
+ # it 'does something', skip: 'reason' do
58
+ # end
59
+ class PendingWithoutReason < Base
60
+ MSG = 'Give the reason for pending or skip.'
61
+
62
+ # @!method skipped_in_example?(node)
63
+ def_node_matcher :skipped_in_example?, <<~PATTERN
64
+ {
65
+ (send nil? ${#Examples.skipped #Examples.pending})
66
+ (block (send nil? ${#Examples.skipped}) ...)
67
+ (numblock (send nil? ${#Examples.skipped}) ...)
68
+ }
69
+ PATTERN
70
+
71
+ # @!method skipped_by_example_method?(node)
72
+ def_node_matcher :skipped_by_example_method?, <<~PATTERN
73
+ (send nil? ${#Examples.skipped #Examples.pending})
74
+ PATTERN
75
+
76
+ # @!method skipped_by_example_method_with_block?(node)
77
+ def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
78
+ ({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
79
+ PATTERN
80
+
81
+ # @!method metadata_without_reason?(node)
82
+ def_node_matcher :metadata_without_reason?, <<~PATTERN
83
+ (send #rspec?
84
+ {#ExampleGroups.all #Examples.all} ...
85
+ {
86
+ <(sym ${:pending :skip}) ...>
87
+ (hash <(pair (sym ${:pending :skip}) true) ...>)
88
+ }
89
+ )
90
+ PATTERN
91
+
92
+ # @!method skipped_by_example_group_method?(node)
93
+ def_node_matcher :skipped_by_example_group_method?, <<~PATTERN
94
+ (send #rspec? ${#ExampleGroups.skipped} ...)
95
+ PATTERN
96
+
97
+ # @!method pending_step_without_reason?(node)
98
+ def_node_matcher :pending_step_without_reason?, <<~PATTERN
99
+ (send nil? {:skip :pending})
100
+ PATTERN
101
+
102
+ def on_send(node)
103
+ on_pending_by_metadata(node)
104
+ return unless (parent = parent_node(node))
105
+
106
+ if spec_group?(parent) || block_node_example_group?(node)
107
+ on_skipped_by_example_method(node)
108
+ on_skipped_by_example_group_method(node)
109
+ elsif example?(parent)
110
+ on_skipped_by_in_example_method(node)
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def parent_node(node)
117
+ node_or_block = node.block_node || node
118
+ return unless (parent = node_or_block.parent)
119
+
120
+ parent.begin_type? && parent.parent ? parent.parent : parent
121
+ end
122
+
123
+ def block_node_example_group?(node)
124
+ node.block_node &&
125
+ example_group?(node.block_node) &&
126
+ explicit_rspec?(node.receiver)
127
+ end
128
+
129
+ def on_skipped_by_in_example_method(node)
130
+ skipped_in_example?(node) do |pending|
131
+ add_offense(node, message: "Give the reason for #{pending}.")
132
+ end
133
+ end
134
+
135
+ def on_pending_by_metadata(node)
136
+ metadata_without_reason?(node) do |pending|
137
+ add_offense(node, message: "Give the reason for #{pending}.")
138
+ end
139
+ end
140
+
141
+ def on_skipped_by_example_method(node)
142
+ skipped_by_example_method?(node) do |pending|
143
+ add_offense(node, message: "Give the reason for #{pending}.")
144
+ end
145
+
146
+ skipped_by_example_method_with_block?(node.parent) do |pending|
147
+ add_offense(node, message: "Give the reason for #{pending}.")
148
+ end
149
+ end
150
+
151
+ def on_skipped_by_example_group_method(node)
152
+ skipped_by_example_group_method?(node) do
153
+ add_offense(node, message: 'Give the reason for skip.')
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end