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,6 +5,8 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks if examples are focused.
7
7
  #
8
+ # This cop does not support autocorrection in some cases.
9
+ #
8
10
  # @example
9
11
  # # bad
10
12
  # describe MyClass, focus: true do
@@ -19,37 +21,74 @@ module RuboCop
19
21
  # # good
20
22
  # describe MyClass do
21
23
  # end
22
- class Focus < Cop
23
- include RuboCop::RSpec::SpecOnly, RuboCop::RSpec::Language
24
-
25
- MSG = 'Focused spec found.'.freeze
26
-
27
- focusable =
28
- ExampleGroups::GROUPS +
29
- ExampleGroups::SKIPPED +
30
- Examples::EXAMPLES +
31
- Examples::SKIPPED
32
-
33
- focused = ExampleGroups::FOCUSED + Examples::FOCUSED
24
+ #
25
+ # # bad
26
+ # fdescribe 'test' do; end
27
+ #
28
+ # # good
29
+ # describe 'test' do; end
30
+ #
31
+ # # bad
32
+ # fdescribe 'test' do; end
33
+ #
34
+ # # good
35
+ # describe 'test' do; end
36
+ #
37
+ # # bad
38
+ # shared_examples 'test', focus: true do; end
39
+ #
40
+ # # good
41
+ # shared_examples 'test' do; end
42
+ #
43
+ # # bad
44
+ # shared_context 'test', focus: true do; end
45
+ #
46
+ # # good
47
+ # shared_context 'test' do; end
48
+ #
49
+ # # bad (does not support autocorrection)
50
+ # focus 'test' do; end
51
+ #
52
+ class Focus < Base
53
+ extend AutoCorrector
54
+ include RangeHelp
34
55
 
35
- FOCUSABLE_SELECTORS = focusable.to_node_pattern
36
- FOCUSING_SELECTORS = focused.to_node_pattern
56
+ MSG = 'Focused spec found.'
37
57
 
38
- FOCUS_SYMBOL = s(:sym, :focus)
39
- FOCUS_TRUE = s(:pair, FOCUS_SYMBOL, s(:true))
58
+ # @!method focusable_selector?(node)
59
+ def_node_matcher :focusable_selector?, <<~PATTERN
60
+ {
61
+ #ExampleGroups.regular
62
+ #ExampleGroups.skipped
63
+ #Examples.regular
64
+ #Examples.skipped
65
+ #Examples.pending
66
+ #SharedGroups.all
67
+ }
68
+ PATTERN
40
69
 
