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
@@ -27,24 +27,35 @@ module RuboCop
27
27
  def on_send(node)
28
28
  return unless node.method?(:assert_equal)
29
29
 
30
- expected, actual, message = *node.arguments
30
+ expected, actual, _message = *node.arguments
31
31
  return unless actual&.recursive_basic_literal?
32
32
  return if expected.recursive_basic_literal?
33
33
 
34
34
  add_offense(all_arguments_range(node)) do |corrector|
35
- autocorrect(corrector, node, expected, actual, message)
35
+ autocorrect(corrector, node, expected, actual)
36
36
  end
37
37
  end
38
38
 
39
- def autocorrect(corrector, node, expected, actual, message)
39
+ private
40
+
41
+ def autocorrect(corrector, node, expected, actual)
40
42
  new_actual_source = if actual.hash_type? && !actual.braces?
41
43
  "{#{actual.source}}"
42
44
  else
43
45
  actual.source
44
46
  end
45
- arguments = [new_actual_source, expected.source, message&.source].compact.join(', ')
46
47
 
47
- corrector.replace(node, "assert_equal(#{arguments})")
48
+ corrector.replace(expected, new_actual_source)
49
+ corrector.replace(actual, expected.source)
50
+
51
+ wrap_with_parentheses(corrector, node) if !node.parenthesized? && actual.hash_type?
52
+ end
53
+
54
+ def wrap_with_parentheses(corrector, node)
55
+ range = node.loc.selector.end.join(node.first_argument.source_range.begin)
56
+
57
+ corrector.replace(range, '(')
58
+ corrector.insert_after(node, ')')
48
59
  end
49
60
  end
50
61
  end
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Minitest
6
- # Checks if test cases contain too many assertion calls.
6
+ # Checks if test cases contain too many assertion calls. If conditional code with assertions
7
+ # is used, the branch with maximum assertions is counted.
7
8
  # The maximum allowed assertion calls is configurable.
8
9
  #
9
10
  # @example Max: 1
@@ -36,7 +37,7 @@ module RuboCop
36
37
  return unless test_class?(class_node)
37
38
 
38
39
  test_cases(class_node).each do |node|
39
- assertions_count = assertions_count(node)
40
+ assertions_count = assertions_count(node.body)
40
41
 
41
42
  next unless assertions_count > max_assertions
42
43
 
@@ -50,8 +51,26 @@ module RuboCop
50
51
  private
51
52
 
52
53
  def assertions_count(node)
53
- base = assertion_method?(node) ? 1 : 0
54
- base + node.each_child_node.sum { |c| assertions_count(c) }
54
+ return 0 unless node.is_a?(RuboCop::AST::Node)
55
+
56
+ assertions =
57
+ case node.type
58
+ when :if, :case, :case_match
59
+ assertions_count_in_branches(node.branches)
60
+ when :rescue
61
+ assertions_count(node.body) + assertions_count_in_branches(node.branches)
62
+ when :block, :numblock
63
+ assertions_count(node.body)
64
+ else
65
+ node.each_child_node.sum { |child| assertions_count(child) }
66
+ end
67
+
68
+ assertions += 1 if assertion_method?(node)
69
+ assertions
70
+ end
71
+
72
+ def assertions_count_in_branches(branches)
73
+ branches.map { |branch| assertions_count(branch) }.max
55
74
  end
56
75
 
57
76
  def max_assertions
@@ -32,16 +32,9 @@ module RuboCop
32
32
 
33
33
  next if assertions_count.positive?
34
34
 
35
- add_offense(node.block_type? ? node.loc.expression : node.loc.name)
35
+ add_offense(node.block_type? ? node.source_range : node.loc.name)
36
36
  end
37
37
  end
38
-
39
- private
40
-
41
- def assertions_count(node)
42
- base = assertion_method?(node) ? 1 : 0
43
- base + node.each_child_node.sum { |c| assertions_count(c) }
44
- end
45
38
  end
46
39
  end
