smartest 0.2.0.alpha3 → 0.2.0.alpha4
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 +4 -4
- data/README.md +30 -0
- data/SMARTEST_DESIGN.md +7 -0
- data/lib/smartest/matchers.rb +275 -9
- data/lib/smartest/version.rb +1 -1
- data/smartest/smartest_test.rb +185 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 944d35bc0a1be2293c8e276d16436693ff7dd6a2d37218f9f65af3b7a47f90f5
|
|
4
|
+
data.tar.gz: edd55fb6442a92e74146eaffc76a3b881463a16b4ce3c72a877734e0f6e36f52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5406cfcb57b56c71140c98565da884a184b8d14dcd41f8d6f65f218010d5ef7672eee6c3ad874dcc22a2d415097c9f9926f3a22c31ea3f5dafcf6dd00c2c795d
|
|
7
|
+
data.tar.gz: '0666928711e5ec22a5f9d671714597eb9e0a7e98edcf11d334bf72301c42739a8ae71104dff5784fa4728c10c186188e9f5278137ebbf7d22f6e14e745964927'
|
data/README.md
CHANGED
|
@@ -179,6 +179,8 @@ Smartest uses an expectation style:
|
|
|
179
179
|
expect(actual).to eq(expected)
|
|
180
180
|
expect(actual).not_to eq(expected)
|
|
181
181
|
expect { action }.to raise_error(ErrorClass)
|
|
182
|
+
expect { action }.to raise_error(/message/)
|
|
183
|
+
expect { action }.to raise_error(ErrorClass, /message/)
|
|
182
184
|
expect { action }.to change { value }
|
|
183
185
|
```
|
|
184
186
|
|
|
@@ -200,6 +202,18 @@ end
|
|
|
200
202
|
test("download") do
|
|
201
203
|
expect("screenshot.png").to end_with(".png")
|
|
202
204
|
end
|
|
205
|
+
|
|
206
|
+
test("type") do
|
|
207
|
+
expect("smartest").to be_a(String)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
test("URL pattern") do
|
|
211
|
+
expect("https://example.test").to match(%r{\Ahttps://})
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
test("events") do
|
|
215
|
+
expect(%i[request close open]).to contain_exactly(:open, :request, :close)
|
|
216
|
+
end
|
|
203
217
|
```
|
|
204
218
|
|
|
205
219
|
Supported matchers include:
|
|
@@ -209,13 +223,29 @@ eq(expected)
|
|
|
209
223
|
include(expected)
|
|
210
224
|
start_with(prefix, ...)
|
|
211
225
|
end_with(suffix, ...)
|
|
226
|
+
be_a(ClassOrModule)
|
|
227
|
+
be_an(ClassOrModule)
|
|
212
228
|
be_nil
|
|
229
|
+
match(regexp)
|
|
230
|
+
contain_exactly(item, ...)
|
|
231
|
+
match_array(items)
|
|
213
232
|
raise_error(ErrorClass)
|
|
233
|
+
raise_error(/message/)
|
|
234
|
+
raise_error(ErrorClass, /message/)
|
|
214
235
|
change { value }
|
|
215
236
|
change { value }.from(before).to(after)
|
|
216
237
|
change { value }.by(delta)
|
|
217
238
|
```
|
|
218
239
|
|
|
240
|
+
`raise_error` accepts an error class, a message regexp, or both. Use an error
|
|
241
|
+
class to check the raised exception class, a regexp to check the raised
|
|
242
|
+
exception message, or both to require both conditions. No-argument and exact
|
|
243
|
+
string message forms are not supported.
|
|
244
|
+
|
|
245
|
+
`contain_exactly` and `match_array` compare collections without requiring a
|
|
246
|
+
specific order, preserve duplicate counts, and can use matcher objects such as
|
|
247
|
+
`match(/foo/)` or `eq(42)` as expected items.
|
|
248
|
+
|
|
219
249
|
`change` is only supported with `expect { ... }` block expectations and must be
|
|
220
250
|
written with a value block.
|
|
221
251
|
|
data/SMARTEST_DESIGN.md
CHANGED
|
@@ -727,8 +727,15 @@ eq(expected)
|
|
|
727
727
|
include(expected)
|
|
728
728
|
start_with(prefix, ...)
|
|
729
729
|
end_with(suffix, ...)
|
|
730
|
+
be_a(ClassOrModule)
|
|
731
|
+
be_an(ClassOrModule)
|
|
730
732
|
be_nil
|
|
733
|
+
match(regexp)
|
|
734
|
+
contain_exactly(item, ...)
|
|
735
|
+
match_array(items)
|
|
731
736
|
raise_error(ErrorClass)
|
|
737
|
+
raise_error(/message/)
|
|
738
|
+
raise_error(ErrorClass, /message/)
|
|
732
739
|
change { value }
|
|
733
740
|
```
|
|
734
741
|
|
data/lib/smartest/matchers.rb
CHANGED
|
@@ -18,12 +18,32 @@ module Smartest
|
|
|
18
18
|
EndWithMatcher.new(*suffixes)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def be_a(expected_class)
|
|
22
|
+
BeAKindOfMatcher.new(expected_class)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def be_an(expected_class)
|
|
26
|
+
BeAKindOfMatcher.new(expected_class)
|
|
27
|
+
end
|
|
28
|
+
|
|
21
29
|
def be_nil
|
|
22
30
|
BeNilMatcher.new
|
|
23
31
|
end
|
|
24
32
|
|
|
25
|
-
def
|
|
26
|
-
|
|
33
|
+
def match(regexp)
|
|
34
|
+
MatchMatcher.new(regexp)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def contain_exactly(*expected_items)
|
|
38
|
+
ContainExactlyMatcher.new(expected_items, matcher_name: "contain exactly")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def match_array(expected_items)
|
|
42
|
+
ContainExactlyMatcher.new(expected_items, matcher_name: "match array")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def raise_error(*expected_error)
|
|
46
|
+
RaiseErrorMatcher.new(*expected_error)
|
|
27
47
|
end
|
|
28
48
|
|
|
29
49
|
def change(*args, &block)
|
|
@@ -51,6 +71,10 @@ module Smartest
|
|
|
51
71
|
def negated_failure_message
|
|
52
72
|
"expected #{@actual.inspect} not to eq #{@expected.inspect}"
|
|
53
73
|
end
|
|
74
|
+
|
|
75
|
+
def description
|
|
76
|
+
"eq #{@expected.inspect}"
|
|
77
|
+
end
|
|
54
78
|
end
|
|
55
79
|
|
|
56
80
|
class IncludeMatcher
|
|
@@ -72,6 +96,10 @@ module Smartest
|
|
|
72
96
|
def negated_failure_message
|
|
73
97
|
"expected #{@actual.inspect} not to include #{@expected.inspect}"
|
|
74
98
|
end
|
|
99
|
+
|
|
100
|
+
def description
|
|
101
|
+
"include #{@expected.inspect}"
|
|
102
|
+
end
|
|
75
103
|
end
|
|
76
104
|
|
|
77
105
|
class StartWithMatcher
|
|
@@ -94,6 +122,10 @@ module Smartest
|
|
|
94
122
|
"expected #{@actual.inspect} not to start with #{expected_description}"
|
|
95
123
|
end
|
|
96
124
|
|
|
125
|
+
def description
|
|
126
|
+
"start with #{expected_description}"
|
|
127
|
+
end
|
|
128
|
+
|
|
97
129
|
private
|
|
98
130
|
|
|
99
131
|
def expected_description
|
|
@@ -123,6 +155,10 @@ module Smartest
|
|
|
123
155
|
"expected #{@actual.inspect} not to end with #{expected_description}"
|
|
124
156
|
end
|
|
125
157
|
|
|
158
|
+
def description
|
|
159
|
+
"end with #{expected_description}"
|
|
160
|
+
end
|
|
161
|
+
|
|
126
162
|
private
|
|
127
163
|
|
|
128
164
|
def expected_description
|
|
@@ -132,6 +168,41 @@ module Smartest
|
|
|
132
168
|
end
|
|
133
169
|
end
|
|
134
170
|
|
|
171
|
+
class BeAKindOfMatcher
|
|
172
|
+
def initialize(expected_class)
|
|
173
|
+
@expected_class = expected_class
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def matches?(actual)
|
|
177
|
+
@actual = actual
|
|
178
|
+
actual.is_a?(@expected_class)
|
|
179
|
+
rescue TypeError
|
|
180
|
+
false
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def failure_message
|
|
184
|
+
"expected #{@actual.inspect} to be a kind of #{expected_description}, but was #{actual_class_description}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def negated_failure_message
|
|
188
|
+
"expected #{@actual.inspect} not to be a kind of #{expected_description}, but was #{actual_class_description}"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def description
|
|
192
|
+
"be a kind of #{expected_description}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
|
|
197
|
+
def expected_description
|
|
198
|
+
@expected_class.to_s
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def actual_class_description
|
|
202
|
+
@actual.class.to_s
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
135
206
|
class BeNilMatcher
|
|
136
207
|
def matches?(actual)
|
|
137
208
|
@actual = actual
|
|
@@ -145,11 +216,164 @@ module Smartest
|
|
|
145
216
|
def negated_failure_message
|
|
146
217
|
"expected #{@actual.inspect} not to be nil"
|
|
147
218
|
end
|
|
219
|
+
|
|
220
|
+
def description
|
|
221
|
+
"be nil"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
class MatchMatcher
|
|
226
|
+
def initialize(regexp)
|
|
227
|
+
@regexp = regexp
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def matches?(actual)
|
|
231
|
+
@actual = actual
|
|
232
|
+
@regexp.match?(actual)
|
|
233
|
+
rescue NoMethodError, TypeError
|
|
234
|
+
false
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def failure_message
|
|
238
|
+
"expected #{@actual.inspect} to match #{@regexp.inspect}"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def negated_failure_message
|
|
242
|
+
"expected #{@actual.inspect} not to match #{@regexp.inspect}"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def description
|
|
246
|
+
"match #{@regexp.inspect}"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
class ContainExactlyMatcher
|
|
251
|
+
def initialize(expected_items, matcher_name:)
|
|
252
|
+
@expected_items = expected_items
|
|
253
|
+
@matcher_name = matcher_name
|
|
254
|
+
reset_result
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def matches?(actual)
|
|
258
|
+
@actual = actual
|
|
259
|
+
reset_result
|
|
260
|
+
return false unless actual_items?
|
|
261
|
+
|
|
262
|
+
match_items
|
|
263
|
+
@missing_items.empty? && @extra_items.empty?
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def failure_message
|
|
267
|
+
details = failure_details
|
|
268
|
+
message = "expected #{@actual.inspect} to #{@matcher_name} #{format_expected_items(@expected_items)}"
|
|
269
|
+
details.empty? ? message : "#{message}; #{details.join('; ')}"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def negated_failure_message
|
|
273
|
+
"expected #{@actual.inspect} not to #{@matcher_name} #{format_expected_items(@expected_items)}"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def description
|
|
277
|
+
"#{@matcher_name} #{format_expected_items(@expected_items)}"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
private
|
|
281
|
+
|
|
282
|
+
def reset_result
|
|
283
|
+
@actual_items = nil
|
|
284
|
+
@missing_items = @expected_items.dup
|
|
285
|
+
@extra_items = []
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def actual_items?
|
|
289
|
+
return false unless @actual.respond_to?(:to_a)
|
|
290
|
+
|
|
291
|
+
@actual_items = @actual.to_a
|
|
292
|
+
true
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def match_items
|
|
296
|
+
adjacency = build_adjacency
|
|
297
|
+
actual_matches = Array.new(@actual_items.length)
|
|
298
|
+
expected_order(adjacency).each do |expected_index|
|
|
299
|
+
assign_expected_item(expected_index, adjacency, actual_matches, [])
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
matched_expected_indexes = actual_matches.compact
|
|
303
|
+
@missing_items = []
|
|
304
|
+
@expected_items.each_with_index do |item, index|
|
|
305
|
+
@missing_items << item unless matched_expected_indexes.include?(index)
|
|
306
|
+
end
|
|
307
|
+
@extra_items = []
|
|
308
|
+
@actual_items.each_with_index do |item, index|
|
|
309
|
+
@extra_items << item if actual_matches[index].nil?
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def build_adjacency
|
|
314
|
+
@expected_items.map do |expected_item|
|
|
315
|
+
@actual_items.each_index.select do |actual_index|
|
|
316
|
+
expected_item_matches_actual_item?(expected_item, @actual_items[actual_index])
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def expected_order(adjacency)
|
|
322
|
+
(0...@expected_items.length).sort_by { |index| [adjacency[index].length, index] }
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def assign_expected_item(expected_index, adjacency, actual_matches, seen_actual_indexes)
|
|
326
|
+
adjacency[expected_index].each do |actual_index|
|
|
327
|
+
next if seen_actual_indexes.include?(actual_index)
|
|
328
|
+
|
|
329
|
+
seen_actual_indexes << actual_index
|
|
330
|
+
if actual_matches[actual_index].nil? ||
|
|
331
|
+
assign_expected_item(actual_matches[actual_index], adjacency, actual_matches, seen_actual_indexes)
|
|
332
|
+
actual_matches[actual_index] = expected_index
|
|
333
|
+
return true
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
false
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def expected_item_matches_actual_item?(expected_item, actual_item)
|
|
341
|
+
if expected_item.respond_to?(:matches?)
|
|
342
|
+
expected_item.matches?(actual_item)
|
|
343
|
+
else
|
|
344
|
+
actual_item == expected_item
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def failure_details
|
|
349
|
+
return ["actual did not provide items"] unless @actual_items
|
|
350
|
+
|
|
351
|
+
details = []
|
|
352
|
+
details << "missing: #{format_expected_items(@missing_items)}" unless @missing_items.empty?
|
|
353
|
+
details << "extra: #{format_actual_items(@extra_items)}" unless @extra_items.empty?
|
|
354
|
+
details
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def format_expected_items(items)
|
|
358
|
+
"[#{items.map { |item| format_expected_item(item) }.join(', ')}]"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def format_actual_items(items)
|
|
362
|
+
"[#{items.map(&:inspect).join(', ')}]"
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def format_expected_item(item)
|
|
366
|
+
return item.description if item.respond_to?(:description)
|
|
367
|
+
|
|
368
|
+
item.inspect
|
|
369
|
+
end
|
|
148
370
|
end
|
|
149
371
|
|
|
150
372
|
class RaiseErrorMatcher
|
|
151
|
-
def initialize(expected_error)
|
|
152
|
-
@
|
|
373
|
+
def initialize(*expected_error)
|
|
374
|
+
@expected_type = expected_type_for(expected_error)
|
|
375
|
+
@expected_error_class = expected_error.find { |item| error_class?(item) }
|
|
376
|
+
@expected_message_regexp = expected_error.find { |item| item.is_a?(Regexp) }
|
|
153
377
|
@actual_error = nil
|
|
154
378
|
@callable = true
|
|
155
379
|
end
|
|
@@ -165,18 +389,60 @@ module Smartest
|
|
|
165
389
|
raise if Smartest.fatal_exception?(error)
|
|
166
390
|
|
|
167
391
|
@actual_error = error
|
|
168
|
-
|
|
392
|
+
expected_error_matches?(error)
|
|
169
393
|
end
|
|
170
394
|
|
|
171
395
|
def failure_message
|
|
172
|
-
return "expected a block to raise #{
|
|
173
|
-
return "expected block to raise #{
|
|
396
|
+
return "expected a block to raise #{expected_description}" unless @callable
|
|
397
|
+
return "expected block to raise #{expected_description}, but nothing was raised" unless @actual_error
|
|
174
398
|
|
|
175
|
-
"expected block to raise #{
|
|
399
|
+
"expected block to raise #{expected_description}, but raised #{actual_error_description}"
|
|
176
400
|
end
|
|
177
401
|
|
|
178
402
|
def negated_failure_message
|
|
179
|
-
"expected block not to raise #{
|
|
403
|
+
"expected block not to raise #{expected_description}, but raised #{actual_error_description}"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
private
|
|
407
|
+
|
|
408
|
+
def expected_type_for(expected_error)
|
|
409
|
+
return :class if expected_error.length == 1 && error_class?(expected_error.first)
|
|
410
|
+
return :message_regexp if expected_error.length == 1 && expected_error.first.is_a?(Regexp)
|
|
411
|
+
return :class_and_message_regexp if expected_error.length == 2 &&
|
|
412
|
+
error_class?(expected_error[0]) &&
|
|
413
|
+
expected_error[1].is_a?(Regexp)
|
|
414
|
+
|
|
415
|
+
raise ArgumentError, "raise_error supports an error class, message regexp, or error class and message regexp"
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def error_class?(value)
|
|
419
|
+
value.is_a?(Class) && value <= Exception
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def expected_error_matches?(error)
|
|
423
|
+
case @expected_type
|
|
424
|
+
when :class
|
|
425
|
+
error.is_a?(@expected_error_class)
|
|
426
|
+
when :message_regexp
|
|
427
|
+
@expected_message_regexp.match?(error.message)
|
|
428
|
+
when :class_and_message_regexp
|
|
429
|
+
error.is_a?(@expected_error_class) && @expected_message_regexp.match?(error.message)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def expected_description
|
|
434
|
+
case @expected_type
|
|
435
|
+
when :class
|
|
436
|
+
@expected_error_class.to_s
|
|
437
|
+
when :message_regexp
|
|
438
|
+
"error with message matching #{@expected_message_regexp.inspect}"
|
|
439
|
+
when :class_and_message_regexp
|
|
440
|
+
"#{@expected_error_class} with message matching #{@expected_message_regexp.inspect}"
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def actual_error_description
|
|
445
|
+
"#{@actual_error.class}: #{@actual_error.message}"
|
|
180
446
|
end
|
|
181
447
|
end
|
|
182
448
|
|
data/lib/smartest/version.rb
CHANGED
data/smartest/smartest_test.rb
CHANGED
|
@@ -48,6 +48,15 @@ class SelfTestRegisteredFixture < Smartest::Fixture
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
class SelfTestBaseType; end
|
|
52
|
+
class SelfTestChildType < SelfTestBaseType; end
|
|
53
|
+
|
|
54
|
+
module SelfTestMarkerType; end
|
|
55
|
+
|
|
56
|
+
class SelfTestMarkedType
|
|
57
|
+
include SelfTestMarkerType
|
|
58
|
+
end
|
|
59
|
+
|
|
51
60
|
around_suite do |suite|
|
|
52
61
|
use_fixture SelfTestRegisteredFixture
|
|
53
62
|
suite.run
|
|
@@ -181,9 +190,25 @@ test("supports basic matchers") do
|
|
|
181
190
|
expect("https://example.test").not_to start_with("about:")
|
|
182
191
|
expect("report.txt").not_to end_with(".png")
|
|
183
192
|
expect(Object.new).not_to start_with("prefix")
|
|
193
|
+
expect(SelfTestChildType.new).to be_a(SelfTestBaseType)
|
|
194
|
+
expect(SelfTestMarkedType.new).to be_an(SelfTestMarkerType)
|
|
184
195
|
expect(nil).to be_nil
|
|
185
196
|
expect("value").not_to be_nil
|
|
197
|
+
expect("https://example.test").to match(%r{\Ahttps://})
|
|
198
|
+
expect("about:blank").not_to match(%r{\Ahttps://})
|
|
199
|
+
expect(%w[request close request]).to contain_exactly("request", "request", "close")
|
|
200
|
+
expect(%i[request close open]).to match_array(%i[open request close])
|
|
201
|
+
expect(["foo", 42]).to contain_exactly(match(/foo/), eq(42))
|
|
202
|
+
expect(["ab", "ac"]).to contain_exactly(match(/a/), "ab")
|
|
203
|
+
expect([nil, false]).to contain_exactly(false, nil)
|
|
204
|
+
expect([:request]).not_to contain_exactly(:request, :request)
|
|
186
205
|
expect { raise ArgumentError, "bad" }.to raise_error(ArgumentError)
|
|
206
|
+
expect { raise RuntimeError, "request timed out" }.to raise_error(/timed out/)
|
|
207
|
+
expect { raise ArgumentError, "bad input" }.to raise_error(ArgumentError, /bad/)
|
|
208
|
+
expect { raise ArgumentError, "bad" }.not_to raise_error(RuntimeError)
|
|
209
|
+
expect { raise ArgumentError, "bad" }.not_to raise_error(/timed out/)
|
|
210
|
+
expect { raise ArgumentError, "bad" }.not_to raise_error(ArgumentError, /timed out/)
|
|
211
|
+
expect { :ok }.not_to raise_error(/timed out/)
|
|
187
212
|
end
|
|
188
213
|
)
|
|
189
214
|
)
|
|
@@ -193,6 +218,166 @@ test("supports basic matchers") do
|
|
|
193
218
|
expect(status).to eq(0)
|
|
194
219
|
end
|
|
195
220
|
|
|
221
|
+
test("reports be_a and match matcher failures") do
|
|
222
|
+
suite = Smartest::Suite.new
|
|
223
|
+
suite.tests.add(
|
|
224
|
+
SmartestSelfTest.test_case(
|
|
225
|
+
"bad type",
|
|
226
|
+
proc { expect(nil).to be_a(String) }
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
suite.tests.add(
|
|
230
|
+
SmartestSelfTest.test_case(
|
|
231
|
+
"bad negated type",
|
|
232
|
+
proc { expect(SelfTestChildType.new).not_to be_an(SelfTestBaseType) }
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
suite.tests.add(
|
|
236
|
+
SmartestSelfTest.test_case(
|
|
237
|
+
"bad regex",
|
|
238
|
+
proc { expect("about:blank").to match(%r{\Ahttps://}) }
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
suite.tests.add(
|
|
242
|
+
SmartestSelfTest.test_case(
|
|
243
|
+
"bad negated regex",
|
|
244
|
+
proc { expect("https://example.test").not_to match(%r{\Ahttps://}) }
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
status, output = SmartestSelfTest.run_suite(suite)
|
|
249
|
+
|
|
250
|
+
expect(status).to eq(1)
|
|
251
|
+
expect(output).to include("expected nil to be a kind of String, but was NilClass")
|
|
252
|
+
expect(output).to include("not to be a kind of SelfTestBaseType, but was SelfTestChildType")
|
|
253
|
+
expect(output).to include('expected "about:blank" to match /\\Ahttps:\\/\\//')
|
|
254
|
+
expect(output).to include('expected "https://example.test" not to match /\\Ahttps:\\/\\//')
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
test("reports contain_exactly and match_array matcher failures") do
|
|
258
|
+
suite = Smartest::Suite.new
|
|
259
|
+
suite.tests.add(
|
|
260
|
+
SmartestSelfTest.test_case(
|
|
261
|
+
"bad collection",
|
|
262
|
+
proc { expect(["foo", "baz", 2]).to contain_exactly(match(/foo/), eq(42), "bar") }
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
suite.tests.add(
|
|
266
|
+
SmartestSelfTest.test_case(
|
|
267
|
+
"bad duplicate count",
|
|
268
|
+
proc { expect([:request]).to match_array(%i[request request]) }
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
suite.tests.add(
|
|
272
|
+
SmartestSelfTest.test_case(
|
|
273
|
+
"bad negated collection",
|
|
274
|
+
proc { expect(%w[b a]).not_to contain_exactly("a", "b") }
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
status, output = SmartestSelfTest.run_suite(suite)
|
|
279
|
+
|
|
280
|
+
expect(status).to eq(1)
|
|
281
|
+
expect(output).to include('expected ["foo", "baz", 2] to contain exactly [match /foo/, eq 42, "bar"]')
|
|
282
|
+
expect(output).to include('missing: [eq 42, "bar"]')
|
|
283
|
+
expect(output).to include('extra: ["baz", 2]')
|
|
284
|
+
expect(output).to include("expected [:request] to match array [:request, :request]")
|
|
285
|
+
expect(output).to include("missing: [:request]")
|
|
286
|
+
expect(output).to include('expected ["b", "a"] not to contain exactly ["a", "b"]')
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
test("reports raise_error matcher failures") do
|
|
290
|
+
suite = Smartest::Suite.new
|
|
291
|
+
suite.tests.add(
|
|
292
|
+
SmartestSelfTest.test_case(
|
|
293
|
+
"nothing raised",
|
|
294
|
+
proc { expect { :ok }.to raise_error(/timeout/) }
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
suite.tests.add(
|
|
298
|
+
SmartestSelfTest.test_case(
|
|
299
|
+
"bad message",
|
|
300
|
+
proc { expect { raise RuntimeError, "permission denied" }.to raise_error(/timeout/) }
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
suite.tests.add(
|
|
304
|
+
SmartestSelfTest.test_case(
|
|
305
|
+
"bad negated message",
|
|
306
|
+
proc { expect { raise RuntimeError, "timeout after 1s" }.not_to raise_error(/timeout/) }
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
suite.tests.add(
|
|
310
|
+
SmartestSelfTest.test_case(
|
|
311
|
+
"bad class and message class",
|
|
312
|
+
proc { expect { raise RuntimeError, "timeout after 1s" }.to raise_error(ArgumentError, /timeout/) }
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
suite.tests.add(
|
|
316
|
+
SmartestSelfTest.test_case(
|
|
317
|
+
"bad class and message message",
|
|
318
|
+
proc { expect { raise ArgumentError, "permission denied" }.to raise_error(ArgumentError, /timeout/) }
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
suite.tests.add(
|
|
322
|
+
SmartestSelfTest.test_case(
|
|
323
|
+
"bad negated class and message",
|
|
324
|
+
proc { expect { raise ArgumentError, "timeout after 1s" }.not_to raise_error(ArgumentError, /timeout/) }
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
status, output = SmartestSelfTest.run_suite(suite)
|
|
329
|
+
|
|
330
|
+
expect(status).to eq(1)
|
|
331
|
+
expect(output).to include("expected block to raise error with message matching /timeout/, but nothing was raised")
|
|
332
|
+
expect(output).to include(
|
|
333
|
+
"expected block to raise error with message matching /timeout/, but raised RuntimeError: permission denied"
|
|
334
|
+
)
|
|
335
|
+
expect(output).to include(
|
|
336
|
+
"expected block not to raise error with message matching /timeout/, but raised RuntimeError: timeout after 1s"
|
|
337
|
+
)
|
|
338
|
+
expect(output).to include(
|
|
339
|
+
"expected block to raise ArgumentError with message matching /timeout/, but raised RuntimeError: timeout after 1s"
|
|
340
|
+
)
|
|
341
|
+
expect(output).to include(
|
|
342
|
+
"expected block to raise ArgumentError with message matching /timeout/, but raised ArgumentError: permission denied"
|
|
343
|
+
)
|
|
344
|
+
expect(output).to include(
|
|
345
|
+
"expected block not to raise ArgumentError with message matching /timeout/, but raised ArgumentError: timeout after 1s"
|
|
346
|
+
)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
test("rejects unsupported raise_error argument forms") do
|
|
350
|
+
error = SmartestSelfTest.capture_error(ArgumentError) do
|
|
351
|
+
raise_error
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
expect(error.message).to eq("raise_error supports an error class, message regexp, or error class and message regexp")
|
|
355
|
+
|
|
356
|
+
error = SmartestSelfTest.capture_error(ArgumentError) do
|
|
357
|
+
raise_error("timeout")
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
expect(error.message).to eq("raise_error supports an error class, message regexp, or error class and message regexp")
|
|
361
|
+
|
|
362
|
+
error = SmartestSelfTest.capture_error(ArgumentError) do
|
|
363
|
+
raise_error(String)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
expect(error.message).to eq("raise_error supports an error class, message regexp, or error class and message regexp")
|
|
367
|
+
|
|
368
|
+
error = SmartestSelfTest.capture_error(ArgumentError) do
|
|
369
|
+
raise_error(RuntimeError, "timeout")
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
expect(error.message).to eq("raise_error supports an error class, message regexp, or error class and message regexp")
|
|
373
|
+
|
|
374
|
+
error = SmartestSelfTest.capture_error(ArgumentError) do
|
|
375
|
+
raise_error(/timeout/, RuntimeError)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
expect(error.message).to eq("raise_error supports an error class, message regexp, or error class and message regexp")
|
|
379
|
+
end
|
|
380
|
+
|
|
196
381
|
test("reports start_with and end_with matcher failures") do
|
|
197
382
|
suite = Smartest::Suite.new
|
|
198
383
|
suite.tests.add(
|