module EmailSpectacular::FailureDescriptions
Module containing the helper methods to describe the difference between the expected and actual emails sent.
@author Aleck Greenham
Public Class Methods
included(base)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 14 def self.included(base) # rubocop:disable Metrics/MethodLength base.class_eval do protected def attribute_and_expected_value(scopes, emails) scopes.each do |attribute, expected| matching_emails = emails.select do |email| email_matches?(email, EmailSpectacular::Matchers::MATCHERS[attribute], expected) && (!EmailSpectacular._mocking_sending_enqueued_emails || email.instance_variable_get(:@enqueued) == @enqueued) end return [attribute, expected] if matching_emails.empty? end [nil, nil] end def describe_failed_assertion(attribute_name, attribute_value) action = mail_action_description field_descriptions = attribute_descriptions([attribute_name]) value_descriptions = value_descriptions([attribute_value]) base_clause = expectation_description( "Expected an email to be #{action}", field_descriptions, value_descriptions ) if @emails.empty? "#{base_clause} However, no emails were #{action}." elsif @matching_emails[:sent].any? || @matching_emails[:enqueued].any? opposite_action = @enqueued ? 'sent' : 'enqueued' "#{base_clause} However, it was #{opposite_action} instead." else field_descriptions = attribute_descriptions([attribute_name]) email_values = sent_email_values(@emails, attribute_name) if email_values.any? base_clause + " However, #{email_pluralisation(@emails)} #{action} " \ "#{result_description(field_descriptions, [to_sentence(email_values)])}." else base_clause end end end def attribute_descriptions(attributes) attributes.map do |attr| attr.to_s.tr('_', ' ') end end def value_descriptions(values) values.map do |value| case value when String "'#{value}'" when Array to_sentence(value.map { |val| "'#{val}'" }) else value end end end def expectation_description(base_clause, field_descriptions, value_descriptions) description = base_clause additional_clauses = [] field_descriptions.each.with_index do |field_description, index| clause = '' clause += " #{field_description}" unless field_description.empty? if (value_description = value_descriptions[index]) clause += " #{value_description}" end additional_clauses.push(clause) unless clause.empty? end description + additional_clauses.join('') + '.' end private def mail_action_description if EmailSpectacular._mocking_sending_enqueued_emails @enqueued ? 'enqueued' : 'sent' else 'sent' end end def result_description(field_descriptions, values) to_sentence( field_descriptions.map.with_index do |field_description, index| value = values[index] if ['matching selector', 'with link', 'with image'].include?(field_description) "with body #{value}" else "#{field_description} #{value}" end end ) end def sent_email_values(emails, attribute) emails.each_with_object([]) do |email, memo| if %i[matching_selector with_link with_image].include?(attribute) memo << raw_email_parts(email).inject([]) do |description, (content_type, raw_email_part)| description.push( "\n\n(Content Type #{content_type}):\n\n#{raw_email_part}" ) end.join('') else matcher = EmailSpectacular::Matchers::MATCHERS[attribute] value = case matcher when String, Symbol email.send(matcher) when Hash parsed_email_parts(email).inject([]) do |description, (content_type, parsed_email_part)| description.push( "\n\n(Content Type #{content_type}):\n\n#{matcher[:actual].call(email, parsed_email_part)}" ) end.join('') else raise ArgumentError, "Failure related to an unknown or unsupported email attribute #{attribute}" end unless attribute == :with_text value = value.is_a?(String) ? "'#{value}'" : value.map { |element| "'#{element}'" } end memo << value end end end def email_pluralisation(emails) emails.length > 2 ? "#{emails.length} were" : '1 was' end def to_sentence(items) _items = items.flatten case _items.length when 0, 1 _items.join('') when 2 _items.join(' and ') else _items[0..(_items.length - 3)].join(', ') + ', ' + _items[(_items.length - 2).._items.length - 1].join(' and ') end end end end
Public Instance Methods
attribute_and_expected_value(scopes, emails)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 18 def attribute_and_expected_value(scopes, emails) scopes.each do |attribute, expected| matching_emails = emails.select do |email| email_matches?(email, EmailSpectacular::Matchers::MATCHERS[attribute], expected) && (!EmailSpectacular._mocking_sending_enqueued_emails || email.instance_variable_get(:@enqueued) == @enqueued) end return [attribute, expected] if matching_emails.empty? end [nil, nil] end
attribute_descriptions(attributes)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 64 def attribute_descriptions(attributes) attributes.map do |attr| attr.to_s.tr('_', ' ') end end
describe_failed_assertion(attribute_name, attribute_value)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 33 def describe_failed_assertion(attribute_name, attribute_value) action = mail_action_description field_descriptions = attribute_descriptions([attribute_name]) value_descriptions = value_descriptions([attribute_value]) base_clause = expectation_description( "Expected an email to be #{action}", field_descriptions, value_descriptions ) if @emails.empty? "#{base_clause} However, no emails were #{action}." elsif @matching_emails[:sent].any? || @matching_emails[:enqueued].any? opposite_action = @enqueued ? 'sent' : 'enqueued' "#{base_clause} However, it was #{opposite_action} instead." else field_descriptions = attribute_descriptions([attribute_name]) email_values = sent_email_values(@emails, attribute_name) if email_values.any? base_clause + " However, #{email_pluralisation(@emails)} #{action} " \ "#{result_description(field_descriptions, [to_sentence(email_values)])}." else base_clause end end end
email_pluralisation(emails)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 160 def email_pluralisation(emails) emails.length > 2 ? "#{emails.length} were" : '1 was' end
expectation_description(base_clause, field_descriptions, value_descriptions)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 83 def expectation_description(base_clause, field_descriptions, value_descriptions) description = base_clause additional_clauses = [] field_descriptions.each.with_index do |field_description, index| clause = '' clause += " #{field_description}" unless field_description.empty? if (value_description = value_descriptions[index]) clause += " #{value_description}" end additional_clauses.push(clause) unless clause.empty? end description + additional_clauses.join('') + '.' end
mail_action_description()
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 104 def mail_action_description if EmailSpectacular._mocking_sending_enqueued_emails @enqueued ? 'enqueued' : 'sent' else 'sent' end end
result_description(field_descriptions, values)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 112 def result_description(field_descriptions, values) to_sentence( field_descriptions.map.with_index do |field_description, index| value = values[index] if ['matching selector', 'with link', 'with image'].include?(field_description) "with body #{value}" else "#{field_description} #{value}" end end ) end
sent_email_values(emails, attribute)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 126 def sent_email_values(emails, attribute) emails.each_with_object([]) do |email, memo| if %i[matching_selector with_link with_image].include?(attribute) memo << raw_email_parts(email).inject([]) do |description, (content_type, raw_email_part)| description.push( "\n\n(Content Type #{content_type}):\n\n#{raw_email_part}" ) end.join('') else matcher = EmailSpectacular::Matchers::MATCHERS[attribute] value = case matcher when String, Symbol email.send(matcher) when Hash parsed_email_parts(email).inject([]) do |description, (content_type, parsed_email_part)| description.push( "\n\n(Content Type #{content_type}):\n\n#{matcher[:actual].call(email, parsed_email_part)}" ) end.join('') else raise ArgumentError, "Failure related to an unknown or unsupported email attribute #{attribute}" end unless attribute == :with_text value = value.is_a?(String) ? "'#{value}'" : value.map { |element| "'#{element}'" } end memo << value end end end
to_sentence(items)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 164 def to_sentence(items) _items = items.flatten case _items.length when 0, 1 _items.join('') when 2 _items.join(' and ') else _items[0..(_items.length - 3)].join(', ') + ', ' + _items[(_items.length - 2).._items.length - 1].join(' and ') end end
value_descriptions(values)
click to toggle source
# File lib/email_spectacular/concerns/failure_descriptions.rb, line 70 def value_descriptions(values) values.map do |value| case value when String "'#{value}'" when Array to_sentence(value.map { |val| "'#{val}'" }) else value end end end