47
40
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Checks if test class contains any test cases.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # class FooTest < Minitest::Test
11
+ # def do_something
12
+ # end
13
+ # end
14
+ #
15
+ # # good
16
+ # class FooTest < Minitest::Test
17
+ # def test_something
18
+ # assert true
19
+ # end
20
+ # end
21
+ #
22
+ class NoTestCases < Base
23
+ include MinitestExplorationHelpers
24
+
25
+ MSG = 'Test class should have test cases.'
26
+
27
+ def on_class(node)
28
+ return unless test_class?(node)
29
+
30
+ add_offense(node) if test_cases(node).empty?
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Detects non `public` (marked as `private` or `protected`) test methods.
7
+ # Minitest runs only test methods which are `public`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class FooTest
12
+ # private # or protected
13
+ # def test_does_something
14
+ # assert_equal 42, do_something
15
+ # end
16
+ # end
17
+ #
18
+ # # good
19
+ # class FooTest
20
+ # def test_does_something
21
+ # assert_equal 42, do_something
22
+ # end
23
+ # end
24
+ #
25
+ # # good (not a test case name)
26
+ # class FooTest
27
+ # private # or protected
28
+ # def does_something
29
+ # assert_equal 42, do_something
30
+ # end
31
+ # end
32
+ #
33
+ # # good (no assertions)
34
+ # class FooTest
35
+ # private # or protected
36
+ # def test_does_something
37
+ # do_something
38
+ # end
39
+ # end
40
+ #
41
+ class NonPublicTestMethod < Base
42
+ include MinitestExplorationHelpers
43
+ include DefNode
44
+
45
+ MSG = 'Non `public` test method detected. Make it `public` for it to run.'
46
+
47
+ def on_class(node)
48
+ test_cases(node, visibility_check: false).each do |test_case|
49
+ add_offense(test_case) if non_public?(test_case) && assertions(test_case).any?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -9,7 +9,6 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # assert("rubocop-minitest" != actual)
12
- # assert(! "rubocop-minitest" == actual)
13
12
  #
14
13
  # # good
15
14
  # refute_equal("rubocop-minitest", actual)
@@ -22,7 +21,7 @@ module RuboCop
22
21
  RESTRICT_ON_SEND = %i[assert].freeze
23
22
 
24
23
  def_node_matcher :assert_not_equal, <<~PATTERN
25
- (send nil? :assert {(send $_ :!= $_) (send (send $_ :! ) :== $_) } $... )
24
+ (send nil? :assert (send $_ :!= $_) $... )
26
25
  PATTERN
27
26
 
28
27
  def on_send(node)
@@ -36,7 +35,7 @@ module RuboCop
36
35
  corrector.replace(node.loc.selector, 'refute_equal')
37
36
 
38
37
  replacement = [expected, actual].map(&:source).join(', ')
39
- corrector.replace(first_argument_range(node), replacement)
38
+ corrector.replace(node.first_argument, replacement)
40
39
  end
41
40
  end
42
41
  end
@@ -5,6 +5,16 @@ module RuboCop
5
5
  module Minitest
6
6
  # Enforces the use of `refute(object)` over `assert_equal(false, object)`.
7
7
  #
8
+ # @safety
9
+ # This cop is unsafe because it cannot detect failure when second argument is `nil`.
10
+ # False positives cannot be prevented when this is a variable or method return value.
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # assert_equal(false, nil) # failure
15
+ # refute(nil) # success
16
+ # ----
17
+ #
8
18
  # @example
9
19
  # # bad
10
20
  # assert_equal(false, actual)
@@ -56,7 +66,7 @@ module RuboCop
56
66
  if node.method?(:assert_equal)
57
67
  corrector.replace(first_and_second_arguments_range(node), actual.source)
58
68
  else
59
- corrector.replace(first_argument_range(node), actual.source)
69
+ corrector.replace(node.first_argument, actual.source)
60
70
  end
61
71
  end
62
72
  end
@@ -11,14 +11,30 @@ module RuboCop
11
11
  # refute(object.instance_of?(Class))
12
12
  # refute(object.instance_of?(Class), 'message')
13
13
  #
14
+ # # bad
15
+ # refute_equal(Class, object.class)
16
+ # refute_equal(Class, object.class, 'message')
17
+ #
14
18
  # # good
15
19
  # refute_instance_of(Class, object)
16
20
  # refute_instance_of(Class, object, 'message')
17
21
  #
18
22
  class RefuteInstanceOf < Base
19
- extend MinitestCopRule
23
+ include InstanceOfAssertionHandleable
24
+ extend AutoCorrector
25
+
26
+ RESTRICT_ON_SEND = %i[refute refute_equal].freeze
27
+
28
+ def_node_matcher :instance_of_assertion?, <<~PATTERN
29
+ {
30
+ (send nil? :refute (send $_ :instance_of? $const) $_?)
31
+ (send nil? :refute_equal $const (send $_ :class) $_?)
32
+ }
33
+ PATTERN
20
34
 
