# frozen_string_literal: true # Source: https://gist.github.com/ka8725/6053d55be74f15b53cabfc3ac4dc99df # Installation: put this file into spec/support/matchers/ folder. # Simple matcher that allows checking of an email was sent during an example run. # # @example Positive expectation # expect { action }.to send_email # # @example Negative expectations # expect { action }.not_to send_email # expect { action }.to not_send_email # # @example More precise expectation with attributes to match # expect { action }.to send_email(to: 'test@example.com', subject: 'Confirm email') # RSpec::Matchers.define :send_email do |criteria = {}| match do |block| @init_criteria = criteria define_matched_emails(block) @matched_emails.size == 1 end failure_message do if multiple_match? 'More than 1 matching emails were sent.' else super() end end match_when_negated do |block| define_matched_emails(block) @matched_emails.size.zero? end diffable supports_block_expectations private def define_matched_emails(block) before = deliveries block.call after = deliveries @diff = after - before @matched_emails = select_matching(@diff) end def deliveries ActionMailer::Base.deliveries.clone end def select_matching(emails) criteria.empty? ? emails : emails.select(&method(:matched_email?)) end def matched_email?(email) # `values_match?` is RSpec's comparison API, supporting value matchers like # `hash_including`, `ends_with` and so on. criteria.all? do |attr, expected| values_match?(expected, email.public_send(attr)) end end def criteria @criteria ||= if init_criteria.key?(:to) { **init_criteria, to: Array(init_criteria[:to]) } else init_criteria end end def init_criteria @init_criteria ||= {} end def multiple_match? @matched_emails.many? end end RSpec::Matchers.define_negated_matcher :not_send_email, :send_email