test_assistant 0.1.2 → 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.
@@ -1,37 +0,0 @@
1
- require 'test_assistant/email/expectation'
2
-
3
- module TestAssistant::Email
4
- # Module containing email helper methods that can be mixed into RSpec the test scope
5
- #
6
- # @see TestAssistant::Configuration#include_email_helpers
7
- module Helpers
8
- # Syntactic sugar for referencing the list of emails sent since the start of the test
9
- #
10
- # @return [Array<Mail::Message>] list of sent emails
11
- #
12
- # @example Asserting email has been sent
13
- # expect(email).to have_been_sent.to('test@email.com')
14
- def email
15
- ActionMailer::Base.deliveries
16
- end
17
-
18
- # Clears the list of sent emails. Automatically called by Test Assistant at the
19
- # end of every test.
20
- #
21
- # @return void
22
- def clear_emails
23
- ActionMailer::Base.deliveries = []
24
- end
25
-
26
- # Creates a new email expectation that allows asserting emails should have specific
27
- # attributes.
28
- #
29
- # @see TestAssistant::Email::Expectation
30
- #
31
- # @example Asserting email has been sent
32
- # expect(email).to have_been_sent.to('test@email.com')
33
- def have_been_sent
34
- TestAssistant::Email::Expectation.new
35
- end
36
- end
37
- end
@@ -1,189 +0,0 @@
1
- require 'capybara/rspec'
2
- require 'hashdiff'
3
-
4
- module TestAssistant::Json
5
- # Backing class for the eql_json RSpec matcher. Used for matching Ruby representations
6
- # of JSON response bodies. Provides clear diff representations for simple and complex
7
- # or nested JSON objects, highlighting only the values that are different, and where
8
- # they are in the larger JSON object.
9
- #
10
- # Expected to be used as part of a RSpec test suite and with json_response.
11
- #
12
- # Implements the same interface as RSpec custom matcher classes
13
- #
14
- # @see TestAssistant::Json::Helpers#json_response
15
- # @see TestAssistant::Json::Helpers#eql_json
16
- class Expectation
17
- # Creates a new TestAssistant::Json::Expectation object.
18
- #
19
- # @see TestAssistant::Json::Helpers#eql_json
20
- #
21
- # @param expected the expected value that will be compared with the actual value
22
- # @return [TestAssistant::Json::Expectation] new expectation object
23
- def initialize(expected)
24
- @expected = expected
25
- @message = ''
26
- @reported_differences = {}
27
- end
28
-
29
- # Declares that RSpec should not attempt to diff the actual and expected values
30
- # to put in the failure message. This class takes care of diffing and presenting
31
- # the differences, itself.
32
- # @return [false] always returns false
33
- def diffable?
34
- false
35
- end
36
-
37
- # Whether the actual value and the expected value are considered equal.
38
- # @param actual value to be compared to the expected value for equality
39
- # @return [Boolean] whether actual is equal to expected
40
- def matches?(actual)
41
- @actual = actual
42
- @expected.eql?(@actual)
43
- end
44
-
45
- # Message to display to StdOut by RSpec if the equality check fails. Includes a
46
- # complete serialisation of the expected and actual values and is then followed
47
- # by a description of only the (possibly deeply nested) attributes that are
48
- # different
49
- # @return [String] message full failure message with explanation of why actual
50
- # failed the equality check with expected
51
- def failure_message
52
- @message += "Expected: #{@expected}\n\n"
53
- @message += "Actual: #{@actual}\n\n"
54
- @message += "Differences\n\n"
55
-
56
- add_diff_to_message(@actual, @expected)
57
-
58
- @message
59
- end
60
-
61
- private
62
-
63
- # Adds diff descriptions to the failure message until the all the nodes of the
64
- # expected and actual values have been compared and all the differences (and the
65
- # paths to them) have been included. For Hashes and Arrays, it recursively calls
66
- # itself to compare all nodes and elements.
67
- #
68
- # @param actual_value current node of the actual value being compared to the
69
- # corresponding node of the expected value
70
- # @param expected_value current node of the expected value being compared to
71
- # the corresponding node of the actual value
72
- # @param [String] path path to the current nodes being compared,
73
- # relative to the root full objects
74
- # @return void Diff descriptions are appended directly to message
75
- def add_diff_to_message(actual_value, expected_value, path = '')
76
- diffs_sorted_by_name = HashDiff
77
- .diff(actual_value, expected_value)
78
- .sort{|diff1, diff2| diff1[1] <=> diff2[1]}
79
-
80
- diffs_grouped_by_name =
81
- diffs_sorted_by_name.inject({}) do |memo, diff|
82
- operator, name, value = diff
83
- memo[name] ||= {}
84
- memo[name][operator] = value
85
- memo
86
- end
87
-
88
- diffs_grouped_by_name.each do |name, difference|
89
-
90
- missing_value = difference['-'] || value_at_path(actual_value, name)
91
- extra_value = difference['+'] || value_at_path(expected_value, name)
92
- different_value = difference['~']
93
-
94
- full_path = path.length > 0 ? "#{path}.#{name}" : name
95
-
96
- if non_empty_hash?(missing_value) && non_empty_hash?(extra_value)
97
-
98
- add_diff_to_message(missing_value, extra_value, full_path)
99
-
100
- elsif non_empty_array?(missing_value) && non_empty_array?(extra_value)
101
-
102
- [ missing_value.length, extra_value.length ].max.times do |i|
103
- add_diff_to_message(missing_value[i], extra_value[i], full_path)
104
- end
105
-
106
- else
107
- if difference.has_key?('~')
108
- append_to_message(full_path,
109
- get_diff(
110
- full_path,
111
- expected: value_at_path(expected_value, name),
112
- actual: different_value
113
- )
114
- )
115
- else
116
- append_to_message(full_path,
117
- get_diff(
118
- full_path,
119
- expected: extra_value,
120
- actual: missing_value
121
- )
122
- )
123
- end
124
- end
125
-
126
- end
127
- end
128
-
129
- def non_empty_hash?(target)
130
- target.kind_of?(Hash) && target.any?
131
- end
132
-
133
- def non_empty_array?(target)
134
- target.kind_of?(Array) && target.any?
135
- end
136
-
137
- def append_to_message(attribute, difference_description)
138
- unless already_reported_difference?(attribute)
139
- @message += difference_description
140
- @reported_differences[attribute] = true
141
- end
142
- end
143
-
144
- def already_reported_difference?(attribute)
145
- !!@reported_differences[attribute]
146
- end
147
-
148
- def value_at_path(target, attribute_path)
149
- keys = attribute_path.split(/\[|\]|\./)
150
-
151
- keys = keys.map do |key|
152
- if key.to_i == 0 && key != '0'
153
- key
154
- else
155
- key.to_i
156
- end
157
- end
158
-
159
- result = target
160
-
161
-
162
- keys.each do |key|
163
-
164
- unless key == ''
165
- result = result[key]
166
- end
167
-
168
- end
169
-
170
- result
171
- end
172
-
173
- def get_diff(attribute, options = {})
174
- diff_description = ''
175
- diff_description += "#{attribute}\n"
176
- diff_description += "Expected: #{format_value(options[:expected])}\n"
177
- diff_description + "Actual: #{format_value(options[:actual])}\n\n"
178
- end
179
-
180
- def format_value(value)
181
- if value.kind_of?(String)
182
- "'#{value}'"
183
- else
184
- value
185
- end
186
- end
187
-
188
- end
189
- end
@@ -1,38 +0,0 @@
1
- require 'test_assistant/json/expectation'
2
-
3
- module TestAssistant::Json
4
- # Module containing JSON helper methods that can be mixed into RSpec the test scope
5
- #
6
- # @see TestAssistant::Configuration#include_json_helpers
7
- module Helpers
8
- # Parses the last response body in a Rails RSpec controller or request test as JSON
9
- #
10
- # @return [Hash{String => String, Number, Hash, Array}] Ruby representation of
11
- # the JSON response body
12
- # @raise []JSON::ParserError] when the response body is not valid JSON
13
- def json_response
14
- begin
15
- JSON.parse(response.body)
16
- rescue JSON::ParserError
17
- '< INVALID JSON RESPONSE >'.freeze
18
- end
19
- end
20
-
21
- # Creates a new TestAssistant::Json::Expectation instance so it can be passed
22
- # to RSpec to match against an actual value.
23
- #
24
- # @see TestAssistant::Expectation
25
- #
26
- # @param expected the expected value the RSpec matcher should match against
27
- # @return [TestAssistant::Json::Expectation] new expectation object
28
- #
29
- # @example Use the eql_json expectation
30
- # expect(actual).to eql_json(expected)
31
- #
32
- # @example Use the eql_json expectation with json_response
33
- # expect(json_response).to eql_json(expected)
34
- def eql_json(expected)
35
- TestAssistant::Json::Expectation.new(expected)
36
- end
37
- end
38
- end
@@ -1,369 +0,0 @@
1
- require 'test_assistant/email/helpers'
2
- require_relative './support/email_mock'
3
-
4
- RSpec.describe 'have_sent_email' do
5
- include TestAssistant::Email::Helpers
6
-
7
- context "when no emails have been sent" do
8
- subject { [] }
9
-
10
- it "then the positive assertion fails" do
11
- expect {
12
- expect(subject).to have_been_sent
13
- }.to raise_error.with_message('Expected an email to be sent. However, no emails were sent.')
14
- end
15
-
16
- it "then the negative assertion passes" do
17
- expect {
18
- expect(subject).to_not have_been_sent
19
- }.to_not raise_error
20
- end
21
-
22
- it "then a non-matching 'to' assertion fails" do
23
- expect {
24
- expect(subject).to have_been_sent.to('test@email.com')
25
- }.to raise_error.with_message('Expected an email to be sent to \'test@email.com\'. However, no emails were sent.')
26
- end
27
-
28
- it "then a non-matching 'from' assertion fails" do
29
- expect {
30
- expect(subject).to have_been_sent.from('test@email.com')
31
- }.to raise_error.with_message('Expected an email to be sent from \'test@email.com\'. However, no emails were sent.')
32
- end
33
-
34
- it "then a non-matching 'with_subject' assertion fails" do
35
- expect {
36
- expect(subject).to have_been_sent.with_subject('Subject')
37
- }.to raise_error.with_message('Expected an email to be sent with subject \'Subject\'. However, no emails were sent.')
38
- end
39
-
40
- it "then a non-matching 'with_text' assertion fails" do
41
- expect {
42
- expect(subject).to have_been_sent.with_text('Text')
43
- }.to raise_error.with_message('Expected an email to be sent with text \'Text\'. However, no emails were sent.')
44
- end
45
-
46
- it "then a non-matching 'matching_selector' assertion fails" do
47
- expect {
48
- expect(subject).to have_been_sent.matching_selector('h1')
49
- }.to raise_error.with_message('Expected an email to be sent matching selector \'h1\'. However, no emails were sent.')
50
- end
51
-
52
- it "then a non-matching 'with_link' assertion fails" do
53
- expect {
54
- expect(subject).to have_been_sent.with_link('www.example.com')
55
- }.to raise_error.with_message('Expected an email to be sent with link \'www.example.com\'. However, no emails were sent.')
56
- end
57
-
58
- it "then a non-matching 'with_image' assertion fails" do
59
- expect {
60
- expect(subject).to have_been_sent.with_image('www.example.com')
61
- }.to raise_error.with_message('Expected an email to be sent with image \'www.example.com\'. However, no emails were sent.')
62
- end
63
-
64
- end
65
-
66
- context "when an email has been sent" do
67
- subject { [ EmailMock.new ] }
68
-
69
- it "then the unqualified assertion passes" do
70
- expect {
71
- expect(subject).to have_been_sent
72
- }.to_not raise_error
73
- end
74
-
75
- it "then the unqualified negative assertion fails" do
76
- expect {
77
- expect(subject).to_not have_been_sent
78
- }.to raise_error("Expected no emails to be sent.")
79
- end
80
- end
81
-
82
- context "when a matching email has been sent" do
83
- subject { [ EmailMock.new ] }
84
-
85
- it "then a positive 'to' assertion passes" do
86
- expect {
87
- expect(subject).to have_been_sent.to(subject[0].to[0])
88
- }.to_not raise_error
89
- end
90
-
91
- it "then a negative 'to' assertion fails" do
92
- expect {
93
- expect(subject).to_not have_been_sent.to(subject[0].to[0])
94
- }.to raise_error.with_message("Expected no emails to be sent to '#{subject[0].to[0]}'.")
95
- end
96
-
97
- it "then a positive 'from' assertion passes" do
98
- expect {
99
- expect(subject).to have_been_sent.from(subject[0].from[0])
100
- }.to_not raise_error
101
- end
102
-
103
- it "then a negative 'from' assertion fails" do
104
- expect {
105
- expect(subject).to_not have_been_sent.from(subject[0].from[0])
106
- }.to raise_error.with_message("Expected no emails to be sent from '#{subject[0].from[0]}'.")
107
- end
108
-
109
- it "then a positive 'with_subject' assertion passes" do
110
- expect {
111
- expect(subject).to have_been_sent.with_subject(subject[0].subject)
112
- }.to_not raise_error
113
- end
114
-
115
- it "then a negative 'with_subject' assertion fails" do
116
- expect {
117
- expect(subject).to_not have_been_sent.with_subject(subject[0].subject)
118
- }.to raise_error.with_message("Expected no emails to be sent with subject '#{subject[0].subject}'.")
119
- end
120
-
121
- it "then a positive 'with_text' assertion passes" do
122
- expect {
123
- expect(subject).to have_been_sent.with_text(subject[0].text)
124
- }.to_not raise_error
125
- end
126
-
127
- it "then a negative 'with_text' assertion fails" do
128
- expect {
129
- expect(subject).to_not have_been_sent.with_text(subject[0].text)
130
- }.to raise_error.with_message("Expected no emails to be sent with text '#{subject[0].text}'.")
131
- end
132
-
133
- it "then a positive 'matching_selector' assertion passes" do
134
- expect {
135
- expect(subject).to have_been_sent.matching_selector('h1')
136
- }.to_not raise_error
137
- end
138
-
139
- it "then a negative 'matching_selector' assertion fails" do
140
- expect {
141
- expect(subject).to_not have_been_sent.matching_selector('h1')
142
- }.to raise_error.with_message("Expected no emails to be sent matching selector 'h1'.")
143
- end
144
-
145
- it "then a positive 'with_link' assertion passes" do
146
- expect {
147
- expect(subject).to have_been_sent.with_link('www.test.com')
148
- }.to_not raise_error
149
- end
150
-
151
- it "then a negative 'with_link' assertion fails" do
152
- expect {
153
- expect(subject).to_not have_been_sent.with_link('www.test.com')
154
- }.to raise_error.with_message("Expected no emails to be sent with link 'www.test.com'.")
155
- end
156
-
157
- it "then a positive 'with_image' assertion passes" do
158
- expect {
159
- expect(subject).to have_been_sent.with_image('www.test.com')
160
- }.to_not raise_error
161
- end
162
-
163
- it "then a negative 'with_image' assertion fails" do
164
- expect {
165
- expect(subject).to_not have_been_sent.with_image('www.test.com')
166
- }.to raise_error.with_message("Expected no emails to be sent with image 'www.test.com'.")
167
- end
168
- end
169
-
170
- context "when a non-matching email has been sent" do
171
- subject { [ EmailMock.new ] }
172
-
173
- it "then a positive 'to' assertion fails" do
174
- expect {
175
- expect(subject).to have_been_sent.to('other@email.com')
176
- }.to raise_error.with_message("Expected an email to be sent to 'other@email.com'. However, 1 was sent to '#{subject[0].to[0]}'.")
177
- end
178
-
179
- it "then a negative 'to' assertion passes" do
180
- expect {
181
- expect(subject).to_not have_been_sent.to('other@email.com')
182
- }.to_not raise_error
183
- end
184
-
185
- it "then a positive 'from' assertion fails" do
186
- expect {
187
- expect(subject).to have_been_sent.from('other@email.com')
188
- }.to raise_error.with_message("Expected an email to be sent from 'other@email.com'. However, 1 was sent from '#{subject[0].from[0]}'.")
189
- end
190
-
191
- it "then a negative 'from' assertion passes" do
192
- expect {
193
- expect(subject).to_not have_been_sent.from('other@email.com')
194
- }.to_not raise_error
195
- end
196
-
197
- it "then a positive 'with_subject' assertion fails" do
198
- expect {
199
- expect(subject).to have_been_sent.with_subject('Other Subject')
200
- }.to raise_error.with_message("Expected an email to be sent with subject 'Other Subject'. However, 1 was sent with subject '#{subject[0].subject}'.")
201
- end
202
-
203
- it "then a negative 'with_subject' assertion passes" do
204
- expect {
205
- expect(subject).to_not have_been_sent.with_subject('Other Subject')
206
- }.to_not raise_error
207
- end
208
-
209
- it "then a positive 'with_text' assertion fails" do
210
- expect {
211
- expect(subject).to have_been_sent.with_text('Other text')
212
- }.to raise_error.with_message("Expected an email to be sent with text 'Other text'. However, 1 was sent with text '#{subject[0].text}'.")
213
- end
214
-
215
- it "then a negative 'with_text' assertion passes" do
216
- expect {
217
- expect(subject).to_not have_been_sent.with_text('Other text')
218
- }.to_not raise_error
219
- end
220
-
221
- it "then a positive 'matching_selector' assertion fails" do
222
- expect {
223
- expect(subject).to have_been_sent.matching_selector('.other')
224
- }.to raise_error.with_message("Expected an email to be sent matching selector '.other'. However, 1 was sent with body #{subject[0].body}.")
225
- end
226
-
227
- it "then a negative 'matching_selector' assertion passes" do
228
- expect {
229
- expect(subject).to_not have_been_sent.matching_selector('.other')
230
- }.to_not raise_error
231
- end
232
-
233
- it "then a positive 'with_link' assertion fails"do
234
- expect {
235
- expect(subject).to have_been_sent.with_link('www.other.com')
236
- }.to raise_error.with_message("Expected an email to be sent with link 'www.other.com'. However, 1 was sent with body #{subject[0].body}.")
237
- end
238
-
239
- it "then a negative 'with_link' assertion passes" do
240
- expect {
241
- expect(subject).to_not have_been_sent.with_link('www.other.com')
242
- }.to_not raise_error
243
- end
244
-
245
- it "then a positive 'with_image' assertion fails" do
246
- expect {
247
- expect(subject).to have_been_sent.with_image('www.other.com')
248
- }.to raise_error.with_message("Expected an email to be sent with image 'www.other.com'. However, 1 was sent with body #{subject[0].body}.")
249
- end
250
-
251
- it "then a negative 'with_image' assertion passes" do
252
- expect {
253
- expect(subject).to_not have_been_sent.with_image('www.other.com')
254
- }.to_not raise_error
255
- end
256
- end
257
-
258
- context "when multiple emails have been sent" do
259
- subject { [ EmailMock.new, EmailMock.new(to: 'other@email.com') ] }
260
-
261
- it "then a positive assertion matching the first email passes" do
262
- expect {
263
- expect(subject).to have_been_sent.to(subject[0].to[0])
264
- }.to_not raise_error
265
- end
266
-
267
- it "then a negative assertion matching the first email fails" do
268
- expect {
269
- expect(subject).to_not have_been_sent.to(subject[0].to[0])
270
- }.to raise_error.with_message("Expected no emails to be sent to '#{subject[0].to[0]}'.")
271
- end
272
-
273
- it "then a positive assertion matching the second email passes" do
274
- expect {
275
- expect(subject).to have_been_sent.to(subject[1].to)
276
- }.to_not raise_error
277
- end
278
-
279
- it "then a negative assertion matching the second email fails" do
280
- expect {
281
- expect(subject).to_not have_been_sent.to(subject[1].to)
282
- }.to raise_error.with_message("Expected no emails to be sent to '#{subject[1].to[0]}'.")
283
- end
284
-
285
- end
286
-
287
- context "when using multiple qualifiers" do
288
- subject { [ EmailMock.new ] }
289
-
290
- it "then a positive assertions correctly matches a matching email" do
291
- expect {
292
- expect(subject).to have_been_sent.to(subject[0].to[0]).from(subject[0].from[0])
293
- }.to_not raise_error
294
- end
295
-
296
- it "then a positive assertions don't match an email if the first qualifier isn't satisfied" do
297
- expect {
298
- expect(subject).to have_been_sent.to('other@email.com').from(subject[0].from[0])
299
- }.to raise_error.with_message("Expected an email to be sent to 'other@email.com'. However, 1 was sent to '#{subject[0].to[0]}'.")
300
- end
301
-
302
- it "then a positive assertions don't match an email if the last qualifier isn't satisfied" do
303
- expect {
304
- expect(subject).to have_been_sent.to(subject[0].to[0]).from('other@email.com')
305
- }.to raise_error.with_message("Expected an email to be sent from 'other@email.com'. However, 1 was sent from '#{subject[0].from[0]}'.")
306
- end
307
-
308
- it "then a negative assertions correctly matches a matching email" do
309
- expect {
310
- expect(subject).to_not have_been_sent.to(subject[0].to[0]).from(subject[0].from[0])
311
- }.to raise_error.with_message("Expected no emails to be sent to '#{subject[0].to[0]}' from '#{subject[0].from[0]}'.")
312
- end
313
-
314
- it "then a negative assertions don't match an email if the first qualifier isn't satisfied" do
315
- expect {
316
- expect(subject).to_not have_been_sent.to('other@email.com').from(subject[0].from[0])
317
- }.to_not raise_error
318
- end
319
-
320
- it "then a negative assertions don't match an email if the last qualifier isn't satisfied" do
321
- expect {
322
- expect(subject).to_not have_been_sent.to(subject[0].to[0]).from('other@email.com')
323
- }.to_not raise_error
324
- end
325
- end
326
-
327
- context "when using the and method" do
328
- subject { [ EmailMock.new ] }
329
-
330
- it "then a positive assertion will fail if the first qualifier is not satisfied" do
331
- expect {
332
- expect(subject).to have_been_sent.with_text('Other').and('Email')
333
- }.to raise_error.with_message("Expected an email to be sent with text 'Other' and 'Email'. However, 1 was sent with text '#{subject[0].text}'.")
334
- end
335
-
336
- it "then a positive assertion will fail if the second qualifier is not satisfied" do
337
- expect {
338
- expect(subject).to have_been_sent.with_text('Test').and('Other')
339
- }.to raise_error.with_message("Expected an email to be sent with text 'Test' and 'Other'. However, 1 was sent with text '#{subject[0].text}'.")
340
- end
341
-
342
- it "then a positive assertion will pass if both qualifiers are satisfied" do
343
- expect {
344
- expect(subject).to have_been_sent.with_text('Test').and('Email')
345
- }.to_not raise_error
346
- end
347
-
348
- it "then a negative assertion will pass if the first qualifier is not satisfied" do
349
- expect {
350
- expect(subject).to_not have_been_sent.with_text('Other').and('Email')
351
- }.to_not raise_error
352
- end
353
-
354
- it "then a negative assertion will pass if the second qualifier is not satisfied" do
355
- expect {
356
- expect(subject).to_not have_been_sent.with_text('Test').and('Other')
357
- }.to_not raise_error
358
- end
359
-
360
- it "then a negative assertion will fail if both qualifiers are satisfied" do
361
- expect {
362
- expect(subject).to_not have_been_sent.with_text('Test').and('Email')
363
- }.to raise_error.with_message('Expected no emails to be sent with text \'Test\' and \'Email\'.')
364
- end
365
-
366
- end
367
-
368
-
369
- end