test_assistant 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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