module EmailSpec module Matchers class ReplyTo def initialize(email) @expected_reply_to = Mail::ReplyToField.new(email).addrs.first end def description "have reply to as #{@expected_reply_to.address}" end def matches?(email) @email = email @actual_reply_to = (email.reply_to || []).first !@actual_reply_to.nil? && @actual_reply_to == @expected_reply_to.address end def failure_message "expected #{@email.inspect} to reply to #{@expected_reply_to.address.inspect}, but it replied to #{@actual_reply_to.inspect}" end def negative_failure_message "expected #{@email.inspect} not to deliver to #{@expected_reply_to.address.inspect}, but it did" end end def reply_to(email) ReplyTo.new(email) end alias :have_reply_to :reply_to class DeliverTo def initialize(expected_email_addresses_or_objects_that_respond_to_email) emails = expected_email_addresses_or_objects_that_respond_to_email.map do |email_or_object| email_or_object.kind_of?(String) ? email_or_object : email_or_object.email end @expected_email_addresses = emails.sort end def description "be delivered to #{@expected_email_addresses.inspect}" end def matches?(email) @email = email @actual_recipients = (Array(email.to) || []).sort @actual_recipients == @expected_email_addresses end def failure_message "expected #{@email.inspect} to deliver to #{@expected_email_addresses.inspect}, but it delivered to #{@actual_recipients.inspect}" end def negative_failure_message "expected #{@email.inspect} not to deliver to #{@expected_email_addresses.inspect}, but it did" end end def deliver_to(*expected_email_addresses_or_objects_that_respond_to_email) DeliverTo.new(expected_email_addresses_or_objects_that_respond_to_email.flatten) end alias :be_delivered_to :deliver_to class DeliverFrom def initialize(email) @expected_sender = Mail::FromField.new(email).addrs.first end def description "be delivered from #{@expected_sender}" end def matches?(email) @email = email @actual_sender = (email.header[:from].addrs || []).first !@actual_sender.nil? && @actual_sender.address == @expected_sender.address && @actual_sender.display_name == @expected_sender.display_name end def failure_message %(expected #{@email.inspect} to deliver from "#{@expected_sender.to_s}", but it delivered from "#{@actual_sender.to_s}") end def negative_failure_message %(expected #{@email.inspect} not to deliver from "#{@expected_sender.to_s}", but it did) end end def deliver_from(email) DeliverFrom.new(email) end alias :be_delivered_from :deliver_from class BccTo def initialize(expected_email_addresses_or_objects_that_respond_to_email) emails = expected_email_addresses_or_objects_that_respond_to_email.map do |email_or_object| email_or_object.kind_of?(String) ? email_or_object : email_or_object.email end @expected_email_addresses = emails.sort end def description "be bcc'd to #{@expected_email_addresses.inspect}" end def matches?(email) @email = email @actual_recipients = (Array(email.bcc) || []).sort @actual_recipients == @expected_email_addresses end def failure_message "expected #{@email.inspect} to bcc to #{@expected_email_addresses.inspect}, but it was bcc'd to #{@actual_recipients.inspect}" end def negative_failure_message "expected #{@email.inspect} not to bcc to #{@expected_email_addresses.inspect}, but it did" end end def bcc_to(*expected_email_addresses_or_objects_that_respond_to_email) BccTo.new(expected_email_addresses_or_objects_that_respond_to_email.flatten) end RSpec::Matchers.define :have_subject do match do |given| given_subject = given.subject expected_subject = expected.first if expected_subject.is_a?(String) description { "have subject of #{expected_subject.inspect}" } failure_message_for_should { "expected the subject to be #{expected_subject.inspect} but was #{given_subject.inspect}" } failure_message_for_should_not { "expected the subject not to be #{expected_subject.inspect} but was" } given_subject == expected_subject else description { "have subject matching #{expected_subject.inspect}" } failure_message_for_should { "expected the subject to match #{expected_subject.inspect}, but did not. Actual subject was: #{given_subject.inspect}" } failure_message_for_should_not { "expected the subject not to match #{expected_subject.inspect} but #{given_subject.inspect} does match it." } !!(given_subject =~ expected_subject) end end end RSpec::Matchers.define :include_email_with_subject do match do |given_emails| expected_subject = expected.first if expected_subject.is_a?(String) description { "include email with subject of #{expected_subject.inspect}" } failure_message_for_should { "expected at least one email to have the subject #{expected_subject.inspect} but none did. Subjects were #{given_emails.map(&:subject).inspect}" } failure_message_for_should_not { "expected no email with the subject #{expected_subject.inspect} but found at least one. Subjects were #{given_emails.map(&:subject).inspect}" } given_emails.map(&:subject).include?(expected_subject) else description { "include email with subject matching #{expected_subject.inspect}" } failure_message_for_should { "expected at least one email to have a subject matching #{expected_subject.inspect}, but none did. Subjects were #{given_emails.map(&:subject).inspect}" } failure_message_for_should_not { "expected no email to have a subject matching #{expected_subject.inspect} but found at least one. Subjects were #{given_emails.map(&:subject).inspect}" } !!(given_emails.any?{ |mail| mail.subject =~ expected_subject }) end end end RSpec::Matchers.define :have_body_text do match do |given| expected_text = expected.first if expected_text.is_a?(String) normalized_body = given.default_part_body.to_s.gsub(/\s+/, " ") normalized_expected = expected_text.gsub(/\s+/, " ") description { "have body including #{normalized_expected.inspect}" } failure_message_for_should { "expected the body to contain #{normalized_expected.inspect} but was #{normalized_body.inspect}" } failure_message_for_should_not { "expected the body not to contain #{normalized_expected.inspect} but was #{normalized_body.inspect}" } normalized_body.include?(normalized_expected) else given_body = given.default_part_body.to_s description { "have body matching #{expected_text.inspect}" } failure_message_for_should { "expected the body to match #{expected_text.inspect}, but did not. Actual body was: #{given_body.inspect}" } failure_message_for_should_not { "expected the body not to match #{expected_text.inspect} but #{given_body.inspect} does match it." } !!(given_body =~ expected_text) end end end def mail_headers_hash(email_headers) email_headers.fields.inject({}) { |hash, field| hash[field.field.class::FIELD_NAME] = field.to_s; hash } end RSpec::Matchers.define :have_header do match do |given| given_header = given.header expected_name, expected_value = *expected if expected_value.is_a?(String) description { "have header #{expected_name}: #{expected_value}" } failure_message_for_should { "expected the headers to include '#{expected_name}: #{expected_value}' but they were #{mail_headers_hash(given_header).inspect}" } failure_message_for_should_not { "expected the headers not to include '#{expected_name}: #{expected_value}' but they were #{mail_headers_hash(given_header).inspect}" } given_header[expected_name].to_s == expected_value else description { "have header #{expected_name} with value matching #{expected_value.inspect}" } failure_message_for_should { "expected the headers to include '#{expected_name}' with a value matching #{expected_value.inspect} but they were #{mail_headers_hash(given_header).inspect}" } failure_message_for_should_not { "expected the headers not to include '#{expected_name}' with a value matching #{expected_value.inspect} but they were #{mail_headers_hash(given_header).inspect}" } given_header[expected_name].to_s =~ expected_value end end end end end