email_spectacular 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rubocop.yml +40 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/Gemfile +5 -0
- data/Guardfile +37 -0
- data/LICENSE.txt +22 -0
- data/README.md +166 -0
- data/Rakefile +3 -0
- data/email_spectacular.gemspec +32 -0
- data/lib/email_spectacular.rb +11 -0
- data/lib/email_spectacular/dsl.rb +178 -0
- data/lib/email_spectacular/email_filter.rb +13 -0
- data/lib/email_spectacular/expectation.rb +88 -0
- data/lib/email_spectacular/failure_descriptions.rb +149 -0
- data/lib/email_spectacular/matchers.rb +66 -0
- data/lib/email_spectacular/parser.rb +26 -0
- data/lib/email_spectacular/rspec.rb +39 -0
- data/lib/email_spectacular/version.rb +5 -0
- data/spec/email_expectation_spec.rb +429 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/email_mock.rb +45 -0
- metadata +175 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/dsl'
|
4
|
+
require 'email_spectacular/failure_descriptions'
|
5
|
+
require 'email_spectacular/matchers'
|
6
|
+
|
7
|
+
module EmailSpectacular
|
8
|
+
class EmailFilter
|
9
|
+
include DSL
|
10
|
+
include Matchers
|
11
|
+
include FailureDescriptions
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/email_filter'
|
4
|
+
|
5
|
+
module EmailSpectacular
|
6
|
+
# Backing class for {#have_been_sent} declarative syntax for specifying email
|
7
|
+
# expectations. Provides ability to assert emails have been sent in a given test
|
8
|
+
# that match particular attribute values, such as sender, receiver or contents.
|
9
|
+
#
|
10
|
+
# Implements the RSpec Matcher interface
|
11
|
+
#
|
12
|
+
# @author Aleck Greenham
|
13
|
+
#
|
14
|
+
# @see EmailSpectacular::RSpec#email
|
15
|
+
# @see EmailSpectacular::RSpec#have_been_sent
|
16
|
+
class Expectation < EmailFilter
|
17
|
+
# Creates a new EmailSpectacular::Expectation object
|
18
|
+
#
|
19
|
+
# @return [EmailSpectacular::Expectation] new expectation object
|
20
|
+
def initialize
|
21
|
+
@failure_message = 'Expected email to be sent'
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
# Declares that RSpec should not attempt to diff the actual and expected values
|
26
|
+
# to put in the failure message. This class takes care of diffing and presenting
|
27
|
+
# the differences, itself.
|
28
|
+
#
|
29
|
+
# @return [false] Always returns false
|
30
|
+
def diffable?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Whether at least one email was sent during the current test that matches the
|
35
|
+
# constructed expectation
|
36
|
+
#
|
37
|
+
# @return [Boolean] True when a matching email was sent
|
38
|
+
def matches?(emails)
|
39
|
+
@emails = emails
|
40
|
+
matching_emails(emails, @scopes).any?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Message to display to StdOut by RSpec if the equality check fails. Includes a
|
44
|
+
# complete a human-readable summary of the differences between what emails were
|
45
|
+
# expected to be sent, and what were actually sent (if any).
|
46
|
+
#
|
47
|
+
# This method is only used when the positive assertion is used, i.e.
|
48
|
+
# <tt>expect(email).to have_been_sent<tt>.
|
49
|
+
#
|
50
|
+
# For the failure message used for negative assertions, i.e.
|
51
|
+
# <tt>expect(email).to_not have_been_sent</tt>, see #failure_message_when_negated
|
52
|
+
#
|
53
|
+
# @see #failure_message_when_negated
|
54
|
+
#
|
55
|
+
# @return [String] message Full failure message with explanation of the differences
|
56
|
+
# between what emails were expected and what was actually sent
|
57
|
+
def failure_message
|
58
|
+
attribute, expected_value =
|
59
|
+
attribute_and_expected_value(@scopes, @emails)
|
60
|
+
|
61
|
+
describe_failed_assertion(
|
62
|
+
@emails,
|
63
|
+
attribute,
|
64
|
+
expected_value
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Failure message to display for negative RSpec assertions, i.e.
|
69
|
+
# <tt>expect(email).to_not have_been_sent</tt>.
|
70
|
+
#
|
71
|
+
# For the failure message displayed for positive assertions, see #failure_message.
|
72
|
+
#
|
73
|
+
# @see #failure_message
|
74
|
+
#
|
75
|
+
# @return [String] message Full failure message with explanation of the differences
|
76
|
+
# between what emails were expected and what was actually sent
|
77
|
+
def failure_message_when_negated
|
78
|
+
field_descriptions = attribute_descriptions(@scopes.keys)
|
79
|
+
value_descriptions = value_descriptions(@scopes.values)
|
80
|
+
|
81
|
+
expectation_description(
|
82
|
+
'Expected no emails to be sent',
|
83
|
+
field_descriptions,
|
84
|
+
value_descriptions
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/parser'
|
4
|
+
require 'email_spectacular/matchers'
|
5
|
+
|
6
|
+
module EmailSpectacular
|
7
|
+
# Module containing the helper methods to describe the difference between the expected
|
8
|
+
# and actual emails sent.
|
9
|
+
#
|
10
|
+
# @author Aleck Greenham
|
11
|
+
module FailureDescriptions # rubocop:disable Metrics/ModuleLength
|
12
|
+
include Parser
|
13
|
+
|
14
|
+
def self.included(base) # rubocop:disable Metrics/MethodLength
|
15
|
+
base.class_eval do
|
16
|
+
protected
|
17
|
+
|
18
|
+
def attribute_and_expected_value(scopes, emails)
|
19
|
+
scopes.each do |attribute, expected|
|
20
|
+
matching_emails =
|
21
|
+
emails.select do |email|
|
22
|
+
email_matches?(email, EmailSpectacular::Matchers::MATCHERS[attribute], expected)
|
23
|
+
end
|
24
|
+
|
25
|
+
return [attribute, expected] if matching_emails.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
[nil, nil]
|
29
|
+
end
|
30
|
+
|
31
|
+
def describe_failed_assertion(emails, attribute_name, attribute_value)
|
32
|
+
field_descriptions = attribute_descriptions([attribute_name])
|
33
|
+
value_descriptions = value_descriptions([attribute_value])
|
34
|
+
|
35
|
+
base_clause = expectation_description(
|
36
|
+
'Expected an email to be sent',
|
37
|
+
field_descriptions,
|
38
|
+
value_descriptions
|
39
|
+
)
|
40
|
+
|
41
|
+
if emails.empty?
|
42
|
+
"#{base_clause} However, no emails were sent."
|
43
|
+
else
|
44
|
+
email_values = sent_email_values(emails, attribute_name)
|
45
|
+
|
46
|
+
if email_values.any?
|
47
|
+
base_clause + " However, #{email_pluralisation(emails)} sent " \
|
48
|
+
"#{result_description(field_descriptions, [to_sentence(email_values)])}."
|
49
|
+
else
|
50
|
+
base_clause
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def attribute_descriptions(attributes)
|
56
|
+
attributes.map do |attr|
|
57
|
+
attr.to_s.tr('_', ' ')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def value_descriptions(values)
|
62
|
+
values.map do |value|
|
63
|
+
case value
|
64
|
+
when String
|
65
|
+
"'#{value}'"
|
66
|
+
when Array
|
67
|
+
to_sentence(value.map { |val| "'#{val}'" })
|
68
|
+
else
|
69
|
+
value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def expectation_description(base_clause, field_descriptions, value_descriptions)
|
75
|
+
description = base_clause
|
76
|
+
|
77
|
+
additional_clauses = []
|
78
|
+
|
79
|
+
field_descriptions.each.with_index do |field_description, index|
|
80
|
+
clause = ''
|
81
|
+
clause += " #{field_description}" unless field_description.empty?
|
82
|
+
|
83
|
+
if (value_description = value_descriptions[index])
|
84
|
+
clause += " #{value_description}"
|
85
|
+
end
|
86
|
+
|
87
|
+
additional_clauses.push(clause) unless clause.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
description + additional_clauses.join('') + '.'
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def result_description(field_descriptions, values)
|
96
|
+
to_sentence(
|
97
|
+
field_descriptions.map.with_index do |field_description, index|
|
98
|
+
value = values[index]
|
99
|
+
|
100
|
+
if ['matching selector', 'with link', 'with image'].include?(field_description)
|
101
|
+
"with body #{value}"
|
102
|
+
else
|
103
|
+
"#{field_description} #{value}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
def sent_email_values(emails, attribute)
|
110
|
+
emails.each_with_object([]) do |email, memo|
|
111
|
+
if %i[matching_selector with_link with_image].include?(attribute)
|
112
|
+
memo << email_body(email)
|
113
|
+
else
|
114
|
+
matcher = EmailSpectacular::Matchers::MATCHERS[attribute]
|
115
|
+
|
116
|
+
value =
|
117
|
+
case matcher
|
118
|
+
when String, Symbol
|
119
|
+
email.send(matcher)
|
120
|
+
when Hash
|
121
|
+
matcher[:actual].call(email, parsed_emails(email))
|
122
|
+
else
|
123
|
+
raise ArgumentError, "Failure related to an unknown or unsupported email attribute #{attribute}"
|
124
|
+
end
|
125
|
+
|
126
|
+
value = value.is_a?(String) ? "'#{value}'" : value.map { |element| "'#{element}'" }
|
127
|
+
memo << value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def email_pluralisation(emails)
|
133
|
+
emails.length > 2 ? "#{emails.length} were" : '1 was'
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_sentence(items)
|
137
|
+
case items.length
|
138
|
+
when 0, 1
|
139
|
+
items.join('')
|
140
|
+
when 2
|
141
|
+
items.join(' and ')
|
142
|
+
else
|
143
|
+
items[0..(items.length - 3)].join(', ') + items[(items.length - 3)..items.length - 1].join(' and ')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/parser'
|
4
|
+
|
5
|
+
module EmailSpectacular
|
6
|
+
# Module containing helper methods for matching expectations against emails
|
7
|
+
#
|
8
|
+
# @author Aleck Greenham
|
9
|
+
module Matchers
|
10
|
+
include Parser
|
11
|
+
|
12
|
+
MATCHERS = {
|
13
|
+
to: :to,
|
14
|
+
from: :from,
|
15
|
+
with_subject: :subject,
|
16
|
+
with_text: {
|
17
|
+
match: ->(_, email, value) { value.all? { |text| email.has_text?(text) } },
|
18
|
+
actual: ->(_, email) { email.text }
|
19
|
+
},
|
20
|
+
matching_selector: {
|
21
|
+
match: ->(_, email, value) { value.all? { |selector| email.has_selector?(selector) } },
|
22
|
+
actual: ->(_, email) { email.native },
|
23
|
+
actual_name: :with_body
|
24
|
+
},
|
25
|
+
with_link: {
|
26
|
+
match: ->(_, email, value) { value.all? { |url| email.has_selector?("a[href='#{url}']") } },
|
27
|
+
actual: ->(_, email) { email.native },
|
28
|
+
actual_name: :with_body
|
29
|
+
},
|
30
|
+
with_image: {
|
31
|
+
match: ->(_, email, value) { value.all? { |url| email.has_selector?("img[src='#{url}']") } },
|
32
|
+
actual: ->(_, email) { email.native },
|
33
|
+
actual_name: :with_body
|
34
|
+
}
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
def self.included(base) # rubocop:disable Metrics/MethodLength
|
38
|
+
base.class_eval do
|
39
|
+
def matching_emails(emails, scopes)
|
40
|
+
if scopes.any?
|
41
|
+
emails.select do |email|
|
42
|
+
scopes.all? do |attribute, expected|
|
43
|
+
email_matches?(email, MATCHERS[attribute], expected)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
else
|
47
|
+
emails
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def email_matches?(email, assertion, expected)
|
52
|
+
case assertion
|
53
|
+
when :to
|
54
|
+
!(expected & email.send(assertion)).empty?
|
55
|
+
when String, Symbol
|
56
|
+
email.send(assertion).include?(expected)
|
57
|
+
when Hash
|
58
|
+
assertion[:match].call(email, parsed_emails(email), expected)
|
59
|
+
else
|
60
|
+
raise "Unsupported assertion mapping '#{assertion}' of type #{assertion.class.name}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara'
|
4
|
+
|
5
|
+
module EmailSpectacular
|
6
|
+
# Module for parsing email bodies
|
7
|
+
#
|
8
|
+
# @author Aleck Greenham
|
9
|
+
module Parser
|
10
|
+
def parsed_emails(email)
|
11
|
+
parser(email)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parser(email)
|
15
|
+
Capybara::Node::Simple.new(email_body(email))
|
16
|
+
end
|
17
|
+
|
18
|
+
def email_body(email)
|
19
|
+
if email.parts.first
|
20
|
+
email.parts.first.body.decoded
|
21
|
+
else
|
22
|
+
email.body.encoded
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/expectation'
|
4
|
+
|
5
|
+
module EmailSpectacular
|
6
|
+
# Module containing email helper methods that can be mixed into the RSpec test scope
|
7
|
+
#
|
8
|
+
# @author Aleck Greenham
|
9
|
+
module RSpec
|
10
|
+
# Syntactic sugar for referencing the list of emails sent since the start of the
|
11
|
+
# test
|
12
|
+
#
|
13
|
+
# @example Asserting email has been sent
|
14
|
+
# expect(email).to have_been_sent.to('test@email.com')
|
15
|
+
#
|
16
|
+
# @return [Array<Mail::Message>] List of sent emails
|
17
|
+
def email
|
18
|
+
ActionMailer::Base.deliveries
|
19
|
+
end
|
20
|
+
|
21
|
+
# Clears the list of sent emails.
|
22
|
+
#
|
23
|
+
# @return void
|
24
|
+
def clear_emails
|
25
|
+
ActionMailer::Base.deliveries = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new email expectation that allows asserting emails should have specific
|
29
|
+
# attributes.
|
30
|
+
#
|
31
|
+
# @see EmailSpectacular::Expectation
|
32
|
+
#
|
33
|
+
# @example Asserting email has been sent
|
34
|
+
# expect(email).to have_been_sent.to('test@email.com')
|
35
|
+
def have_been_sent # rubocop:disable Naming/PredicateName
|
36
|
+
EmailSpectacular::Expectation.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,429 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'email_spectacular/rspec'
|
4
|
+
|
5
|
+
require_relative './support/email_mock'
|
6
|
+
|
7
|
+
RSpec.describe 'have_sent_email' do
|
8
|
+
include EmailSpectacular::RSpec
|
9
|
+
|
10
|
+
context 'when no emails have been sent' do
|
11
|
+
subject { [] }
|
12
|
+
|
13
|
+
it 'then the positive assertion fails' do
|
14
|
+
expect do
|
15
|
+
expect(subject).to have_been_sent
|
16
|
+
end.to raise_error.with_message(
|
17
|
+
'Expected an email to be sent. However, no emails were sent.'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'then the negative assertion passes' do
|
22
|
+
expect do
|
23
|
+
expect(subject).to_not have_been_sent
|
24
|
+
end.to_not raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it "then a non-matching 'to' assertion fails" do
|
28
|
+
expect do
|
29
|
+
expect(subject).to have_been_sent.to('test@email.com')
|
30
|
+
end.to raise_error.with_message(
|
31
|
+
'Expected an email to be sent to \'test@email.com\'. However, no emails were sent.'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "then a non-matching 'from' assertion fails" do
|
36
|
+
expect do
|
37
|
+
expect(subject).to have_been_sent.from('test@email.com')
|
38
|
+
end.to raise_error.with_message(
|
39
|
+
'Expected an email to be sent from \'test@email.com\'. However, no emails were sent.'
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "then a non-matching 'with_subject' assertion fails" do
|
44
|
+
expect do
|
45
|
+
expect(subject).to have_been_sent.with_subject('Subject')
|
46
|
+
end.to raise_error.with_message(
|
47
|
+
'Expected an email to be sent with subject \'Subject\'. However, no emails were sent.'
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "then a non-matching 'with_text' assertion fails" do
|
52
|
+
expect do
|
53
|
+
expect(subject).to have_been_sent.with_text('Text')
|
54
|
+
end.to raise_error.with_message(
|
55
|
+
'Expected an email to be sent with text \'Text\'. However, no emails were sent.'
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "then a non-matching 'matching_selector' assertion fails" do
|
60
|
+
expect do
|
61
|
+
expect(subject).to have_been_sent.matching_selector('h1')
|
62
|
+
end.to raise_error.with_message(
|
63
|
+
'Expected an email to be sent matching selector \'h1\'. However, no emails were sent.'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "then a non-matching 'with_link' assertion fails" do
|
68
|
+
expect do
|
69
|
+
expect(subject).to have_been_sent.with_link('www.example.com')
|
70
|
+
end.to raise_error.with_message(
|
71
|
+
'Expected an email to be sent with link \'www.example.com\'. However, no emails were sent.'
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "then a non-matching 'with_image' assertion fails" do
|
76
|
+
expect do
|
77
|
+
expect(subject).to have_been_sent.with_image('www.example.com')
|
78
|
+
end.to raise_error.with_message(
|
79
|
+
'Expected an email to be sent with image \'www.example.com\'. However, no emails were sent.'
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when an email has been sent' do
|
85
|
+
subject { [EmailMock.new] }
|
86
|
+
|
87
|
+
it 'then the unqualified assertion passes' do
|
88
|
+
expect do
|
89
|
+
expect(subject).to have_been_sent
|
90
|
+
end.to_not raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'then the unqualified negative assertion fails' do
|
94
|
+
expect do
|
95
|
+
expect(subject).to_not have_been_sent
|
96
|
+
end.to raise_error('Expected no emails to be sent.')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when a matching email has been sent' do
|
101
|
+
subject { [EmailMock.new] }
|
102
|
+
|
103
|
+
it "then a positive 'to' assertion passes" do
|
104
|
+
expect do
|
105
|
+
expect(subject).to have_been_sent.to(subject[0].to[0])
|
106
|
+
end.to_not raise_error
|
107
|
+
end
|
108
|
+
|
109
|
+
it "then a negative 'to' assertion fails" do
|
110
|
+
expect do
|
111
|
+
expect(subject).to_not have_been_sent.to(subject[0].to[0])
|
112
|
+
end.to raise_error.with_message(
|
113
|
+
"Expected no emails to be sent to '#{subject[0].to[0]}'."
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "then a positive 'from' assertion passes" do
|
118
|
+
expect do
|
119
|
+
expect(subject).to have_been_sent.from(subject[0].from[0])
|
120
|
+
end.to_not raise_error
|
121
|
+
end
|
122
|
+
|
123
|
+
it "then a negative 'from' assertion fails" do
|
124
|
+
expect do
|
125
|
+
expect(subject).to_not have_been_sent.from(subject[0].from[0])
|
126
|
+
end.to raise_error.with_message(
|
127
|
+
"Expected no emails to be sent from '#{subject[0].from[0]}'."
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "then a positive 'with_subject' assertion passes" do
|
132
|
+
expect do
|
133
|
+
expect(subject).to have_been_sent.with_subject(subject[0].subject)
|
134
|
+
end.to_not raise_error
|
135
|
+
end
|
136
|
+
|
137
|
+
it "then a negative 'with_subject' assertion fails" do
|
138
|
+
expect do
|
139
|
+
expect(subject).to_not have_been_sent.with_subject(subject[0].subject)
|
140
|
+
end.to raise_error.with_message(
|
141
|
+
"Expected no emails to be sent with subject '#{subject[0].subject}'."
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "then a positive 'with_text' assertion passes" do
|
146
|
+
expect do
|
147
|
+
expect(subject).to have_been_sent.with_text(subject[0].text)
|
148
|
+
end.to_not raise_error
|
149
|
+
end
|
150
|
+
|
151
|
+
it "then a negative 'with_text' assertion fails" do
|
152
|
+
expect do
|
153
|
+
expect(subject).to_not have_been_sent.with_text(subject[0].text)
|
154
|
+
end.to raise_error.with_message(
|
155
|
+
"Expected no emails to be sent with text '#{subject[0].text}'."
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "then a positive 'matching_selector' assertion passes" do
|
160
|
+
expect do
|
161
|
+
expect(subject).to have_been_sent.matching_selector('h1')
|
162
|
+
end.to_not raise_error
|
163
|
+
end
|
164
|
+
|
165
|
+
it "then a negative 'matching_selector' assertion fails" do
|
166
|
+
expect do
|
167
|
+
expect(subject).to_not have_been_sent.matching_selector('h1')
|
168
|
+
end.to raise_error.with_message(
|
169
|
+
"Expected no emails to be sent matching selector 'h1'."
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "then a positive 'with_link' assertion passes" do
|
174
|
+
expect do
|
175
|
+
expect(subject).to have_been_sent.with_link('www.test.com')
|
176
|
+
end.to_not raise_error
|
177
|
+
end
|
178
|
+
|
179
|
+
it "then a negative 'with_link' assertion fails" do
|
180
|
+
expect do
|
181
|
+
expect(subject).to_not have_been_sent.with_link('www.test.com')
|
182
|
+
end.to raise_error.with_message(
|
183
|
+
"Expected no emails to be sent with link 'www.test.com'."
|
184
|
+
)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "then a positive 'with_image' assertion passes" do
|
188
|
+
expect do
|
189
|
+
expect(subject).to have_been_sent.with_image('www.test.com')
|
190
|
+
end.to_not raise_error
|
191
|
+
end
|
192
|
+
|
193
|
+
it "then a negative 'with_image' assertion fails" do
|
194
|
+
expect do
|
195
|
+
expect(subject).to_not have_been_sent.with_image('www.test.com')
|
196
|
+
end.to raise_error.with_message(
|
197
|
+
"Expected no emails to be sent with image 'www.test.com'."
|
198
|
+
)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'when a non-matching email has been sent' do
|
203
|
+
subject { [EmailMock.new] }
|
204
|
+
|
205
|
+
it "then a positive 'to' assertion fails" do
|
206
|
+
expect do
|
207
|
+
expect(subject).to have_been_sent.to('other@email.com')
|
208
|
+
end.to raise_error.with_message(
|
209
|
+
"Expected an email to be sent to 'other@email.com'. However, 1 was sent to '#{subject[0].to[0]}'."
|
210
|
+
)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "then a negative 'to' assertion passes" do
|
214
|
+
expect do
|
215
|
+
expect(subject).to_not have_been_sent.to('other@email.com')
|
216
|
+
end.to_not raise_error
|
217
|
+
end
|
218
|
+
|
219
|
+
it "then a positive 'from' assertion fails" do
|
220
|
+
expect do
|
221
|
+
expect(subject).to have_been_sent.from('other@email.com')
|
222
|
+
end.to raise_error.with_message(
|
223
|
+
"Expected an email to be sent from 'other@email.com'. However, 1 was sent from '#{subject[0].from[0]}'."
|
224
|
+
)
|
225
|
+
end
|
226
|
+
|
227
|
+
it "then a negative 'from' assertion passes" do
|
228
|
+
expect do
|
229
|
+
expect(subject).to_not have_been_sent.from('other@email.com')
|
230
|
+
end.to_not raise_error
|
231
|
+
end
|
232
|
+
|
233
|
+
it "then a positive 'with_subject' assertion fails" do
|
234
|
+
expect do
|
235
|
+
expect(subject).to have_been_sent.with_subject('Other Subject')
|
236
|
+
end.to raise_error.with_message(
|
237
|
+
"Expected an email to be sent with subject 'Other Subject'. However, 1 was " \
|
238
|
+
"sent with subject '#{subject[0].subject}'."
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "then a negative 'with_subject' assertion passes" do
|
243
|
+
expect do
|
244
|
+
expect(subject).to_not have_been_sent.with_subject('Other Subject')
|
245
|
+
end.to_not raise_error
|
246
|
+
end
|
247
|
+
|
248
|
+
it "then a positive 'with_text' assertion fails" do
|
249
|
+
expect do
|
250
|
+
expect(subject).to have_been_sent.with_text('Other text')
|
251
|
+
end.to raise_error.with_message(
|
252
|
+
"Expected an email to be sent with text 'Other text'. However, 1 was sent with text '#{subject[0].text}'."
|
253
|
+
)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "then a negative 'with_text' assertion passes" do
|
257
|
+
expect do
|
258
|
+
expect(subject).to_not have_been_sent.with_text('Other text')
|
259
|
+
end.to_not raise_error
|
260
|
+
end
|
261
|
+
|
262
|
+
it "then a positive 'matching_selector' assertion fails" do
|
263
|
+
expect do
|
264
|
+
expect(subject).to have_been_sent.matching_selector('.other')
|
265
|
+
end.to raise_error.with_message(
|
266
|
+
"Expected an email to be sent matching selector '.other'. However, 1 was sent with body #{subject[0].body}."
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
it "then a negative 'matching_selector' assertion passes" do
|
271
|
+
expect do
|
272
|
+
expect(subject).to_not have_been_sent.matching_selector('.other')
|
273
|
+
end.to_not raise_error
|
274
|
+
end
|
275
|
+
|
276
|
+
it "then a positive 'with_link' assertion fails" do
|
277
|
+
expect do
|
278
|
+
expect(subject).to have_been_sent.with_link('www.other.com')
|
279
|
+
end.to raise_error.with_message(
|
280
|
+
"Expected an email to be sent with link 'www.other.com'. However, 1 was sent with body #{subject[0].body}."
|
281
|
+
)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "then a negative 'with_link' assertion passes" do
|
285
|
+
expect do
|
286
|
+
expect(subject).to_not have_been_sent.with_link('www.other.com')
|
287
|
+
end.to_not raise_error
|
288
|
+
end
|
289
|
+
|
290
|
+
it "then a positive 'with_image' assertion fails" do
|
291
|
+
expect do
|
292
|
+
expect(subject).to have_been_sent.with_image('www.other.com')
|
293
|
+
end.to raise_error.with_message(
|
294
|
+
"Expected an email to be sent with image 'www.other.com'. However, 1 was sent with body #{subject[0].body}."
|
295
|
+
)
|
296
|
+
end
|
297
|
+
|
298
|
+
it "then a negative 'with_image' assertion passes" do
|
299
|
+
expect do
|
300
|
+
expect(subject).to_not have_been_sent.with_image('www.other.com')
|
301
|
+
end.to_not raise_error
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'when multiple emails have been sent' do
|
306
|
+
subject { [EmailMock.new, EmailMock.new(to: 'other@email.com')] }
|
307
|
+
|
308
|
+
it 'then a positive assertion matching the first email passes' do
|
309
|
+
expect do
|
310
|
+
expect(subject).to have_been_sent.to(subject[0].to[0])
|
311
|
+
end.to_not raise_error
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'then a negative assertion matching the first email fails' do
|
315
|
+
expect do
|
316
|
+
expect(subject).to_not have_been_sent.to(subject[0].to[0])
|
317
|
+
end.to raise_error.with_message(
|
318
|
+
"Expected no emails to be sent to '#{subject[0].to[0]}'."
|
319
|
+
)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'then a positive assertion matching the second email passes' do
|
323
|
+
expect do
|
324
|
+
expect(subject).to have_been_sent.to(subject[1].to)
|
325
|
+
end.to_not raise_error
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'then a negative assertion matching the second email fails' do
|
329
|
+
expect do
|
330
|
+
expect(subject).to_not have_been_sent.to(subject[1].to)
|
331
|
+
end.to raise_error.with_message(
|
332
|
+
"Expected no emails to be sent to '#{subject[1].to[0]}'."
|
333
|
+
)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'when using multiple qualifiers' do
|
338
|
+
subject { [EmailMock.new] }
|
339
|
+
|
340
|
+
it 'then a positive assertions correctly matches a matching email' do
|
341
|
+
expect do
|
342
|
+
expect(subject).to have_been_sent.to(subject[0].to[0]).from(subject[0].from[0])
|
343
|
+
end.to_not raise_error
|
344
|
+
end
|
345
|
+
|
346
|
+
it "then a positive assertions don't match an email if the first qualifier isn't satisfied" do
|
347
|
+
expect do
|
348
|
+
expect(subject).to have_been_sent.to('other@email.com').from(subject[0].from[0])
|
349
|
+
end.to raise_error.with_message(
|
350
|
+
"Expected an email to be sent to 'other@email.com'. However, 1 was sent to '#{subject[0].to[0]}'."
|
351
|
+
)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "then a positive assertions don't match an email if the last qualifier isn't satisfied" do
|
355
|
+
expect do
|
356
|
+
expect(subject).to have_been_sent.to(subject[0].to[0]).from('other@email.com')
|
357
|
+
end.to raise_error.with_message(
|
358
|
+
"Expected an email to be sent from 'other@email.com'. However, 1 was sent from '#{subject[0].from[0]}'."
|
359
|
+
)
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'then a negative assertions correctly matches a matching email' do
|
363
|
+
expect do
|
364
|
+
expect(subject).to_not have_been_sent.to(subject[0].to[0]).from(subject[0].from[0])
|
365
|
+
end.to raise_error.with_message(
|
366
|
+
"Expected no emails to be sent to '#{subject[0].to[0]}' from '#{subject[0].from[0]}'."
|
367
|
+
)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "then a negative assertions don't match an email if the first qualifier isn't satisfied" do
|
371
|
+
expect do
|
372
|
+
expect(subject).to_not have_been_sent.to('other@email.com').from(subject[0].from[0])
|
373
|
+
end.to_not raise_error
|
374
|
+
end
|
375
|
+
|
376
|
+
it "then a negative assertions don't match an email if the last qualifier isn't satisfied" do
|
377
|
+
expect do
|
378
|
+
expect(subject).to_not have_been_sent.to(subject[0].to[0]).from('other@email.com')
|
379
|
+
end.to_not raise_error
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'when using the and method' do
|
384
|
+
subject { [EmailMock.new] }
|
385
|
+
|
386
|
+
it 'then a positive assertion will fail if the first qualifier is not satisfied' do
|
387
|
+
expect do
|
388
|
+
expect(subject).to have_been_sent.with_text('Other').and('Email')
|
389
|
+
end.to raise_error.with_message(
|
390
|
+
"Expected an email to be sent with text 'Other' and 'Email'. However, 1 was " \
|
391
|
+
"sent with text '#{subject[0].text}'."
|
392
|
+
)
|
393
|
+
end
|
394
|
+
|
395
|
+
it 'then a positive assertion will fail if the second qualifier is not satisfied' do
|
396
|
+
expect do
|
397
|
+
expect(subject).to have_been_sent.with_text('Test').and('Other')
|
398
|
+
end.to raise_error.with_message(
|
399
|
+
"Expected an email to be sent with text 'Test' and 'Other'. However, 1 was sent with text '#{subject[0].text}'."
|
400
|
+
)
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'then a positive assertion will pass if both qualifiers are satisfied' do
|
404
|
+
expect do
|
405
|
+
expect(subject).to have_been_sent.with_text('Test').and('Email')
|
406
|
+
end.to_not raise_error
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'then a negative assertion will pass if the first qualifier is not satisfied' do
|
410
|
+
expect do
|
411
|
+
expect(subject).to_not have_been_sent.with_text('Other').and('Email')
|
412
|
+
end.to_not raise_error
|
413
|
+
end
|
414
|
+
|
415
|
+
it 'then a negative assertion will pass if the second qualifier is not satisfied' do
|
416
|
+
expect do
|
417
|
+
expect(subject).to_not have_been_sent.with_text('Test').and('Other')
|
418
|
+
end.to_not raise_error
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'then a negative assertion will fail if both qualifiers are satisfied' do
|
422
|
+
expect do
|
423
|
+
expect(subject).to_not have_been_sent.with_text('Test').and('Email')
|
424
|
+
end.to raise_error.with_message(
|
425
|
+
'Expected no emails to be sent with text \'Test\' and \'Email\'.'
|
426
|
+
)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|