21
- define_rule :refute, target_method: :instance_of?, inverse: true
35
+ def on_send(node)
36
+ investigate(node, :refute)
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
  # refute(matcher.match(string))
12
+ # refute(matcher.match?(string))
13
+ # refute(matcher =~ string)
12
14
  # refute(matcher.match(string), 'message')
13
15
  #
14
16
  # # good
@@ -18,7 +20,8 @@ module RuboCop
18
20
  class RefuteMatch < Base
19
21
  extend MinitestCopRule
20
22
 
21
- define_rule :refute, target_method: :match, inverse: 'regexp_type?'
23
+ define_rule :refute, target_method: %i[match match? =~],
24
+ preferred_method: :refute_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 `refute_operator(expected, :<, actual)` over `refute(expected < actual)`.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # refute(expected < actual)
12
+ #
13
+ # # good
14
+ # refute_operator(expected, :<, actual)
15
+ #
16
+ class RefuteOperator < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Prefer using `refute_operator(%<new_arguments>s)`.'
20
+ RESTRICT_ON_SEND = %i[refute].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, 'refute_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
@@ -30,20 +30,25 @@ module RuboCop
30
30
  def on_send(node)
31
31
  refute_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, 'refute_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
- "refute_path_exists(#{args})"
47
+ if node.parenthesized?
48
+ "refute_path_exists(#{args})"
49
+ else
50
+ "refute_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 `refute_same(expected, object)`
7
+ # over `refute(expected.equal?(actual))`.
8
+ #
9
+ # NOTE: Use `refute_same` only when there is a need to compare by identity.
10
+ # Otherwise, use `refute_equal`.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # refute(expected.equal?(actual))
15
+ #
16
+ # # good
17
+ # refute_same(expected, actual)
18
+ #
19
+ class RefuteSame < Base
20
+ extend MinitestCopRule
21
+
22
+ define_rule :refute, target_method: :equal?, preferred_method: :refute_same
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Enforces the use of `skip` instead of `return` in test methods.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # def test_something
11
+ # return if condition?
12
+ # assert_equal(42, something)
13
+ # end
14
+ #
15
+ # # good
16
+ # def test_something
17
+ # skip if condition?
18
+ # assert_equal(42, something)
19
+ # end
20
+ #
21
+ class ReturnInTestMethod < Base
22
+ include MinitestExplorationHelpers
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Use `skip` instead of `return`.'
26
+
27
+ def on_return(node)
28
+ return unless node.ancestors.any? { |parent| test_case?(parent) }
29
+ return if inside_block?(node)
30
+
31
+ add_offense(node) do |corrector|
32
+ corrector.replace(node, 'skip')
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def inside_block?(node)
39
+ node.ancestors.any?(&:block_type?) || node.ancestors.any?(&:numblock_type?)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Checks for skipped tests missing the skipping reason.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # skip
11
+ # skip('')
12
+ #
13
+ # # bad
14
+ # if condition?
15
+ # skip
16
+ # else
17
+ # skip
18
+ # end
19
+ #
20
+ # # good
21
+ # skip("Reason why the test was skipped")
22
+ #
23
+ # # good
24
+ # skip if condition?
25
+ #
26
+ class SkipWithoutReason < Base
27
+ MSG = 'Add a reason explaining why the test is skipped.'
28
+
29
+ RESTRICT_ON_SEND = %i[skip].freeze
30
+
31
+ def on_send(node)
32
+ return if node.receiver || !blank_argument?(node)
33
+
34
+ conditional_node = conditional_parent(node)
35
+ return if conditional_node && !only_skip_branches?(conditional_node)
36
+
37
+ return if node.parent&.resbody_type?
38
+
39
+ add_offense(node)
40
+ end
41
+
42
+ private
43
+
44
+ def blank_argument?(node)
45
+ message = node.first_argument
46
+ message.nil? || (message.str_type? && message.value == '')
47
+ end
48
+
49
+ def conditional_parent(node)
50
+ return unless (parent = node.parent)
51
+
52
+ if parent.if_type? || parent.case_type?
53
+ parent
54
+ elsif parent.when_type?
55
+ parent.parent
56
+ end
57
+ end
58
+
59
+ def only_skip_branches?(node)
60
+ branches = node.branches.compact
61
+ branches.size > 1 && branches.all? { |branch| branch.send_type? && branch.method?(:skip) }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Checks if test file names start with `test_` or end with `_test.rb`.
7
+ # Files which define classes having names ending with `Test` are checked.
8
+ # Not following this convention may result in tests not being run.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # my_class.rb
13
+ #
14
+ # # good
15
+ # my_class_test.rb
16
+ # test_my_class.rb
17
+ #
18
+ class TestFileName < Base
19
+ include MinitestExplorationHelpers
20
+
21
+ MSG = 'Test file path should start with `test_` or end with `_test.rb`.'
22
+
23
+ def on_new_investigation
24
+ return unless (ast = processed_source.ast)
25
+ return unless test_file?(ast)
26
+
27
+ add_global_offense(MSG) unless valid_file_name?
28
+ end
29
+
30
+ private
31
+
32
+ def test_file?(node)
33
+ return true if node.class_type? && test_class?(node)
34
+
35
+ node.each_descendant(:class).any? { |class_node| test_class?(class_node) }
36
+ end
37
+
38
+ def valid_file_name?
39
+ basename = File.basename(processed_source.file_path)
40
+
41
+ basename.start_with?('test_') || basename.end_with?('_test.rb')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -37,7 +37,7 @@ module RuboCop
37
37
  def on_class(class_node)
