rubocop-minitest 0.22.2 → 0.32.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -5
  4. data/config/default.yml +82 -1
  5. data/lib/rubocop/cop/generator.rb +1 -1
  6. data/lib/rubocop/cop/minitest/assert_instance_of.rb +18 -2
  7. data/lib/rubocop/cop/minitest/assert_match.rb +4 -1
  8. data/lib/rubocop/cop/minitest/assert_operator.rb +58 -0
  9. data/lib/rubocop/cop/minitest/assert_output.rb +1 -2
  10. data/lib/rubocop/cop/minitest/assert_path_exists.rb +9 -4
  11. data/lib/rubocop/cop/minitest/assert_same.rb +26 -0
  12. data/lib/rubocop/cop/minitest/assert_truthy.rb +10 -0
  13. data/lib/rubocop/cop/minitest/assert_with_expected_argument.rb +7 -1
  14. data/lib/rubocop/cop/minitest/empty_line_before_assertion_methods.rb +101 -0
  15. data/lib/rubocop/cop/minitest/global_expectations.rb +3 -1
  16. data/lib/rubocop/cop/minitest/lifecycle_hooks_order.rb +100 -0
  17. data/lib/rubocop/cop/minitest/literal_as_actual_argument.rb +16 -5
  18. data/lib/rubocop/cop/minitest/multiple_assertions.rb +23 -4
  19. data/lib/rubocop/cop/minitest/no_assertions.rb +1 -8
  20. data/lib/rubocop/cop/minitest/no_test_cases.rb +35 -0
  21. data/lib/rubocop/cop/minitest/non_public_test_method.rb +55 -0
  22. data/lib/rubocop/cop/minitest/refute_equal.rb +2 -3
  23. data/lib/rubocop/cop/minitest/refute_false.rb +11 -1
  24. data/lib/rubocop/cop/minitest/refute_instance_of.rb +18 -2
  25. data/lib/rubocop/cop/minitest/refute_match.rb +4 -1
  26. data/lib/rubocop/cop/minitest/refute_operator.rb +58 -0
  27. data/lib/rubocop/cop/minitest/refute_path_exists.rb +9 -4
  28. data/lib/rubocop/cop/minitest/refute_same.rb +26 -0
  29. data/lib/rubocop/cop/minitest/return_in_test_method.rb +44 -0
  30. data/lib/rubocop/cop/minitest/skip_without_reason.rb +66 -0
  31. data/lib/rubocop/cop/minitest/test_file_name.rb +46 -0
  32. data/lib/rubocop/cop/minitest/test_method_name.rb +1 -12
  33. data/lib/rubocop/cop/minitest/useless_assertion.rb +75 -0
  34. data/lib/rubocop/cop/minitest_cops.rb +16 -3
  35. data/lib/rubocop/cop/mixin/argument_range_helper.rb +0 -6
  36. data/lib/rubocop/cop/mixin/instance_of_assertion_handleable.rb +48 -0
  37. data/lib/rubocop/cop/mixin/minitest_cop_rule.rb +16 -5
  38. data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +32 -11
  39. data/lib/rubocop/cop/mixin/predicate_assertion_handleable.rb +1 -1
  40. data/lib/rubocop/minitest/assert_offense.rb +47 -6
  41. data/lib/rubocop/minitest/version.rb +1 -1
  42. metadata +20 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8a90bf7594f331c351450d9cd2012ab3c7378e72c24fad69c79a052983d2ea8
4
- data.tar.gz: 9cbdab3ceafeddbe54fba39ea451c5347489dae3066f36208635b7aea2abcd7e
3
+ metadata.gz: ce290f7a13faed25c00a17d9daf8bcaf17e187599a075323fc99f8494c29c2bb
4
+ data.tar.gz: 9def860d52f65aed4e8923bcf2fd851a5188485986c7ad5bce44a9dc0d79b2d3
5
5
  SHA512:
