rubocop-minitest 0.22.2 → 0.32.2

Sign up to get free protection for your applications and to get access to all the features.
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