38
38
  return unless test_class?(class_node)
39
39
 
40
- class_elements(class_node).each do |node|
40
+ class_def_nodes(class_node).each do |node|
41
41
  next unless offense?(node)
42
42
 
43
43
  test_method_name = node.loc.name
@@ -50,17 +50,6 @@ module RuboCop
50
50
 
51
51
  private
52
52
 
53
- def class_elements(class_node)
54
- class_def = class_node.body
55
- return [] unless class_def
56
-
57
- if class_def.def_type?
58
- [class_def]
59
- else
60
- class_def.each_child_node(:def).to_a
61
- end
62
- end
63
-
64
53
  def offense?(node)
65
54
  return false if assertions(node).none?
66
55
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # Detects useless assertions (assertions that either always pass or always fail).
7
+ #
8
+ # @example
9
+ # # bad
10
+ # assert true
11
+ # assert_equal @foo, @foo
12
+ # assert_nil [foo, bar]
13
+ #
14
+ # # good
15
+ # assert something
16
+ # assert_equal foo, bar
17
+ # assert_nil foo
18
+ # assert false, "My message"
19
+ #
20
+ class UselessAssertion < Base
21
+ MSG = 'Useless assertion detected.'
22
+
23
+ SINGLE_ASSERTION_ARGUMENT_METHODS = %i[
24
+ assert refute assert_nil refute_nil assert_not assert_empty refute_empty
25
+ ].freeze
26
+ TWO_ASSERTION_ARGUMENTS_METHODS = %i[
27
+ assert_equal refute_equal assert_in_delta refute_in_delta
28
+ assert_in_epsilon refute_in_epsilon assert_same refute_same
29
+ ].freeze
30
+
31
+ RESTRICT_ON_SEND = SINGLE_ASSERTION_ARGUMENT_METHODS +
32
+ TWO_ASSERTION_ARGUMENTS_METHODS +
33
+ %i[assert_includes refute_includes assert_silent]
34
+
35
+ def on_send(node)
36
+ return if node.receiver
37
+
38
+ add_offense(node) if offense?(node)
39
+ end
40
+
41
+ private
42
+
43
+ # rubocop:disable Metrics
44
+ def offense?(node)
45
+ expected, actual, = node.arguments
46
+
47
+ case node.method_name
48
+ when *SINGLE_ASSERTION_ARGUMENT_METHODS
49
+ actual.nil? && expected&.literal? && !expected.xstr_type?
50
+ when *TWO_ASSERTION_ARGUMENTS_METHODS
51
+ return false unless expected || actual
52
+ return false if expected.source != actual.source
53
+
54
+ (expected.variable? && actual.variable?) ||
55
+ (empty_composite?(expected) && empty_composite?(actual))
56
+ when :assert_includes, :refute_includes
57
+ expected && empty_composite?(expected)
58
+ when :assert_silent
59
+ block_node = node.parent
60
+ block_node&.body.nil?
61
+ else
62
+ false
63
+ end
64
+ end
65
+ # rubocop:enable Metrics
66
+
67
+ def empty_composite?(node)
68
+ return true if node.str_type? && node.value.empty?
69
+
70
+ (node.array_type? || node.hash_type?) && node.children.empty?
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end