6
- metadata.gz: 8b0d79ca4c4de8521c71b8752bc759f346efd898e7538acf1e424b7f3920ca291a9e85b7017eeae7a251313643c5bd0a4abcfaa042e14ac1c592ca8c9e3dde47
7
- data.tar.gz: 838aa30294ec6f330a90dd500bbdce42febf70fa40e0e9b32490a9ffd7d663634e343a8cc321a7aa1e46a417373e26cb89f85d1d551a1b8ec2c4b1ffb1375542
6
+ metadata.gz: 8db20fcf7d8e58b564da63b85f6692c4a2be189ffeacdc654f4b18ea689416688442df945606c84f55e63b9007c8df51b4f173004a7ae145603733f6bac62c61
7
+ data.tar.gz: 41232dfbabd012e4edbe1e78b49f1846004246950773a6f06c50fff358cb71a79fbdcd95ed0657690387801331093606c2dabb376788368cf51438ff84846be4
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019-2022 Bozhidar Batsov, Jonas Arvidsson, Koichi ITO
3
+ Copyright (c) 2019-2023 Bozhidar Batsov, Jonas Arvidsson, Koichi ITO
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -3,15 +3,15 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-minitest.svg)](https://badge.fury.io/rb/rubocop-minitest)
4
4
  [![CircleCI](https://circleci.com/gh/rubocop/rubocop-minitest.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-minitest)
5
5
 
6
- A [RuboCop](https://github.com/rubocop/rubocop) extension focused on enforcing Minitest best practices and coding conventions.
6
+ A [RuboCop](https://github.com/rubocop/rubocop) extension focused on enforcing [Minitest](https://github.com/minitest/minitest) best practices and coding conventions.
7
7
  The library is based on the guidelines outlined in the community [Minitest Style Guide](https://minitest.rubystyle.guide).
8
8
 
9
9
  ## Installation
10
10
 
11
11
  Just install the `rubocop-minitest` gem
12
12
 
13
- ```bash
14
- gem install rubocop-minitest
13
+ ```sh
14
+ $ gem install rubocop-minitest
15
15
  ```
16
16
 
17
17
  or if you use bundler put this in your `Gemfile`
@@ -46,8 +46,8 @@ cops together with the standard cops.
46
46
 
47
47
  ### Command line
48
48
 
49
- ```bash
50
- rubocop --require rubocop-minitest
49
+ ```sh
50
+ $ rubocop --require rubocop-minitest
51
51
  ```
52
52
 
53
53
  ### Rake task
data/config/default.yml CHANGED
@@ -59,6 +59,12 @@ Minitest/AssertNil:
59
59
  Enabled: true
60
60
  VersionAdded: '0.1'
61
61
 
62
+ Minitest/AssertOperator:
63
+ Description: 'This cop enforces the use of `assert_operator(expected, :<, actual)` over `assert(expected < actual)`.'
64
+ StyleGuide: 'https://minitest.rubystyle.guide#assert-operator'
65
+ Enabled: pending
66
+ VersionAdded: '0.32'
67
+
62
68
  Minitest/AssertOutput:
63
69
  Description: 'This cop checks for opportunities to use `assert_output`.'
64
70
  StyleGuide: 'https://minitest.rubystyle.guide/#assert-output'
@@ -85,7 +91,9 @@ Minitest/AssertRaisesCompoundBody:
85
91
  Minitest/AssertRaisesWithRegexpArgument:
86
92
  Description: 'This cop enforces checks for regular expression literals passed to `assert_raises`.'
87
93
  Enabled: pending
94
+ Severity: warning
88
95
  VersionAdded: '0.22'
96
+ VersionChanged: '0.26'
89
97
 
90
98
  Minitest/AssertRespondTo:
91
99
  Description: 'This cop enforces the test to use `assert_respond_to(object, :do_something)` over `assert(object.respond_to?(:do_something))`.'
@@ -93,6 +101,12 @@ Minitest/AssertRespondTo:
93
101
  Enabled: true
94
102
  VersionAdded: '0.3'
95
103
 
104
+ Minitest/AssertSame:
105
+ Description: 'Enforces the use of `assert_same(expected, actual)` over `assert(expected.equal?(actual))`.'
106
+ StyleGuide: 'https://minitest.rubystyle.guide#assert-same'
107
+ Enabled: pending
108
+ VersionAdded: '0.26'
109
+
96
110
  Minitest/AssertSilent:
97
111
  Description: "This cop enforces the test to use `assert_silent { ... }` instead of using `assert_output('', '') { ... }`."
98
112
  StyleGuide: 'https://github.com/rubocop/minitest-style-guide#assert-silent'
@@ -103,13 +117,17 @@ Minitest/AssertTruthy:
103
117
  Description: 'This cop enforces the test to use `assert(actual)` instead of using `assert_equal(true, actual)`.'
104
118
  StyleGuide: 'https://minitest.rubystyle.guide#assert-truthy'
105
119
  Enabled: true
120
+ Safe: false
106
121
  VersionAdded: '0.2'
122
+ VersionChanged: '0.27'
107
123
 
108
124
  Minitest/AssertWithExpectedArgument:
109
125
  Description: 'This cop tries to detect when a user accidentally used `assert` when they meant to use `assert_equal`.'
110
126
  Enabled: pending
127
+ Severity: warning
111
128
  Safe: false
112
129
  VersionAdded: '0.11'
130
+ VersionChanged: '0.26'
113
131
 
114
132
  Minitest/AssertionInLifecycleHook:
115
133
  Description: 'This cop checks for usage of assertions in lifecycle hooks.'
@@ -122,10 +140,16 @@ Minitest/DuplicateTestRun:
122
140
  Enabled: pending
123
141
  VersionAdded: '0.19'
124
142
 
143
+ Minitest/EmptyLineBeforeAssertionMethods:
144
+ Description: 'Add empty line before assertion methods.'
145
+ Enabled: pending
146
+ VersionAdded: '0.23'
147
+
125
148
  Minitest/GlobalExpectations:
126
149
  Description: 'This cop checks for deprecated global expectations.'
127
150
  StyleGuide: 'https://minitest.rubystyle.guide#global-expectations'
128
151
  Enabled: true
152
+ Severity: warning
129
153
  EnforcedStyle: any
130
154
  Include:
131
155
  - '**/test/**/*'
@@ -138,7 +162,13 @@ Minitest/GlobalExpectations:
138
162
  - expect
139
163
  - value
140
164
  VersionAdded: '0.7'
141
- VersionChanged: '0.16'
165
+ VersionChanged: '0.26'
166
+
167
+ Minitest/LifecycleHooksOrder:
168
+ Description: 'Checks that lifecycle hooks are declared in the order in which they will be executed.'
169
+ StyleGuide: 'https://minitest.rubystyle.guide/#hooks-ordering'
170
+ Enabled: pending
171
+ VersionAdded: '0.28'
142
172
 
143
173
  Minitest/LiteralAsActualArgument:
144
174
  Description: 'This cop enforces correct order of `expected` and `actual` arguments for `assert_equal`.'
@@ -157,6 +187,17 @@ Minitest/NoAssertions:
157
187
  Enabled: false
158
188
  VersionAdded: '0.12'
159
189
 
190
+ Minitest/NoTestCases:
191
+ Description: 'Checks if test class contains any test cases.'
192
+ Enabled: false
193
+ VersionAdded: '0.30'
194
+
195
+ Minitest/NonPublicTestMethod:
196
+ Description: 'Detects non `public` (marked as `private` or `protected`) test methods.'
197
+ Enabled: pending
198
+ Severity: warning
199
+ VersionAdded: '0.27'
200
+
160
201
  Minitest/RefuteEmpty:
161
202
  Description: 'This cop enforces to use `refute_empty` instead of using `refute(object.empty?)`.'
162
203
  StyleGuide: 'https://minitest.rubystyle.guide#refute-empty'
@@ -173,7 +214,9 @@ Minitest/RefuteFalse:
173
214
  Description: 'Check if your test uses `refute(actual)` instead of `assert_equal(false, actual)`.'
174
215
  StyleGuide: 'https://minitest.rubystyle.guide#refute-false'
175
216
  Enabled: true
217
+ Safe: false
176
218
  VersionAdded: '0.3'
219
+ VersionChanged: '0.27'
177
220
 
178
221
  Minitest/RefuteInDelta:
179
222
  Description: 'This cop enforces the test to use `refute_in_delta` instead of using `refute_equal` to compare floats.'
@@ -211,6 +254,12 @@ Minitest/RefuteNil:
211
254
  Enabled: true
212
255
  VersionAdded: '0.2'
213
256
 
257
+ Minitest/RefuteOperator:
258
+ Description: 'This cop enforces the use of `refute_operator(expected, :<, actual)` over `refute(expected < actual)`.'
259
+ StyleGuide: 'https://minitest.rubystyle.guide#refute-operator'
260
+ Enabled: pending
261
+ VersionAdded: '0.32'
262
+
214
263
  Minitest/RefutePathExists:
215
264
  Description: 'This cop enforces the test to use `refute_path_exists` instead of using `refute(File.exist?(path))`.'
216
265
  StyleGuide: 'https://minitest.rubystyle.guide/#refute-path-exists'
@@ -229,10 +278,35 @@ Minitest/RefuteRespondTo:
229
278
  Enabled: true
230
279
  VersionAdded: '0.4'
231
280
 
281
+ Minitest/RefuteSame:
282
+ Description: 'Enforces the use of `refute_same(expected, actual)` over `refute(expected.equal?(actual))`.'
283
+ StyleGuide: 'https://minitest.rubystyle.guide#refute-same'
284
+ Enabled: pending
285
+ VersionAdded: '0.26'
286
+
287
+ Minitest/ReturnInTestMethod:
288
+ Description: 'Enforces the use of `skip` instead of `return` in test methods.'
289
+ StyleGuide: 'https://minitest.rubystyle.guide/#skipping-runnable-methods'
290
+ Enabled: pending
291
+ VersionAdded: '0.31'
292
+
232
293
  Minitest/SkipEnsure:
233
294
  Description: 'Checks that `ensure` call even if `skip`.'
234
295
  Enabled: pending
296
+ Severity: warning
235
297
  VersionAdded: '0.20'
298
+ VersionChanged: '0.26'
299
+
300
+ Minitest/SkipWithoutReason:
301
+ Description: 'Checks for skipped tests missing the skipping reason.'
302
+ Enabled: pending
303
+ VersionAdded: '0.24'
304
+
305
+ Minitest/TestFileName:
306
+ Description: 'Checks if test file names start with `test_` or end with `_test.rb`.'
307
+ StyleGuide: 'https://minitest.rubystyle.guide/#file-naming'
308
+ Enabled: pending
309
+ VersionAdded: '0.26'
236
310
 
237
311
  Minitest/TestMethodName:
238
312
  Description: 'This cop enforces that test method names start with `test_` prefix.'
@@ -242,10 +316,17 @@ Minitest/TestMethodName:
242
316
  Minitest/UnreachableAssertion:
243
317
  Description: 'This cop checks for an `assert_raises` block containing any unreachable assertions.'
244
318
  Enabled: pending
319
+ Severity: warning
245
320
  VersionAdded: '0.14'
321
+ VersionChanged: '0.26'
246
322
 
247
323
  Minitest/UnspecifiedException:
248
324
  Description: 'This cop checks for a specified error in `assert_raises`.'
249
325
  StyleGuide: 'https://minitest.rubystyle.guide#unspecified-exception'
250
326
  Enabled: 'pending'
251
327
  VersionAdded: '0.10'
328
+
329
+ Minitest/UselessAssertion:
330
+ Description: 'Detects useless assertions (assertions that either always pass or always fail).'
331
+ Enabled: pending
332
+ VersionAdded: '0.26'
@@ -10,7 +10,7 @@ module RuboCop
10
10
  TEST_TEMPLATE = <<~TEST
11
11
  # frozen_string_literal: true
12
12
 
13
- require 'test_helper'
13
+ require_relative '../../../test_helper'
14
14
 
15
15
  class %<cop_name>sTest < Minitest::Test
16
16
  def test_registers_offense_when_using_bad_method
@@ -11,14 +11,30 @@ module RuboCop
11
11
  # assert(object.instance_of?(Class))
12
12
  # assert(object.instance_of?(Class), 'message')
13
13
  #
14
+ # # bad
15
+ # assert_equal(Class, object.class)
16
+ # assert_equal(Class, object.class, 'message')
17
+ #
14
18
  # # good
15
19
  # assert_instance_of(Class, object)
16
20
  # assert_instance_of(Class, object, 'message')
17
21
  #
18
22
  class AssertInstanceOf < Base
19
- extend MinitestCopRule
23
+ include InstanceOfAssertionHandleable
24
+ extend AutoCorrector
25
+
26
+ RESTRICT_ON_SEND = %i[assert assert_equal].freeze
27
+
28
+ def_node_matcher :instance_of_assertion?, <<~PATTERN
29
+ {
30
+ (send nil? :assert (send $_ :instance_of? $const) $_?)
31
+ (send nil? :assert_equal $const (send $_ :class) $_?)
32
+ }
33
+ PATTERN
20
34
 
21
- define_rule :assert, target_method: :instance_of?, inverse: true
35
+ def on_send(node)
36
+ investigate(node, :assert)
37
+ end
22
38
  end
23
39
  end
24
40
  end
@@ -9,6 +9,8 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # assert(matcher.match(string))
12
+ # assert(matcher.match?(string))
13
+ # assert(matcher =~ string)
12
14
  # assert(matcher.match(string), 'message')
13
15
  #
14
16
  # # good
@@ -18,7 +20,8 @@ module RuboCop
18
20
  class AssertMatch < Base
19
21
  extend MinitestCopRule
20
22
 
21
- define_rule :assert, target_method: :match, inverse: 'regexp_type?'
23
+ define_rule :assert, target_method: %i[match match? =~],
24
+ preferred_method: :assert_match, inverse: 'regexp_type?'
22
25
  end
23
26
  end
24
27
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Enforces the use of `assert_operator(expected, :<, actual)` over `assert(expected < actual)`.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # assert(expected < actual)
12
+ #
13
+ # # good
14
+ # assert_operator(expected, :<, actual)
15
+ #
16
+ class AssertOperator < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Prefer using `assert_operator(%<new_arguments>s)`.'
20
+ RESTRICT_ON_SEND = %i[assert].freeze
21
+ ALLOWED_OPERATORS = [:[]].freeze
22
+
23
+ def on_send(node)
24
+ first_argument = node.first_argument
25
+ return unless first_argument.respond_to?(:binary_operation?) && first_argument.binary_operation?
26
+
27
+ operator = first_argument.to_a[1]
28
+ return if ALLOWED_OPERATORS.include?(operator)
29
+
30
+ new_arguments = build_new_arguments(node)
31
+
32
+ add_offense(node, message: format(MSG, new_arguments: new_arguments)) do |corrector|
33
+ corrector.replace(node.loc.selector, 'assert_operator')
34
+
35
+ corrector.replace(range_of_arguments(node), new_arguments)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def build_new_arguments(node)
42
+ lhs, op, rhs = *node.first_argument
43
+ new_arguments = "#{lhs.source}, :#{op}, #{rhs.source}"
44
+
45
+ if node.arguments.count == 2
46
+ new_arguments << ", #{node.last_argument.source}"
47
+ else
48
+ new_arguments
49
+ end
50
+ end
51
+
52
+ def range_of_arguments(node)
53
+ node.first_argument.source_range.begin.join(node.last_argument.source_range.end)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -36,8 +36,7 @@ module RuboCop
36
36
  private
37
37
 
38
38
  def find_test_case(node)
39
- def_ancestor = node.each_ancestor(:def).first
40
- def_ancestor if test_case?(def_ancestor)
39
+ node.each_ancestor.find { |ancestor| test_case?(ancestor) }
41
40
  end
42
41
 
43
42
  def references_gvar?(assertion, gvar_name)
@@ -30,20 +30,25 @@ module RuboCop
30
30
  def on_send(node)
31
31
  assert_file_exists(node) do |path, failure_message|
32
32
  failure_message = failure_message.first
33
- good_method = build_good_method(path, failure_message)
33
+ good_method = build_good_method(node, path, failure_message)
34
34
  message = format(MSG, good_method: good_method)
35
35
 
36
36
  add_offense(node, message: message) do |corrector|
37
- corrector.replace(node, good_method)
37
+ corrector.replace(node.loc.selector, 'assert_path_exists')
38
+ corrector.replace(node.first_argument, path.source)
38
39
  end
39
40
  end
40
41
  end
41
42
 
42
43
  private
43
44
 
44
- def build_good_method(path, message)
45
+ def build_good_method(node, path, message)
45
46
  args = [path.source, message&.source].compact.join(', ')
46
- "assert_path_exists(#{args})"
47
+ if node.parenthesized?
48
+ "assert_path_exists(#{args})"
49
+ else
50
+ "assert_path_exists #{args}"
51
+ end
47
52
  end
48
53
  end
49
54
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Enforces the use of `assert_same(expected, actual)`
7
+ # over `assert(expected.equal?(actual))`.
8
+ #
9
+ # NOTE: Use `assert_same` only when there is a need to compare by identity.
10
+ # Otherwise, use `assert_equal`.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # assert(expected.equal?(actual))
15
+ #
16
+ # # good
17
+ # assert_same(expected, actual)
18
+ #
19
+ class AssertSame < Base
20
+ extend MinitestCopRule
21
+
22
+ define_rule :assert, target_method: :equal?, preferred_method: :assert_same
23
+ end
24
+ end
25
+ end
26
+ end
@@ -5,6 +5,16 @@ module RuboCop
5
5
  module Minitest
6
6
  # Enforces the test to use `assert(actual)` instead of using `assert_equal(true, actual)`.
7
7
  #
8
+ # @safety
9
+ # This cop is unsafe because true might be expected instead of truthy.
10
+ # False positives cannot be prevented when this is a variable or method return value.
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # assert_equal(true, 'truthy') # failure
15
+ # assert('truthy') # success
16
+ # ----
17
+ #
8
18
  # @example
9
19
  # # bad
10
20
  # assert_equal(true, actual)
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # Tries to detect when a user accidentally used
7
7
  # `assert` when they meant to use `assert_equal`.
8
8
  #
9
+ # NOTE: The second argument to the `assert` method named `message` and `msg` is allowed.
10
+ # Because their names are inferred as message arguments.
11
+ #
9
12
  # @safety
10
13
  # This cop is unsafe because it is not possible to determine
11
14
  # whether the second argument of `assert` is a message or not.
@@ -19,10 +22,13 @@ module RuboCop
19
22
  # assert_equal(3, my_list.length)
20
23
  # assert_equal(expected, actual)
21
24
  # assert(foo, 'message')
25
+ # assert(foo, message)
26
+ # assert(foo, msg)
22
27
  #
23
28
  class AssertWithExpectedArgument < Base
24
29
  MSG = 'Did you mean to use `assert_equal(%<arguments>s)`?'
25
30
  RESTRICT_ON_SEND = %i[assert].freeze
31
+ MESSAGE_VARIABLES = %w[message msg].freeze
26
32
 
27
33
  def_node_matcher :assert_with_two_arguments?, <<~PATTERN
28
34
  (send nil? :assert $_ $_)
@@ -30,7 +36,7 @@ module RuboCop
30
36
 
31
37
  def on_send(node)
32
38
  assert_with_two_arguments?(node) do |_expected, message|
33
- return if message.str_type? || message.dstr_type?
39
+ return if message.str_type? || message.dstr_type? || MESSAGE_VARIABLES.include?(message.source)
34
40
 
35
41
  arguments = node.arguments.map(&:source).join(', ')
36
42
  add_offense(node, message: format(MSG, arguments: arguments))
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Enforces empty line before assertion methods because it separates assertion phase.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # do_something
12
+ # assert_equal(expected, actual)
13
+ #
14
+ # # good
15
+ # do_something
16
+ #
17
+ # assert_equal(expected, actual)
18
+ #
19
+ class EmptyLineBeforeAssertionMethods < Base
20
+ include MinitestExplorationHelpers
21
+ include RangeHelp
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Add empty line before assertion.'
25
+
26
+ # rubocop:disable Metrics/CyclomaticComplexity
27
+ def on_send(node)
28
+ return unless (assertion_method = assertion_method(node))
29
+ return unless (previous_line_node = assertion_method.left_sibling)
30
+ return if node.parent.resbody_type?
31
+ return if accept_previous_line?(previous_line_node, assertion_method)
32
+
33
+ previous_line_node = previous_line_node.arguments.last if use_heredoc_argument?(previous_line_node)
34
+ return if use_assertion_method_at_last_of_block?(previous_line_node)
35
+ return unless no_empty_line?(previous_line_node, assertion_method)
36
+
37
+ register_offense(assertion_method, previous_line_node)
38
+ end
39
+ # rubocop:enable Metrics/CyclomaticComplexity
40
+
41
+ private
42
+
43
+ def assertion_method(node)
44
+ return node if assertion_method?(node)
45
+ return unless (parent = node.parent)
46
+ return unless parent.block_type?
47
+ return if parent.method?(:test)
48
+
49
+ node.parent if parent.body && assertion_method?(parent.body)
50
+ end
51
+
52
+ def accept_previous_line?(previous_line_node, node)
53
+ return true if !previous_line_node.is_a?(RuboCop::AST::Node) ||
54
+ previous_line_node.args_type? || node.parent.basic_conditional?
55
+
56
+ assertion_method?(previous_line_node)
57
+ end
58
+
59
+ def use_heredoc_argument?(node)
60
+ node.respond_to?(:arguments) && heredoc?(node.arguments.last)
61
+ end
62
+
63
+ def use_assertion_method_at_last_of_block?(node)
64
+ return false if !node.block_type? || !node.body
65
+
66
+ if node.body.begin_type?
67
+ assertion_method?(node.body.children.last)
68
+ else
69
+ assertion_method?(node.body)
70
+ end
71
+ end
72
+
73
+ def heredoc?(last_argument)
74
+ last_argument.respond_to?(:heredoc?) && last_argument.heredoc?
75
+ end
76
+
77
+ def no_empty_line?(previous_line_node, node)
78
+ previous_line = if heredoc?(previous_line_node)
79
+ previous_line_node.loc.heredoc_end.line
80
+ else
81
+ previous_line_node.loc.last_line
82
+ end
83
+
84
+ previous_line + 1 == node.loc.line
85
+ end
86
+
87
+ def register_offense(node, previous_line_node)
88
+ add_offense(node) do |corrector|
89
+ range = if heredoc?(previous_line_node)
90
+ previous_line_node.loc.heredoc_end
91
+ else
92
+ range_by_whole_lines(previous_line_node.source_range, include_final_newline: true)
93
+ end
94
+
95
+ corrector.insert_after(range, "\n")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -96,7 +96,9 @@ module RuboCop
96
96
  wont_be_kind_of wont_match wont_be_nil wont_be wont_respond_to wont_be_same_as
97
97
  ].freeze
98
98
 
99
- BLOCK_MATCHERS = %i[must_output must_raise must_be_silent must_throw].freeze
99
+ BLOCK_MATCHERS = %i[
100
+ must_output must_pattern_match must_raise must_be_silent must_throw wont_pattern_match
101
+ ].freeze
100
102
 
101
103
  RESTRICT_ON_SEND = VALUE_MATCHERS + BLOCK_MATCHERS
102
104
 
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Checks that lifecycle hooks are declared in the order in which they will be executed.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # class FooTest < Minitest::Test
11
+ # def teardown; end
12
+ # def setup; end
13
+ # end
14
+ #
15
+ # # good
16
+ # class FooTest < Minitest::Test
17
+ # def setup; end
18
+ # def teardown; end
19
+ # end
20
+ #
21
+ # # bad (after test cases)
22
+ # class FooTest < Minitest::Test
23
+ # def test_something
24
+ # assert foo
25
+ # end
26
+ # def setup; end
27
+ # def teardown; end
28
+ # end
29
+ #
30
+ # # good
31
+ # class FooTest < Minitest::Test
32
+ # def setup; end
33
+ # def teardown; end
34
+ # def test_something
35
+ # assert foo
36
+ # end
37
+ # end
38
+ #
39
+ # # good (after non test case methods)
40
+ # class FooTest < Minitest::Test
41
+ # def do_something; end
42
+ # def setup; end
43
+ # def teardown; end
44
+ # end
45
+ #
46
+ class LifecycleHooksOrder < Base
47
+ include MinitestExplorationHelpers
48
+ include RangeHelp
49
+ extend AutoCorrector
50
+
51
+ MSG = '`%<current>s` is supposed to appear before `%<previous>s`.'
52
+
53
+ # Regular method's position should be last.
54
+ REGULAR_METHOD_POSITION = LIFECYCLE_HOOK_METHODS_IN_ORDER.size + 1
55
+ HOOKS_ORDER_MAP = Hash.new do |hash, hook|
56
+ hash[hook] = LIFECYCLE_HOOK_METHODS_IN_ORDER.index(hook) || REGULAR_METHOD_POSITION
57
+ end
58
+
59
+ # rubocop:disable Metrics/MethodLength
60
+ def on_class(class_node)
61
+ return unless test_class?(class_node)
62
+
63
+ previous_index = -1
64
+ previous_hook_node = nil
65
+
66
+ hooks_and_test_cases(class_node).each do |node|
67
+ hook = node.method_name
68
+ index = HOOKS_ORDER_MAP[hook]
69
+
70
+ if index < previous_index
71
+ message = format(MSG, current: hook, previous: previous_hook_node.method_name)
72
+ add_offense(node, message: message) do |corrector|
73
+ autocorrect(corrector, previous_hook_node, node)
74
+ end
75
+ end
76
+ previous_index = index
77
+ previous_hook_node = node
78
+ end
79
+ end
80
+ # rubocop:enable Metrics/MethodLength
81
+
82
+ private
83
+
84
+ def hooks_and_test_cases(class_node)
85
+ class_def_nodes(class_node).select do |node|
86
+ lifecycle_hook_method?(node) || test_case?(node)
87
+ end
88
+ end
89
+
90
+ def autocorrect(corrector, previous_node, node)
91
+ previous_node_range = range_with_comments_and_lines(previous_node)
92
+ node_range = range_with_comments_and_lines(node)
93
+
94
+ corrector.insert_before(previous_node_range, node_range.source)
95
+ corrector.remove(node_range)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end