rubocop-minitest 0.2.1 → 0.5.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +7 -0
  3. data/.rubocop_todo.yml +5 -5
  4. data/CHANGELOG.md +44 -0
  5. data/Gemfile +6 -2
  6. data/README.md +2 -1
  7. data/config/default.yml +70 -10
  8. data/lib/rubocop-minitest.rb +0 -3
  9. data/lib/rubocop/cop/minitest/assert_empty.rb +11 -10
  10. data/lib/rubocop/cop/minitest/assert_empty_literal.rb +36 -0
  11. data/lib/rubocop/cop/minitest/assert_equal.rb +52 -0
  12. data/lib/rubocop/cop/minitest/assert_includes.rb +13 -12
  13. data/lib/rubocop/cop/minitest/assert_instance_of.rb +59 -0
  14. data/lib/rubocop/cop/minitest/assert_nil.rb +10 -4
  15. data/lib/rubocop/cop/minitest/assert_respond_to.rb +62 -0
  16. data/lib/rubocop/cop/minitest/assert_truthy.rb +10 -5
  17. data/lib/rubocop/cop/minitest/refute_empty.rb +51 -0
  18. data/lib/rubocop/cop/minitest/refute_equal.rb +68 -0
  19. data/lib/rubocop/cop/minitest/refute_false.rb +51 -0
  20. data/lib/rubocop/cop/minitest/refute_includes.rb +61 -0
  21. data/lib/rubocop/cop/minitest/refute_instance_of.rb +59 -0
  22. data/lib/rubocop/cop/minitest/refute_nil.rb +10 -4
  23. data/lib/rubocop/cop/minitest/refute_respond_to.rb +62 -0
  24. data/lib/rubocop/cop/minitest_cops.rb +11 -0
  25. data/lib/rubocop/cop/mixin/argument_range_helper.rb +31 -0
  26. data/lib/rubocop/minitest/version.rb +1 -1
  27. data/manual/cops.md +10 -0
  28. data/manual/cops_minitest.md +264 -15
  29. data/relnotes/v0.1.0.md +7 -0
  30. data/relnotes/v0.2.0.md +9 -0
  31. data/relnotes/v0.2.1.md +5 -0
  32. data/relnotes/v0.3.0.md +16 -0
  33. data/relnotes/v0.4.0.md +14 -0
  34. data/relnotes/v0.4.1.md +5 -0
  35. data/relnotes/v0.5.0.md +5 -0
  36. data/relnotes/v0.5.1.md +3 -0
  37. data/tasks/cut_release.rake +60 -0
  38. metadata +24 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc2eabe582388a8716653ef64d84833b48547f2c0e84fd666c36ab59b773abdc
4
- data.tar.gz: 726cd0a1cc2f11eee03f49278408b9eda29308d74641cdc8a562f84c063e4e8c
3
+ metadata.gz: c3c643f7623a14a4a99f813daf392e31a491b7177504cad25760093d80e0b32e
4
+ data.tar.gz: b2e5de6e39a741828ae69ca48311b366747bb19aebbc85b23c96ff346f180b39
5
5
  SHA512:
6
- metadata.gz: 60bec75d6fab0181c7f1ee5e664ca0a9dda0d8ba7d9a54d184a7f4573633e10fccef93719da3dead41c5919862f67aa0b66eb92f1dc933759c240e093ee93bd0
7
- data.tar.gz: 28d6e3f708ce291589ff8e2b74a12792777dbc39e244309951ba21e1ffdd59ac982fed3f52af148acca2b8a96bc8f0158711c3e99f893f09d26ef74beb743d2e
6
+ metadata.gz: eada0f53e4b625d321342f22aa80942512fcefa2131443a7d7d1d7a6f9fafabc1f1e8659f7b7fd2cc002bf83dcec037e760def29db035ac2f1e29bb6ace5fba3
7
+ data.tar.gz: ca010bc26330c8c32a9a28fe12f27870affa6e554c6832b6eedede0daf4c89fd5e28ffd93bca1e152a5e3785240023db9e4d35226f3e1bcfc02f49a7cffc5c68
@@ -0,0 +1,7 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: [bbatsov, koic]
4
+ patreon: bbatsov
5
+ open_collective: rubocop
6
+ tidelift: "rubygems/rubocop"
7
+ custom: https://www.paypal.me/bbatsov
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2019-08-07 15:41:02 +0900 using RuboCop version 0.74.0.
3
+ # on 2019-12-21 01:15:41 +0900 using RuboCop version 0.78.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -10,14 +10,14 @@
10
10
  Metrics/AbcSize:
