summaryrefslogtreecommitdiff
path: root/rubocop/cop/rspec/any_instance_of.rb
blob: a939af36c13054a5b65ddbe74c44e34bd7fc12e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# frozen_string_literal: true

module RuboCop
  module Cop
    module RSpec
      # This cop checks for `allow_any_instance_of` or `expect_any_instance_of`
      # usage in specs.
      #
      # @example
      #
      #   # bad
      #   allow_any_instance_of(User).to receive(:invalidate_issue_cache_counts)
      #
      #   # bad
      #   expect_any_instance_of(User).to receive(:invalidate_issue_cache_counts)
      #
      #   # good
      #   allow_next_instance_of(User) do |instance|
      #     allow(instance).to receive(:invalidate_issue_cache_counts)
      #   end
      #
      #   # good
      #   expect_next_instance_of(User) do |instance|
      #     expect(instance).to receive(:invalidate_issue_cache_counts)
      #   end
      #
      class AnyInstanceOf < RuboCop::Cop::Cop
        MESSAGE_EXPECT = 'Do not use `expect_any_instance_of` method, use `expect_next_instance_of` instead.'
        MESSAGE_ALLOW = 'Do not use `allow_any_instance_of` method, use `allow_next_instance_of` instead.'

        def_node_search :expect_any_instance_of?, <<~PATTERN
          (send (send nil? :expect_any_instance_of ...) ...)
        PATTERN
        def_node_search :allow_any_instance_of?, <<~PATTERN
          (send (send nil? :allow_any_instance_of ...) ...)
        PATTERN

        def on_send(node)
          if expect_any_instance_of?(node)
            add_offense(node, location: :expression, message: MESSAGE_EXPECT)
          elsif allow_any_instance_of?(node)
            add_offense(node, location: :expression, message: MESSAGE_ALLOW)
          end
        end

        def autocorrect(node)
          replacement =
            if expect_any_instance_of?(node)
              replacement_any_instance_of(node, 'expect')
            elsif allow_any_instance_of?(node)
              replacement_any_instance_of(node, 'allow')
            end

          lambda do |corrector|
            corrector.replace(node.loc.expression, replacement)
          end
        end

        private

        def replacement_any_instance_of(node, rspec_prefix)
          method_call =
            node.receiver.source.sub(
              "#{rspec_prefix}_any_instance_of",
              "#{rspec_prefix}_next_instance_of")

          block = <<~RUBY.chomp
            do |instance|
              #{rspec_prefix}(instance).#{node.method_name} #{node.children.last.source}
            end
          RUBY

          "#{method_call} #{block}"
        end
      end
    end
  end
end