require 'uri'
require 'email_spec/deliveries'
module EmailSpec
module Helpers
include Deliveries
def visit_in_email(link_text)
visit(parse_email_for_link(current_email, link_text))
end
def click_email_link_matching(regex, email = current_email)
url = links_in_email(email).detect { |link| link =~ regex }
raise "No link found matching #{regex.inspect} in #{email.default_part_body}" unless url
visit request_uri(url)
end
def click_first_link_in_email(email = current_email)
link = links_in_email(email).first
visit request_uri(link)
end
def open_email(address, opts={})
set_current_email(find_email!(address, opts))
end
alias_method :open_email_for, :open_email
def open_last_email
set_current_email(last_email_sent)
end
def open_last_email_for(address)
set_current_email(mailbox_for(address).last)
end
def current_email(address=nil)
address = convert_address(address)
email = address ? email_spec_hash[:current_emails][address] : email_spec_hash[:current_email]
raise RSpec::Expectations::ExpectationNotMetError, "Expected an open email but none was found. Did you forget to call open_email?" unless email
email
end
def current_email_attachments(address=nil)
current_email(address).attachments || Array.new
end
def unread_emails_for(address)
mailbox_for(address) - read_emails_for(address)
end
def read_emails_for(address)
email_spec_hash[:read_emails][convert_address(address)] ||= []
end
def find_email(address, opts={})
address = convert_address(address)
if opts[:with_subject]
mailbox_for(address).find { |m| m.subject =~ Regexp.new(Regexp.escape(opts[:with_subject])) }
elsif opts[:with_text]
mailbox_for(address).find { |m| m.default_part_body =~ Regexp.new(Regexp.escape(opts[:with_text])) }
else
mailbox_for(address).first
end
end
def links_in_email(email)
URI.extract(email.default_part_body.to_s, ['http', 'https'])
end
private
def email_spec_hash
@email_spec_hash ||= {:read_emails => {}, :unread_emails => {}, :current_emails => {}, :current_email => nil}
end
def find_email!(address, opts={})
email = find_email(address, opts)
if email.nil?
error = "#{opts.keys.first.to_s.humanize unless opts.empty?} #{('"' + opts.values.first.to_s.humanize + '"') unless opts.empty?}"
raise RSpec::Expectations::ExpectationNotMetError, "Could not find email #{error}. \n Found the following emails:\n\n #{all_emails.to_s}"
end
email
end
def set_current_email(email)
return unless email
[email.to, email.cc, email.bcc].compact.flatten.each do |to|
read_emails_for(to) << email
email_spec_hash[:current_emails][to] = email
end
email_spec_hash[:current_email] = email
end
def parse_email_for_link(email, text_or_regex)
email.should have_body_text(text_or_regex)
url = parse_email_for_explicit_link(email, text_or_regex)
url ||= parse_email_for_anchor_text_link(email, text_or_regex)
raise "No link found matching #{text_or_regex.inspect} in #{email}" unless url
url
end
def request_uri(link)
return unless link
url = URI::parse(link)
url.fragment ? (url.request_uri + "#" + url.fragment) : url.request_uri
end
# e.g. confirm in http://confirm
def parse_email_for_explicit_link(email, regex)
regex = /#{Regexp.escape(regex)}/ unless regex.is_a?(Regexp)
url = links_in_email(email).detect { |link| link =~ regex }
request_uri(url)
end
# e.g. Click here in Click here
def parse_email_for_anchor_text_link(email, link_text)
if textify_images(email.default_part_body) =~ %r{]*href=['"]?([^'"]*)['"]?[^>]*?>[^<]*?#{link_text}[^<]*?}
URI.split($1)[5..-1].compact!.join("?").gsub("&", "&")
# sub correct ampersand after rails switches it (http://dev.rubyonrails.org/ticket/4002)
else
return nil
end
end
def textify_images(email_body)
email_body.to_s.gsub(%r{]*alt=['"]?([^'"]*)['"]?[^>]*?/>}) { $1 }
end
def parse_email_count(amount)
case amount
when "no"
0
when "an"
1
else
amount.to_i
end
end
attr_reader :last_email_address
def convert_address(address)
@last_email_address = (address || current_email_address)
AddressConverter.instance.convert(@last_email_address)
end
# Overwrite this method to set default email address, for example:
# last_email_address || @current_user.email
def current_email_address
last_email_address
end
def mailbox_for(address)
super(convert_address(address)) # super resides in Deliveries
end
def email_spec_deprecate(text)
puts ""
puts "DEPRECATION: #{text.split.join(' ')}"
puts ""
end
end
end