oojspec 0.2.0 → 0.3.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.
@@ -0,0 +1,692 @@
1
+ ((typeof define === "function" && define.amd && function (m) {
2
+ define("referee", ["expect", "lodash", "samsam", "bane"], m);
3
+ }) || (typeof module === "object" && function (m) {
4
+ module.exports = m(
5
+ require("./expect"),
6
+ require("lodash"),
7
+ require("samsam"),
8
+ require("bane")
9
+ );
10
+ }) || function (m) {
11
+ this.referee = m(
12
+ this.expect,
13
+ this._,
14
+ this.samsam,
15
+ this.bane
16
+ );
17
+ })(function (expect, _, samsam, bane) {
18
+ "use strict";
19
+
20
+ var toString = Object.prototype.toString;
21
+ var slice = Array.prototype.slice;
22
+ var assert, refute, referee = bane.createEventEmitter();
23
+
24
+ referee.countAssertion = function countAssertion() {
25
+ if (typeof referee.count !== "number") { referee.count = 0; }
26
+ referee.count += 1;
27
+ };
28
+
29
+ function interpolate(string, prop, value) {
30
+ return string.replace(new RegExp("\\$\\{" + prop + "\\}", "g"), value);
31
+ }
32
+
33
+ // Interpolate positional arguments. Replaces occurences of ${<index>} in
34
+ // the string with the corresponding entry in values[<index>]
35
+ function interpolatePosArg(message, values) {
36
+ return _.reduce(values, function (msg, value, index) {
37
+ return interpolate(msg, index, referee.format(value));
38
+ }, message);
39
+ }
40
+
41
+ function interpolateProperties(message, properties) {
42
+ return _.reduce(_.keys(properties), function (str, name) {
43
+ var formattedValue = name == "customMessage" ? referee.prepareMessage(properties[name]) : referee.format(properties[name]);
44
+ return interpolate(str, name, formattedValue);
45
+ }, message || "");
46
+ }
47
+
48
+ // Internal helper. Used throughout to fail assertions if they receive
49
+ // too few arguments. The name is provided for a helpful error message.
50
+ function assertArgNum(name, args, num) {
51
+ if (args.length < num) {
52
+ referee.fail("[" + name + "] Expected to receive at least " +
53
+ num + " argument" + (num > 1 ? "s" : ""));
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+
59
+ // Internal helper. Not the most elegant of functions, but it takes
60
+ // care of all the nitty-gritty of assertion functions: counting,
61
+ // verifying parameter count, interpolating messages with actual
62
+ // values and so on.
63
+ function defineAssertion(type, name, func, minArgs, messageValues) {
64
+ referee[type][name] = function () {
65
+ referee.countAssertion();
66
+ var fullName = type + "." + name, failed = false;
67
+
68
+ if (!assertArgNum(fullName, arguments, minArgs || func.length)) {
69
+ return;
70
+ }
71
+
72
+ var args = slice.call(arguments, 0),
73
+ namedValues = {};
74
+
75
+ if (typeof messageValues === "function") {
76
+ var replacedValues = messageValues.apply(this, args);
77
+ if (typeof(replacedValues) === "object") {
78
+ namedValues = replacedValues;
79
+ } else {
80
+ args = replacedValues
81
+ }
82
+ }
83
+
84
+ var ctx = {
85
+ fail: function (msg) {
86
+ failed = true;
87
+ delete this.fail;
88
+ var message = referee[type][name][msg] || msg;
89
+ message = interpolatePosArg(message, args);
90
+ message = interpolateProperties(message, this);
91
+ message = interpolateProperties(message, namedValues);
92
+ referee.fail("[" + type + "." + name + "] " + message);
93
+ return false;
94
+ }
95
+ };
96
+
97
+ if (!func.apply(ctx, arguments) && !failed) {
98
+ // when a function returns false and hasn't already failed with a custom message,
99
+ // fail with default message
100
+ ctx.fail("message");
101
+ }
102
+
103
+ if (!failed) {
104
+ referee.emit.apply(referee, ["pass", fullName].concat(args));
105
+ }
106
+ };
107
+ }
108
+
109
+ referee.add = function (name, opt) {
110
+ var refuteArgs;
111
+
112
+ if (opt.refute) {
113
+ refuteArgs = opt.refute.length;
114
+ } else {
115
+ refuteArgs = opt.assert.length;
116
+ opt.refute = function () {
117
+ return !opt.assert.apply(this, arguments);
118
+ };
119
+ }
120
+
121
+ var values = opt.values;
122
+ defineAssertion("assert", name, opt.assert, opt.assert.length, values);
123
+ defineAssertion("refute", name, opt.refute, refuteArgs, values);
124
+
125
+ assert[name].message = opt.assertMessage;
126
+ refute[name].message = opt.refuteMessage;
127
+
128
+ if (opt.expectation) {
129
+ if (referee.expect && referee.expect.wrapAssertion) {
130
+ referee.expect.wrapAssertion(name, opt.expectation, referee);
131
+ } else {
132
+ assert[name].expectationName = opt.expectation;
133
+ refute[name].expectationName = opt.expectation;
134
+ }
135
+ }
136
+ };
137
+
138
+ assert = referee.assert = function assert(actual, message) {
139
+ referee.countAssertion();
140
+ if (!assertArgNum("assert", arguments, 1)) { return; }
141
+
142
+ if (!actual) {
143
+ var v = referee.format(actual);
144
+ referee.fail(message || "[assert] Expected " + v + " to be truthy");
145
+ } else {
146
+ referee.emit("pass", "assert", message || "", actual);
147
+ }
148
+ };
149
+
150
+ assert.toString = function () {
151
+ return "referee.assert()";
152
+ };
153
+
154
+ refute = referee.refute = function (actual, message) {
155
+ referee.countAssertion();
156
+ if (!assertArgNum("refute", arguments, 1)) { return; }
157
+
158
+ if (actual) {
159
+ var v = referee.format(actual);
160
+ referee.fail(message || "[refute] Expected " + v + " to be falsy");
161
+ } else {
162
+ referee.emit("pass", "refute", message || "", actual);
163
+ }
164
+ };
165
+
166
+ referee.count = 0;
167
+
168
+ referee.fail = function (message) {
169
+ var exception = new Error(message);
170
+ exception.name = "AssertionError";
171
+
172
+ try {
173
+ throw exception;
174
+ } catch (e) {
175
+ referee.emit("failure", e);
176
+ }
177
+
178
+ if (typeof referee.throwOnFailure !== "boolean" ||
179
+ referee.throwOnFailure) {
180
+ throw exception;
181
+ }
182
+ };
183
+
184
+ referee.format = function (object) { return String(object); };
185
+
186
+ referee.prepareMessage = function msg(message) {
187
+ if (!message) {
188
+ return "";
189
+ }
190
+ return message + (/[.:!?]$/.test(message) ? " " : ": ");
191
+ };
192
+
193
+ function actualAndExpectedMessageValues(actual, expected, message) {
194
+ return {
195
+ actual: actual,
196
+ expected: expected,
197
+ customMessage: message
198
+ };
199
+ }
200
+
201
+ function actualMessageValues(actual, message) {
202
+ return {
203
+ actual: actual,
204
+ customMessage: message
205
+ };
206
+ }
207
+
208
+ function actualAndTypeOfMessageValues(actual, message) {
209
+ return {
210
+ actual: actual,
211
+ actualType: typeof actual,
212
+ customMessage: message
213
+ };
214
+ }
215
+
216
+ referee.add("same", {
217
+ assert: function (actual, expected) {
218
+ return samsam.identical(actual, expected);
219
+ },
220
+ refute: function (actual, expected) {
221
+ return !samsam.identical(actual, expected);
222
+ },
223
+ assertMessage: "${customMessage}${actual} expected to be the same object as ${expected}",
224
+ refuteMessage: "${customMessage}${actual} expected not to be the same object as ${expected}",
225
+ expectation: "toBe",
226
+ values: actualAndExpectedMessageValues
227
+ });
228
+
229
+ // Extract/replace with separate module that does a more detailed
230
+ // visualization of multi-line strings
231
+ function multiLineStringDiff(actual, expected, message) {
232
+ if (actual === expected) { return true; }
233
+
234
+ var heading = assert.equals.multiLineStringHeading;
235
+ var failureText = interpolateProperties(heading, { customMessage: message });
236
+ var actualLines = actual.split("\n");
237
+ var expectedLines = expected.split("\n");
238
+ var lineCount = Math.max(expectedLines.length, actualLines.length);
239
+ var i, lines = [];
240
+
241
+ for (i = 0; i < lineCount; ++i) {
242
+ if (expectedLines[i] !== actualLines[i]) {
243
+ lines.push("line " + (i + 1) + ": " + (expectedLines[i] || "") +
244
+ "\nwas: " + (actualLines[i] || ""));
245
+ }
246
+ }
247
+
248
+ referee.fail("[assert.equals] " + failureText + lines.join("\n\n"));
249
+ return false;
250
+ }
251
+
252
+ referee.add("equals", {
253
+ // Uses arguments[2] because the function's .length is used to determine
254
+ // the minimum required number of arguments.
255
+ assert: function (actual, expected) {
256
+ if (typeof actual === "string" && typeof expected === "string" &&
257
+ (actual.indexOf("\n") >= 0 ||
258
+ expected.indexOf("\n") >= 0)) {
259
+ return multiLineStringDiff(actual, expected, arguments[2]);
260
+ }
261
+
262
+ return samsam.deepEqual(actual, expected);
263
+ },
264
+
265
+ refute: function (actual, expected) {
266
+ return !samsam.deepEqual(actual, expected);
267
+ },
268
+
269
+ assertMessage: "${customMessage}${actual} expected to be equal to ${expected}",
270
+ refuteMessage: "${customMessage}${actual} expected not to be equal to ${expected}",
271
+ expectation: "toEqual",
272
+ values: actualAndExpectedMessageValues
273
+ });
274
+
275
+ assert.equals.multiLineStringHeading = "${customMessage}Expected multi-line strings " +
276
+ "to be equal:\n";
277
+
278
+ referee.add("greater", {
279
+ assert: function (actual, expected) {
280
+ return actual > expected;
281
+ },
282
+
283
+ assertMessage: "${customMessage}Expected ${actual} to be greater than ${expected}",
284
+ refuteMessage: "${customMessage}Expected ${actual} to be less than or equal to ${expected}",
285
+ expectation: "toBeGreaterThan",
286
+ values: actualAndExpectedMessageValues
287
+ });
288
+
289
+ referee.add("less", {
290
+ assert: function (actual, expected) {
291
+ return actual < expected;
292
+ },
293
+
294
+ assertMessage: "${customMessage}Expected ${actual} to be less than ${expected}",
295
+ refuteMessage: "${customMessage}Expected ${actual} to be greater than or equal to ${expected}",
296
+ expectation: "toBeLessThan",
297
+ values: actualAndExpectedMessageValues
298
+ });
299
+
300
+ referee.add("defined", {
301
+ assert: function (actual) {
302
+ return typeof actual !== "undefined";
303
+ },
304
+ assertMessage: "${customMessage}Expected to be defined",
305
+ refuteMessage: "${customMessage}Expected ${actual} (${actualType}) not to be defined",
306
+ expectation: "toBeDefined",
307
+ values: actualAndTypeOfMessageValues
308
+ });
309
+
310
+ referee.add("isNull", {
311
+ assert: function (actual) {
312
+ return actual === null;
313
+ },
314
+ assertMessage: "${customMessage}Expected ${actual} to be null",
315
+ refuteMessage: "${customMessage}Expected not to be null",
316
+ expectation: "toBeNull",
317
+ values: actualMessageValues
318
+ });
319
+
320
+
321
+ referee.match = function (actual, matcher) {
322
+ try {
323
+ return samsam.match(actual, matcher);
324
+ } catch (e) {
325
+ throw new Error("Matcher (" + referee.format(matcher) +
326
+ ") was not a string, a number, a function, " +
327
+ "a boolean or an object");
328
+ }
329
+ };
330
+
331
+ referee.add("match", {
332
+ assert: function (actual, matcher) {
333
+ var passed;
334
+
335
+ try {
336
+ passed = referee.match(actual, matcher);
337
+ } catch (e) {
338
+ this.exceptionMessage = e.message;
339
+ return this.fail("exceptionMessage");
340
+ }
341
+
342
+ return passed;
343
+ },
344
+
345
+ refute: function (actual, matcher) {
346
+ var passed;
347
+
348
+ try {
349
+ passed = referee.match(actual, matcher);
350
+ } catch (e) {
351
+ this.exceptionMessage = e.message;
352
+ return this.fail("exceptionMessage");
353
+ }
354
+
355
+ return !passed;
356
+ },
357
+
358
+ assertMessage: "${customMessage}${actual} expected to match ${expected}",
359
+ refuteMessage: "${customMessage}${actual} expected not to match ${expected}",
360
+ expectation: "toMatch",
361
+ values: actualAndExpectedMessageValues
362
+ });
363
+
364
+ assert.match.exceptionMessage = refute.match.exceptionMessage = "${customMessage}${exceptionMessage}";
365
+
366
+ referee.add("isObject", {
367
+ assert: function (actual) {
368
+ return typeof actual === "object" && !!actual;
369
+ },
370
+ assertMessage: "${customMessage}${actual} (${actualType}) expected to be object and not null",
371
+ refuteMessage: "${customMessage}${actual} expected to be null or not an object",
372
+ expectation: "toBeObject",
373
+ values: actualAndTypeOfMessageValues
374
+ });
375
+
376
+ referee.add("isFunction", {
377
+ assert: function (actual) {
378
+ return typeof actual === "function";
379
+ },
380
+ assertMessage: "${customMessage}${actual} (${actualType}) expected to be function",
381
+ refuteMessage: "${customMessage}${actual} expected not to be function",
382
+ expectation: "toBeFunction",
383
+ values: function (actual, message) {
384
+ return {
385
+ actual: String(actual).replace("\n", ""),
386
+ actualType: typeof actual,
387
+ customMessage: message
388
+ };
389
+ }
390
+ });
391
+
392
+ referee.add("isTrue", {
393
+ assert: function (actual) {
394
+ return actual === true;
395
+ },
396
+ assertMessage: "${customMessage}Expected ${actual} to be true",
397
+ refuteMessage: "${customMessage}Expected ${actual} to not be true",
398
+ expectation: "toBeTrue",
399
+ values: actualMessageValues
400
+ });
401
+
402
+ referee.add("isFalse", {
403
+ assert: function (actual) {
404
+ return actual === false;
405
+ },
406
+ assertMessage: "${customMessage}Expected ${actual} to be false",
407
+ refuteMessage: "${customMessage}Expected ${actual} to not be false",
408
+ expectation: "toBeFalse",
409
+ values: actualMessageValues
410
+ });
411
+
412
+ referee.add("isString", {
413
+ assert: function (actual) {
414
+ return typeof actual === "string";
415
+ },
416
+ assertMessage: "${customMessage}Expected ${actual} (${actualType}) to be string",
417
+ refuteMessage: "${customMessage}Expected ${actual} not to be string",
418
+ expectation: "toBeString",
419
+ values: actualAndTypeOfMessageValues
420
+ });
421
+
422
+ referee.add("isBoolean", {
423
+ assert: function (actual) {
424
+ return typeof actual === "boolean";
425
+ },
426
+ assertMessage: "${customMessage}Expected ${actual} (${actualType}) to be boolean",
427
+ refuteMessage: "${customMessage}Expected ${actual} not to be boolean",
428
+ expectation: "toBeBoolean",
429
+ values: actualAndTypeOfMessageValues
430
+ });
431
+
432
+ referee.add("isNumber", {
433
+ assert: function (actual) {
434
+ return typeof actual === "number" && !isNaN(actual);
435
+ },
436
+ assertMessage: "${customMessage}Expected ${actual} (${actualType}) to be a non-NaN number",
437
+ refuteMessage: "${customMessage}Expected ${actual} to be NaN or a non-number value",
438
+ expectation: "toBeNumber",
439
+ values: actualAndTypeOfMessageValues
440
+ });
441
+
442
+ referee.add("isNaN", {
443
+ assert: function (actual) {
444
+ return typeof actual === "number" && isNaN(actual);
445
+ },
446
+ assertMessage: "${customMessage}Expected ${actual} to be NaN",
447
+ refuteMessage: "${customMessage}Expected not to be NaN",
448
+ expectation: "toBeNaN",
449
+ values: actualAndTypeOfMessageValues
450
+ });
451
+
452
+ referee.add("isArray", {
453
+ assert: function (actual) {
454
+ return toString.call(actual) === "[object Array]";
455
+ },
456
+ assertMessage: "${customMessage}Expected ${actual} to be array",
457
+ refuteMessage: "${customMessage}Expected ${actual} not to be array",
458
+ expectation: "toBeArray",
459
+ values: actualAndTypeOfMessageValues
460
+ });
461
+
462
+ function isArrayLike(object) {
463
+ return _.isArray(object) ||
464
+ (!!object && typeof object.length === "number" &&
465
+ typeof object.splice === "function") ||
466
+ _.isArguments(object);
467
+ }
468
+
469
+ referee.isArrayLike = isArrayLike;
470
+
471
+ referee.add("isArrayLike", {
472
+ assert: function (actual) {
473
+ return isArrayLike(actual);
474
+ },
475
+ assertMessage: "${customMessage}Expected ${actual} to be array like",
476
+ refuteMessage: "${customMessage}Expected ${actual} not to be array like",
477
+ expectation: "toBeArrayLike",
478
+ values: actualAndTypeOfMessageValues
479
+ });
480
+
481
+ function exactKeys(object, keys) {
482
+ var keyMap = {};
483
+ var keyCnt = 0;
484
+ for (var i = 0; i < keys.length; i++) {
485
+ keyMap[keys[i]] = true;
486
+ keyCnt += 1;
487
+ }
488
+ for (var key in object) {
489
+ if (object.hasOwnProperty(key)) {
490
+ if (! keyMap[key]) {
491
+ return false;
492
+ }
493
+ keyCnt -= 1;
494
+ }
495
+ }
496
+ return keyCnt === 0;
497
+ }
498
+
499
+ referee.add("keys", {
500
+ assert: function (actual, keys) {
501
+ return exactKeys(actual, keys);
502
+ },
503
+ assertMessage: "${customMessage}Expected ${actualObject} to have exact keys ${keys}",
504
+ refuteMessage: "${customMessage}Expected not to have exact keys ${keys}",
505
+ expectation: "toHaveKeys",
506
+ values: function (actual, keys, message) {
507
+ return {
508
+ actualObject: actual,
509
+ keys: keys,
510
+ customMessage: message
511
+ }
512
+ }
513
+ });
514
+
515
+ function captureException(callback) {
516
+ try { callback(); } catch (e) { return e; }
517
+ return null;
518
+ }
519
+
520
+ referee.captureException = captureException;
521
+
522
+ referee.add("exception", {
523
+ assert: function (callback) {
524
+ var matcher = arguments[1];
525
+ var customMessage = arguments[2];
526
+
527
+ if (typeof matcher === "string") {
528
+ customMessage = matcher;
529
+ matcher = undefined;
530
+ }
531
+
532
+ this.expected = matcher;
533
+ this.customMessage = customMessage;
534
+
535
+ var err = captureException(callback);
536
+
537
+ if (err) {
538
+ this.actualExceptionType = err.name;
539
+ this.actualExceptionMessage = err.message;
540
+ this.actualExceptionStack = err.stack;
541
+ }
542
+
543
+ if (!err) {
544
+ if (typeof matcher === "object") {
545
+ return this.fail("typeNoExceptionMessage");
546
+ } else {
547
+ return this.fail("message");
548
+ }
549
+ }
550
+
551
+ if (typeof matcher === "object" && !referee.match(err, matcher)) {
552
+ return this.fail("typeFailMessage");
553
+ }
554
+
555
+ if (typeof matcher === "function" && matcher(err) !== true) {
556
+ return this.fail("matchFailMessage");
557
+ }
558
+
559
+ return true;
560
+ },
561
+
562
+ refute: function (callback) {
563
+ var err = captureException(callback);
564
+
565
+ if (err) {
566
+ this.customMessage = arguments[1];
567
+ this.actualExceptionType = err.name;
568
+ this.actualExceptionMessage = err.message;
569
+ return false;
570
+ }
571
+
572
+ return true;
573
+ },
574
+
575
+ expectation: "toThrow",
576
+ assertMessage: "${customMessage}Expected exception",
577
+ refuteMessage: "${customMessage}Expected not to throw but threw ${actualExceptionType} (${actualExceptionMessage})"
578
+ });
579
+
580
+ assert.exception.typeNoExceptionMessage = "${customMessage}Expected ${expected} but no exception was thrown";
581
+ assert.exception.typeFailMessage = "${customMessage}Expected ${expected} but threw ${actualExceptionType} (${actualExceptionMessage})\n${actualExceptionStack}";
582
+ assert.exception.matchFailMessage = "${customMessage}Expected thrown ${actualExceptionType} (${actualExceptionMessage}) to pass matcher function";
583
+
584
+
585
+ referee.add("near", {
586
+ assert: function (actual, expected, delta) {
587
+ return Math.abs(actual - expected) <= delta;
588
+ },
589
+ assertMessage: "${customMessage}Expected ${actual} to be equal to ${expected} +/- ${delta}",
590
+ refuteMessage: "${customMessage}Expected ${actual} not to be equal to ${expected} +/- ${delta}",
591
+ expectation: "toBeNear",
592
+ values: function (actual, expected, delta, message) {
593
+ return {
594
+ actual: actual,
595
+ expected: expected,
596
+ delta: delta,
597
+ customMessage: message
598
+ };
599
+ }
600
+ });
601
+
602
+ referee.add("hasPrototype", {
603
+ assert: function (actual, protoObj) {
604
+ return protoObj.isPrototypeOf(actual);
605
+ },
606
+ assertMessage: "${customMessage}Expected ${actual} to have ${expected} on its prototype chain",
607
+ refuteMessage: "${customMessage}Expected ${actual} not to have ${expected} on its " +
608
+ "prototype chain",
609
+ expectation: "toHavePrototype",
610
+ values: actualAndExpectedMessageValues
611
+ });
612
+
613
+ referee.add("contains", {
614
+ assert: function (haystack, needle) {
615
+ return _.include(haystack, needle);
616
+ },
617
+ assertMessage: "${customMessage}Expected [${actual}] to contain ${expected}",
618
+ refuteMessage: "${customMessage}Expected [${actual}] not to contain ${expected}",
619
+ expectation: "toContain",
620
+ values: actualAndExpectedMessageValues
621
+ });
622
+
623
+ referee.add("tagName", {
624
+ assert: function (element, tagName) {
625
+ // Uses arguments[2] because the function's .length is used to
626
+ // determine the minimum required number of arguments.
627
+ if (!element.tagName) {
628
+ return this.fail("noTagNameMessage");
629
+ }
630
+
631
+ return tagName.toLowerCase &&
632
+ tagName.toLowerCase() === element.tagName.toLowerCase();
633
+ },
634
+ assertMessage: "${customMessage}Expected tagName to be ${expected} but was ${actual}",
635
+ refuteMessage: "${customMessage}Expected tagName not to be ${actual}",
636
+ expectation: "toHaveTagName",
637
+ values: function (element, tagName, message) {
638
+ return {
639
+ actualElement: element,
640
+ actual: element.tagName,
641
+ expected: tagName,
642
+ customMessage: message
643
+ };
644
+ }
645
+ });
646
+
647
+ assert.tagName.noTagNameMessage = "${customMessage}Expected ${actualElement} to have tagName " +
648
+ "property";
649
+ refute.tagName.noTagNameMessage = "${customMessage}Expected ${actualElement} to have tagName " +
650
+ "property";
651
+
652
+ referee.add("className", {
653
+ assert: function (element, name) {
654
+ if (typeof element.className === "undefined") {
655
+ return this.fail("noClassNameMessage");
656
+ }
657
+
658
+ var expected = typeof name === "string" ? name.split(" ") : name;
659
+ var actual = element.className.split(" ");
660
+ var i, l;
661
+ for (i = 0, l = expected.length; i < l; i++) {
662
+ if (!_.include(actual, expected[i])) { return false; }
663
+ }
664
+
665
+ return true;
666
+ },
667
+ assertMessage: "${customMessage}Expected object's className to include ${expected} " +
668
+ "but was ${actual}",
669
+ refuteMessage: "${customMessage}Expected object's className not to include ${expected}",
670
+ expectation: "toHaveClassName",
671
+ values: function (element, className, message) {
672
+ return {
673
+ actualElement: element,
674
+ actual: element.className,
675
+ expected: className,
676
+ customMessage: message
677
+ };
678
+ }
679
+ });
680
+
681
+ assert.className.noClassNameMessage = "${customMessage}Expected object to have " +
682
+ "className property";
683
+ refute.className.noClassNameMessage = "${customMessage}Expected object to have " +
684
+ "className property";
685
+
686
+ referee.expect = function () {
687
+ expect.init(referee);
688
+ return expect.apply(referee, arguments);
689
+ };
690
+
691
+ return referee;
692
+ });