41
- def_node_matcher :metadata, <<-PATTERN
42
- {(send nil {#{FOCUSABLE_SELECTORS}} ... (hash $...))
43
- (send nil {#{FOCUSABLE_SELECTORS}} $...)}
70
+ # @!method metadata(node)
71
+ def_node_matcher :metadata, <<~PATTERN
72
+ {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
73
+ (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
44
74
  PATTERN
45
75
 
46
- def_node_matcher :focused_block?, <<-PATTERN
47
- (send nil {#{FOCUSING_SELECTORS}} ...)
76
+ # @!method focused_block?(node)
77
+ def_node_matcher :focused_block?, <<~PATTERN
78
+ (send #rspec? {#ExampleGroups.focused #Examples.focused} ...)
48
79
  PATTERN
49
80
 
50
81
  def on_send(node)
82
+ return if node.chained? || node.each_ancestor(:def, :defs).any?
83
+
51
84
  focus_metadata(node) do |focus|
52
- add_offense(focus, :expression)
85
+ add_offense(focus) do |corrector|
86
+ if focus.pair_type? || focus.str_type? || focus.sym_type?
87
+ corrector.remove(with_surrounding(focus))
88
+ elsif focus.send_type?
89
+ correct_send(corrector, focus)
90
+ end
91
+ end
53
92
  end
54
93
  end
55
94
 
@@ -58,10 +97,25 @@ module RuboCop
58
97
  def focus_metadata(node, &block)
59
98
  yield(node) if focused_block?(node)
60
99
 
61
- metadata(node) do |matches|
62
- matches.grep(FOCUS_SYMBOL, &block)
63
- matches.grep(FOCUS_TRUE, &block)
100
+ metadata(node, &block)
101
+ end
102
+
103
+ def with_surrounding(focus)
104
+ range_with_space =
105
+ range_with_surrounding_space(focus.source_range, side: :left)
106
+
107
+ range_with_surrounding_comma(range_with_space, :left)
108
+ end
109
+
110
+ def correct_send(corrector, focus)
111
+ range = focus.loc.selector
112
+ unfocused = focus.method_name.to_s.sub(/^f/, '')
113
+ unless Examples.regular(unfocused) || ExampleGroups.regular(unfocused)
114
+ return
64
115
  end
116
+
117
+ corrector.replace(range,
118
+ range.source.sub(focus.method_name.to_s, unfocused))
65
119
  end
66
120
  end
67
121
  end
@@ -10,68 +10,70 @@ module RuboCop
10
10
  # styles: "implicit", "each", and "example." All styles have
11
11
  # the same behavior.
12
12
  #
13
- # @example when configuration is `EnforcedStyle: implicit`
13
+ # @example `EnforcedStyle: implicit` (default)
14
14
  # # bad
15
15
  # before(:each) do
16
- # ...
16
+ # # ...
17
17
  # end
18
18
  #
19
19
  # # bad
20
20
  # before(:example) do
21
- # ...
21
+ # # ...
22
22
  # end
23
23
  #
24
24
  # # good
25
25
  # before do
26
- # ...
26
+ # # ...
27
27
  # end
28
28
  #
29
- # @example when configuration is `EnforcedStyle: each`
29
+ # @example `EnforcedStyle: each`
30
30
  # # bad
31
31
  # before(:example) do
32
- # ...
32
+ # # ...
33
33
  # end
34
34
  #
35
- # # good
35
+ # # bad
36
36
  # before do
37
- # ...
37
+ # # ...
38
38
  # end
39
39
  #
40
40
  # # good
41
41
  # before(:each) do
42
- # ...
42
+ # # ...
43
43
  # end
44
44
  #
45
- # @example when configuration is `EnforcedStyle: example`
45
+ # @example `EnforcedStyle: example`
46
46
  # # bad
47
47
  # before(:each) do
48
- # ...
48
+ # # ...
49
49
  # end
50
50
  #
51
51
  # # bad
52
52
  # before do
53
- # ...
53
+ # # ...
54
54
  # end
55
55
  #
56
56
  # # good
57
57
  # before(:example) do
58
- # ...
58
+ # # ...
59
59
  # end
60
- class HookArgument < RuboCop::Cop::Cop
61
- include RuboCop::RSpec::Language,
62
- RuboCop::RSpec::SpecOnly,
63
- ConfigurableEnforcedStyle
64
-
65
- IMPLICIT_MSG = 'Omit the default `%p` argument for RSpec hooks.'.freeze
66
- EXPLICIT_MSG = 'Use `%p` for RSpec hooks.'.freeze
60
+ #
61
+ class HookArgument < Base
62
+ extend AutoCorrector
63
+ include ConfigurableEnforcedStyle
67
64
 
68
- HOOKS = "{#{Hooks::ALL.to_node_pattern}}".freeze
65
+ IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
66
+ EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
69
67
 
70
- def_node_matcher :scoped_hook, <<-PATTERN
71
- (block $(send nil #{HOOKS} (sym ${:each :example})) ...)
68
+ # @!method scoped_hook(node)
69
+ def_node_matcher :scoped_hook, <<~PATTERN
70
+ ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
72
71
  PATTERN
73
72
 
74
- def_node_matcher :unscoped_hook, "(block $(send nil #{HOOKS}) ...)"
73
+ # @!method unscoped_hook(node)
74
+ def_node_matcher :unscoped_hook, <<~PATTERN
75
+ ({block numblock} $(send _ #Hooks.all) ...)
76
+ PATTERN
75
77
 
76
78
  def on_block(node)
77
79
  hook(node) do |method_send, scope_name|
@@ -79,33 +81,43 @@ module RuboCop
79
81
  return check_implicit(method_send) unless scope_name
80
82
 
81
83
  style_detected(scope_name)
82
- add_offense(method_send, :expression, explicit_message(scope_name))
84
+ msg = explicit_message(scope_name)
85
+ add_offense(method_send, message: msg) do |corrector|
86
+ autocorrect(corrector, node, method_send)
87
+ end
83
88
  end
84
89
  end
85
90
 
86
- def autocorrect(node)
87
- scope = "(#{style.inspect})" unless implicit_style?
88
- hook = "#{node.method_name}#{scope}"
89
-
90
- lambda do |corrector|
91
- corrector.replace(node.loc.expression, hook)
92
- end
93
- end
91
+ alias on_numblock on_block
94
92
 
95
93
  private
96
94
 
95
+ def autocorrect(corrector, _node, method_send)
96
+ scope = implicit_style? ? '' : "(#{style.inspect})"
97
+ corrector.replace(
98
+ LocationHelp.arguments_with_whitespace(method_send), scope
99
+ )
100
+ end
101
+
97
102
  def check_implicit(method_send)
98
103
  style_detected(:implicit)
99
104
  return if implicit_style?
100
105
 
101
- add_offense(method_send, :selector, format(EXPLICIT_MSG, style))
106
+ msg = explicit_message(nil)
107
+ add_offense(method_send.loc.selector, message: msg) do |corrector|
108
+ scope = "(#{style.inspect})"
109
+ corrector.replace(
110
+ LocationHelp.arguments_with_whitespace(method_send),
111
+ scope
112
+ )
113
+ end
102
114
  end
103
115
 
104
116
  def explicit_message(scope)
105
117
  if implicit_style?
106
- format(IMPLICIT_MSG, scope)
118
+ format(IMPLICIT_MSG, scope: scope)
107
119
  else
108
- format(EXPLICIT_MSG, style)
120
+ format(EXPLICIT_MSG, scope: style)
109
121
  end
110
122
  end
111
123
 
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for before/around/after hooks that come after an example.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # it 'checks what foo does' do
11
+ # expect(foo).to be
12
+ # end
13
+ #
14
+ # before { prepare }
15
+ # after { clean_up }
16
+ #
17
+ # # good
18
+ # before { prepare }
19
+ # after { clean_up }
20
+ #
21
+ # it 'checks what foo does' do
22
+ # expect(foo).to be
23
+ # end
24
+ #
25
+ class HooksBeforeExamples < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Move `%<hook>s` above the examples in the group.'
29
+
30
+ # @!method example_or_group?(node)
31
+ def_node_matcher :example_or_group?, <<~PATTERN
32
+ {
33
+ ({block numblock} {
34
+ (send #rspec? #ExampleGroups.all ...)
35
+ (send nil? #Examples.all ...)
36
+ } ...)
37
+ (send nil? #Includes.examples ...)
38
+ }
39
+ PATTERN
40
+
41
+ def on_block(node)
42
+ return unless example_group_with_body?(node)
43
+
44
+ check_hooks(node.body) if multiline_block?(node.body)
45
+ end
46
+
47
+ alias on_numblock on_block
48
+
49
+ private
50
+
51
+ def multiline_block?(block)
52
+ block.begin_type?
53
+ end
54
+
55
+ def check_hooks(node)
56
+ first_example = find_first_example(node)
57
+ return unless first_example
58
+
59
+ first_example.right_siblings.each do |sibling|
60
+ next unless hook?(sibling)
61
+
62
+ msg = format(MSG, hook: sibling.method_name)
63
+ add_offense(sibling, message: msg) do |corrector|
64
+ autocorrect(corrector, sibling, first_example)
65
+ end
66
+ end
67
+ end
68
+
69
+ def find_first_example(node)
70
+ node.children.find { |sibling| example_or_group?(sibling) }
71
+ end
72
+
73
+ def autocorrect(corrector, node, first_example)
74
+ RuboCop::RSpec::Corrector::MoveNode.new(
75
+ node, corrector, processed_source
76
+ ).move_before(first_example)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for equality assertions with identical expressions on both sides.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect(foo.bar).to eq(foo.bar)
11
+ # expect(foo.bar).to eql(foo.bar)
12
+ #
13
+ # # good
14
+ # expect(foo.bar).to eq(2)
15
+ # expect(foo.bar).to eql(2)
16
+ #
17
+ class IdenticalEqualityAssertion < Base
18
+ MSG = 'Identical expressions on both sides of the equality ' \
19
+ 'may indicate a flawed test.'
20
+ RESTRICT_ON_SEND = %i[to].freeze
21
+
22
+ # @!method equality_check?(node)
23
+ def_node_matcher :equality_check?, <<~PATTERN
24
+ (send (send nil? :expect $_) :to
25
+ {(send nil? {:eql :eq :be} $_)
26
+ (send (send nil? :be) :== $_)})
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ equality_check?(node) do |left, right|
31
+ add_offense(node) if left == right
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check that implicit block expectation syntax is not used.
7
+ #
8
+ # Prefer using explicit block expectations.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # subject { -> { do_something } }
13
+ # it { is_expected.to change(something).to(new_value) }
14
+ #
15
+ # # good
16
+ # it 'changes something to a new value' do
17
+ # expect { do_something }.to change(something).to(new_value)
18
+ # end
19
+ #
20
+ class ImplicitBlockExpectation < Base
21
+ MSG = 'Avoid implicit block expectations.'
22
+ RESTRICT_ON_SEND = %i[is_expected should should_not].freeze
23
+
24
+ # @!method lambda?(node)
25
+ def_node_matcher :lambda?, <<~PATTERN
26
+ {
27
+ (send (const nil? :Proc) :new)
28
+ (send nil? {:proc :lambda})
29
+ }
30
+ PATTERN
31
+
32
+ # @!method lambda_subject?(node)
33
+ def_node_matcher :lambda_subject?, '(block #lambda? ...)'
34
+
35
+ # @!method implicit_expect(node)
36
+ def_node_matcher :implicit_expect, <<~PATTERN
37
+ $(send nil? {:is_expected :should :should_not} ...)
38
+ PATTERN
39
+
40
+ def on_send(node)
41
+ implicit_expect(node) do |implicit_expect|
42
+ subject = nearest_subject(implicit_expect)
43
+ add_offense(implicit_expect) if lambda_subject?(subject&.body)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def nearest_subject(node)
50
+ node
51
+ .each_ancestor(:block)
52
+ .lazy
53
+ .select { |block_node| multi_statement_example_group?(block_node) }
54
+ .map { |block_node| find_subject(block_node) }
55
+ .find(&:itself)
56
+ end
57
+
58
+ def multi_statement_example_group?(node)
59
+ example_group_with_body?(node) && node.body.begin_type?
60
+ end
61
+
62
+ def find_subject(block_node)
63
+ block_node.body.child_nodes.find { |send_node| subject?(send_node) }
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check that a consistent implicit expectation style is used.
7
+ #
8
+ # This cop can be configured using the `EnforcedStyle` option
9
+ # and supports the `--auto-gen-config` flag.
10
+ #
11
+ # @example `EnforcedStyle: is_expected` (default)
12
+ # # bad
13
+ # it { should be_truthy }
14
+ #
15
+ # # good
16
+ # it { is_expected.to be_truthy }
17
+ #
18
+ # @example `EnforcedStyle: should`
19
+ # # bad
20
+ # it { is_expected.to be_truthy }
21
+ #
22
+ # # good
23
+ # it { should be_truthy }
24
+ #
25
+ class ImplicitExpect < Base
26
+ extend AutoCorrector
27
+ include ConfigurableEnforcedStyle
28
+
29
+ MSG = 'Prefer `%<good>s` over `%<bad>s`.'
30
+
31
+ RESTRICT_ON_SEND = Runners.all + %i[should should_not]
32
+
33
+ # @!method implicit_expect(node)
34
+ def_node_matcher :implicit_expect, <<~PATTERN
35
+ {
36
+ (send nil? ${:should :should_not} ...)
37
+ (send (send nil? $:is_expected) #Runners.all ...)
38
+ }
39
+ PATTERN
40
+
41
+ alternatives = {
42
+ 'is_expected.to' => 'should',
43
+ 'is_expected.not_to' => 'should_not',
44
+ 'is_expected.to_not' => 'should_not'
45
+ }
46
+
47
+ ENFORCED_REPLACEMENTS = alternatives.merge(alternatives.invert).freeze
48
+
49
+ def on_send(node) # rubocop:disable Metrics/MethodLength
50
+ return unless (source_range = offending_expect(node))
51
+
52
+ expectation_source = source_range.source
53
+
54
+ if expectation_source.start_with?(style.to_s)
55
+ correct_style_detected
56
+ else
57
+ opposite_style_detected
58
+
59
+ msg = offense_message(expectation_source)
60
+ add_offense(source_range, message: msg) do |corrector|
61
+ replacement = replacement_source(expectation_source)
62
+ corrector.replace(source_range, replacement)
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def offending_expect(node)
70
+ case implicit_expect(node)
71
+ when :is_expected
72
+ is_expected_range(node.loc)
73
+ when :should, :should_not
74
+ node.loc.selector
75
+ end
76
+ end
77
+
78
+ def is_expected_range(source_map) # rubocop:disable Naming/PredicateName
79
+ Parser::Source::Range.new(
80
+ source_map.expression.source_buffer,
81
+ source_map.expression.begin_pos,
82
+ source_map.selector.end_pos
83
+ )
84
+ end
85
+
86
+ def offense_message(offending_source)
87
+ format(
88
+ MSG,
89
+ good: replacement_source(offending_source),
90
+ bad: offending_source
91
+ )
92
+ end
93
+
94
+ def replacement_source(offending_source)
95
+ ENFORCED_REPLACEMENTS.fetch(offending_source)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end