email_spectacular 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|