11
11
  Max: 17
12
12
 
13
- # Offense count: 5
13
+ # Offense count: 19
14
14
  # Configuration parameters: CountComments, ExcludedMethods.
15
15
  Metrics/MethodLength:
16
16
  Max: 14
17
17
 
18
- # Offense count: 2
18
+ # Offense count: 40
19
19
  # Cop supports --auto-correct.
20
20
  # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
21
21
  # URISchemes: http, https
22
- Metrics/LineLength:
23
- Max: 87
22
+ Layout/LineLength:
23
+ Max: 90
@@ -2,6 +2,49 @@
2
2
 
3
3
  ## master (unreleased)
4
4
 
5
+ ## 0.5.1 (2019-12-25)
6
+
7
+ * [#42](https://github.com/rubocop-hq/rubocop-minitest/issues/42): Fix an incorrect autocorrect for some cops of `Minitest` department when using heredoc message. ([@koic][])
8
+
9
+ ## 0.5.0 (2019-11-24)
10
+
11
+ ### New features
12
+
13
+ * [#32](https://github.com/rubocop-hq/rubocop-minitest/issues/32): Add new `Minitest/AssertEmptyLiteral` cop. ([@tejasbubane][])
14
+
15
+ ## 0.4.1 (2019-11-10)
16
+
17
+ ### Bug fixes
18
+
19
+ * [#39](https://github.com/rubocop-hq/rubocop-minitest/issues/39): Fix an incorrect autocorrect for `Minitest/AssertRespondTo` and `Minitest/RefuteRespondTo` when using assertion method calling `respond_to` with receiver omitted. ([@koic][])
20
+
21
+ ## 0.4.0 (2019-11-07)
22
+
23
+ ### New features
24
+
25
+ * [#29](https://github.com/rubocop-hq/rubocop-minitest/pull/29): Add new `Minitest/RefuteRespondTo` cop. ([@herwinw][])
26
+ * [#31](https://github.com/rubocop-hq/rubocop-minitest/pull/31): Add new `Minitest/AssertEqual` cop. ([@herwinw][])
27
+ * [#34](https://github.com/rubocop-hq/rubocop-minitest/pull/34): Add new `Minitest/AssertInstanceOf` cop. ([@abhaynikam][])
28
+ * [#35](https://github.com/rubocop-hq/rubocop-minitest/pull/35): Add new `Minitest/RefuteInstanceOf` cop. ([@abhaynikam][])
29
+
30
+ ### Bug fixes
31
+
32
+ * [#25](https://github.com/rubocop-hq/rubocop-minitest/issues/25): Add `Enabled: true` to `Minitest` department config to suppress `Warning: Minitest does not support Enabled parameter`. ([@koic][])
33
+
34
+ ## 0.3.0 (2019-10-13)
35
+
36
+ ### New features
37
+
38
+ * [#15](https://github.com/rubocop-hq/rubocop-minitest/pull/15): Add new `Minitest/RefuteIncludes` cop. ([@abhaynikam][])
39
+ * [#18](https://github.com/rubocop-hq/rubocop-minitest/pull/18): Add new `Minitest/RefuteFalse` cop. ([@duduribeiro][])
40
+ * [#20](https://github.com/rubocop-hq/rubocop-minitest/pull/20): Add new `Minitest/RefuteEmpty` cop. ([@abhaynikam][])
41
+ * [#21](https://github.com/rubocop-hq/rubocop-minitest/pull/21): Add new `Minitest/RefuteEqual` cop. ([@duduribeiro][])
42
+ * [#27](https://github.com/rubocop-hq/rubocop-minitest/pull/27): Add new `Minitest/AssertRespondTo` cop. ([@duduribeiro][])
43
+
44
+ ### Bug fixes
45
+
46
+ * [#19](https://github.com/rubocop-hq/rubocop-minitest/pull/19): Fix a false negative for `Minitest/AssertIncludes` when using `include` method in arguments of `assert` method. ([@abhaynikam][])
47
+
5
48
  ## 0.2.1 (2019-09-24)
6
49
 
7
50
  ### Bug fixes
@@ -28,3 +71,4 @@
28
71
  [@duduribeiro]: https://github.com/duduribeiro
29
72
  [@tejasbubane]: https://github.com/tejasbubane
30
73
  [@abhaynikam]: https://github.com/abhaynikam
74
+ [@herwinw]: https://github.com/herwinw
data/Gemfile CHANGED
@@ -6,7 +6,11 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
6
6
 
7
7
  gemspec
8
8
 
9
+ gem 'bump', require: false
9
10
  gem 'rake'
10
11
  gem 'rubocop', github: 'rubocop-hq/rubocop'
11
- gem 'rubocop-performance', '~> 1.4.0'
12
- gem 'yard', '~> 0.9'
12
+ gem 'rubocop-performance', '~> 1.5.0'
13
+ # Workaround for YARD 0.9.20 or lower.
14
+ # It specifies `github` until the release that includes the following changes:
15
+ # https://github.com/lsegal/yard/pull/1290
16
+ gem 'yard', github: 'lsegal/yard', ref: '10a2e5b'
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-minitest.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-minitest)
5
5
 
6
6
  A [RuboCop](https://github.com/rubocop-hq/rubocop) extension focused on enforcing Minitest best practices and coding conventions.
7
+ The library is based on the guidelines outlined in the community [Minitest Style Guide](https://minitest.rubystyle.guide).
7
8
 
8
9
  ## Installation
9
10
 
@@ -61,7 +62,7 @@ end
61
62
 
62
63
  All cops are located under
63
64
  [`lib/rubocop/cop/minitest`](lib/rubocop/cop/minitest), and contain
64
- examples/documentation.
65
+ examples/documentation. The documentation is published [here](https://docs.rubocop.org/projects/minitest).
65
66
 
66
67
  In your `.rubocop.yml`, you may treat the Minitest cops just like any other
67
68
  cop. For example:
@@ -1,33 +1,93 @@
1
1
  Minitest:
2
+ Enabled: true
2
3
  Include:
3
4
  - '**/test/**/*'
4
5
 
5
- Minitest/AssertNil:
6
- Description: 'Check if your test uses `assert_nil` instead of `assert_equal(nil, something)`.'
7
- StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-nil'
8
- Enabled: true
9
- VersionAdded: '0.1'
10
-
11
6
  Minitest/AssertEmpty:
12
- Description: 'Check if your test uses `assert_empty` instead of `assert(actual.empty?)`.'
7
+ Description: 'This cop enforces the test to use `assert_empty` instead of using `assert(object.empty?)`.'
13
8
  StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-empty'
14
9
  Enabled: true
15
10
  VersionAdded: '0.2'
16
11
 
12
+ Minitest/AssertEmptyLiteral:
13
+ Description: 'This cop enforces the test to use `assert_empty` instead of using `assert([], object)` or `assert({}, object)`.'
14
+ Enabled: true
15
+ VersionAdded: '0.5'
16
+
17
+ Minitest/AssertEqual:
18
+ Description: 'This cop enforces the test to use `assert_equal` instead of using `assert(expected == actual)`.'
19
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-equal-arguments-order'
20
+ Enabled: true
21
+ VersionAdded: '0.4'
22
+
17
23
  Minitest/AssertIncludes:
18
- Description: 'Check if your test uses `assert_includes` instead of `assert(collection.includes?(actual))`.'
24
+ Description: 'This cop enforces the test to use `assert_includes` instead of using `assert(collection.include?(object))`.'
19
25
  StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-includes'
20
26
  Enabled: true
21
27
  VersionAdded: '0.2'
22
28
 
29
+ Minitest/AssertInstanceOf:
30
+ Description: 'This cop enforces the test to use `assert_instance_of(Class, object)` over `assert(object.instance_of?(Class))`'
31
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-instance-of'
32
+ Enabled: true
33
+ VersionAdded: '0.4'
34
+
35
+ Minitest/AssertNil:
36
+ Description: 'This cop enforces the test to use `assert_nil` instead of using `assert_equal(nil, something)`.'
37
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-nil'
38
+ Enabled: true
39
+ VersionAdded: '0.1'
40
+
41
+ Minitest/AssertRespondTo:
42
+ Description: 'This cop enforces the test to use `assert_respond_to(object, :some_method)` over `assert(object.respond_to?(:some_method))`.'
43
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-responds-to-method'
44
+ Enabled: true
45
+ VersionAdded: '0.3'
46
+
23
47
  Minitest/AssertTruthy:
24
- Description: 'Check if your test uses `assert(actual)` instead of `assert_equal(true, actual)`.'
48
+ Description: 'This cop enforces the test to use `assert(actual)` instead of using `assert_equal(true, actual)`.'
25
49
  StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-truthy'
26
50
  Enabled: true
27
51
  VersionAdded: '0.2'
28
52
 
53
+ Minitest/RefuteEmpty:
54
+ Description: 'This cop enforces to use `refute_empty` instead of using `refute(object.empty?)`.'
55
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-empty'
56
+ Enabled: true
57
+ VersionAdded: '0.3'
58
+
59
+ Minitest/RefuteEqual:
60
+ Description: 'Check if your test uses `refute_equal` instead of `assert(expected != object)` or `assert(! expected == object))`.'
61
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-equal'
62
+ Enabled: true
63
+ VersionAdded: '0.3'
64
+
65
+ Minitest/RefuteFalse:
66
+ Description: 'Check if your test uses `refute(actual)` instead of `assert_equal(false, actual)`.'
67
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-false'
68
+ Enabled: true
69
+ VersionAdded: '0.3'
70
+
71
+ Minitest/RefuteIncludes:
72
+ Description: 'This cop enforces the test to use `refute_includes` instead of using `refute(collection.include?(object))`.'
73
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-includes'
74
+ Enabled: true
75
+ VersionAdded: '0.3'
76
+
77
+ Minitest/RefuteInstanceOf:
78
+ Description: 'This cop enforces the test to use `refute_instance_of(Class, object)` over `refute(object.instance_of?(Class))`.'
79
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-instance-of'
80
+ Enabled: true
81
+ VersionAdded: '0.4'
82
+
29
83
  Minitest/RefuteNil:
30
- Description: 'Check if your test uses `refute_nil` instead of `refute_equal(nil, something)`.'
84
+ Description: 'This cop enforces the test to use `refute_nil` instead of using `refute_equal(nil, something)`.'
31
85
  StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-nil'
32
86
  Enabled: true
33
87
  VersionAdded: '0.2'
88
+
89
+ Minitest/RefuteRespondTo:
90
+ Description: 'This cop enforces the test to use `refute_respond_to(object, :some_method)` over `refute(object.respond_to?(:some_method))`.'
91
+ StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-respond-to'
92
+ Enabled: true
93
+ VersionAdded: '0.4'
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
- require 'yaml'
5
-
6
3
  require 'rubocop'
7
4
 
8
5
  require_relative 'rubocop/minitest'
@@ -3,18 +3,21 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Minitest
6
- # Check if your test uses `assert_empty` instead of `assert(actual.empty?)`.
6
+ # This cop enforces the test to use `assert_empty`
7
+ # instead of using `assert(object.empty?)`.
7
8
  #
8
9
  # @example
9
10
  # # bad
10
- # assert(actual.empty?)
11
- # assert(actual.empty?, 'the message')
11
+ # assert(object.empty?)
12
+ # assert(object.empty?, 'the message')
12
13
  #
13
14
  # # good
14
- # assert_empty(actual)
15
- # assert_empty(actual, 'the message')
15
+ # assert_empty(object)
16
+ # assert_empty(object, 'the message')
16
17
  #
17
18
  class AssertEmpty < Cop
19
+ include ArgumentRangeHelper
20
+
18
21
  MSG = 'Prefer using `assert_empty(%<arguments>s)` over ' \
19
22
  '`assert(%<receiver>s)`.'
20
23
 
@@ -36,11 +39,9 @@ module RuboCop
36
39
 
37
40
  def autocorrect(node)
38
41
  lambda do |corrector|
39
- assert_with_empty(node) do |_first_receiver_arg, actual, rest_receiver_arg|
40
- message = rest_receiver_arg.first
41
-
42
- replacement = [actual.source, message&.source].compact.join(', ')
43
- corrector.replace(node.loc.expression, "assert_empty(#{replacement})")
42
+ assert_with_empty(node) do |_, actual_arg|
43
+ corrector.replace(node.loc.selector, 'assert_empty')
44
+ corrector.replace(first_argument_range(node), actual_arg.source)
44
45
  end
45
46
  end
46
47
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `assert_empty`
7
+ # instead of using `assert([], object)`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert([], object)
12
+ # assert({}, object)
13
+ #
14
+ # # good
15
+ # assert_empty(object)
16
+ #
17
+ class AssertEmptyLiteral < Cop
18
+ MSG = 'Prefer using `assert_empty(%<arguments>s)` over ' \
19
+ '`assert(%<literal>s, %<arguments>s)`.'
20
+
21
+ def_node_matcher :assert_with_empty_literal, <<~PATTERN
22
+ (send nil? :assert ${hash array} $...)
23
+ PATTERN
24
+
25
+ def on_send(node)
26
+ assert_with_empty_literal(node) do |literal, matchers|
27
+ args = matchers.map(&:source).join(', ')
28
+
29
+ message = format(MSG, literal: literal.source, arguments: args)
30
+ add_offense(node, message: message)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the use of `assert_equal(expected, actual)`
7
+ # over `assert(expected == actual)`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert("rubocop-minitest" == actual)
12
+ #
13
+ # # good
14
+ # assert_equal("rubocop-minitest", actual)
15
+ #
16
+ class AssertEqual < Cop
17
+ include ArgumentRangeHelper
18
+
19
+ MSG = 'Prefer using `assert_equal(%<preferred>s)` over ' \
20
+ '`assert(%<over>s)`.'
21
+
22
+ def_node_matcher :assert_equal, <<~PATTERN
23
+ (send nil? :assert $(send $_ :== $_) $...)
24
+ PATTERN
25
+
26
+ def on_send(node)
27
+ assert_equal(node) do |first_receiver_arg, expected, actual, rest_receiver_arg|
28
+ message = rest_receiver_arg.first
29
+ preferred = [expected.source, actual.source, message&.source]
30
+ .compact.join(', ')
31
+ over = [first_receiver_arg.source, message&.source].compact.join(', ')
32
+
33
+ offense_message = format(MSG, preferred: preferred, over: over)
34
+
35
+ add_offense(node, message: offense_message)
36
+ end
37
+ end
38
+
39
+ def autocorrect(node)
40
+ lambda do |corrector|
41
+ assert_equal(node) do |_, expected, actual|
42
+ corrector.replace(node.loc.selector, 'assert_equal')
43
+
44
+ replacement = [expected, actual].map(&:source).join(', ')
45
+ corrector.replace(first_argument_range(node), replacement)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -3,24 +3,26 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Minitest
6
- # Check if your test uses `assert_includes`
7
- # instead of `assert(collection.includes?(actual))`.
6
+ # This cop enforces the test to use `assert_includes`
7
+ # instead of using `assert(collection.include?(object))`.
8
8
  #
9
9
  # @example
10
10
  # # bad
11
- # assert(collection.includes?(actual))
12
- # assert(collection.includes?(actual), 'the message')
11
+ # assert(collection.include?(object))
12
+ # assert(collection.include?(object), 'the message')
13
13
  #
14
14
  # # good
15
- # assert_includes(collection, actual)
16
- # assert_includes(collection, actual, 'the message')
15
+ # assert_includes(collection, object)
16
+ # assert_includes(collection, object, 'the message')
17
17
  #
18
18
  class AssertIncludes < Cop
19
+ include ArgumentRangeHelper
20
+
19
21
  MSG = 'Prefer using `assert_includes(%<arguments>s)` over ' \
20
22
  '`assert(%<receiver>s)`.'
21
23
 
22
24
  def_node_matcher :assert_with_includes, <<~PATTERN
23
- (send nil? :assert $(send $_ :includes? $_) $...)
25
+ (send nil? :assert $(send $_ :include? $_) $...)
24
26
  PATTERN
25
27
 
26
28
  def on_send(node)
@@ -39,12 +41,11 @@ module RuboCop
39
41
 
40
42
  def autocorrect(node)
41
43
  lambda do |corrector|
42
- assert_with_includes(node) do
43
- |_receiver, collection, actual, rest_receiver_arg|
44
+ assert_with_includes(node) do |_, collection, actual|
45
+ corrector.replace(node.loc.selector, 'assert_includes')
44
46
 
45
- message = rest_receiver_arg.first
46
- replacement = node_arguments(collection, actual, message)
47
- corrector.replace(node.loc.expression, "assert_includes(#{replacement})")
47
+ replacement = [collection, actual].map(&:source).join(', ')
48
+ corrector.replace(first_argument_range(node), replacement)
48
49
  end
49
50
  end
50
51
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `assert_instance_of(Class, object)`
7
+ # over `assert(object.instance_of?(Class))`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert(object.instance_of?(Class))
12
+ # assert(object.instance_of?(Class), 'the message')
13
+ #
14
+ # # good
15
+ # assert_instance_of(Class, object)
16
+ # assert_instance_of(Class, object, 'the message')
17
+ #
18
+ class AssertInstanceOf < Cop
19
+ include ArgumentRangeHelper
20
+
21
+ MSG = 'Prefer using `assert_instance_of(%<arguments>s)` over ' \
22
+ '`assert(%<receiver>s)`.'
23
+
24
+ def_node_matcher :assert_with_instance_of, <<~PATTERN
25
+ (send nil? :assert $(send $_ :instance_of? $_) $...)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ assert_with_instance_of(node) do |first_receiver_arg, object, method, rest_args|
30
+ message = rest_args.first
31
+ arguments = node_arguments(object, method, message)
32
+ receiver = [first_receiver_arg.source, message&.source].compact.join(', ')
33
+
34
+ offense_message = format(MSG, arguments: arguments, receiver: receiver)
35
+
36
+ add_offense(node, message: offense_message)
37
+ end
38
+ end
39
+
40
+ def autocorrect(node)
41
+ lambda do |corrector|
42
+ assert_with_instance_of(node) do |_, object, method|
43
+ corrector.replace(node.loc.selector, 'assert_instance_of')
44
+
45
+ replacement = [method, object].map(&:source).join(', ')
46
+ corrector.replace(first_argument_range(node), replacement)
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def node_arguments(object, method, message)
54
+ [method, object, message].compact.map(&:source).join(', ')
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end