1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | var util = require('util')
|
13 | , http = require('http')
|
14 | , assert = require('assert')
|
15 | , AssertionError = assert.AssertionError
|
16 | , statusCodes = http.STATUS_CODES
|
17 | , eql = require('./eql')
|
18 | , i = util.inspect;
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | exports = module.exports = assert;
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | exports.version = '1.0.0';
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | exports.exist = function(obj, msg){
|
47 | if (null == obj) {
|
48 | throw new AssertionError({
|
49 | message: msg || ('expected ' + i(obj) + ' to exist')
|
50 | , stackStartFunction: should.exist
|
51 | });
|
52 | }
|
53 | };
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 | exports.not = {};
|
64 | exports.not.exist = function(obj, msg){
|
65 | if (null != obj) {
|
66 | throw new AssertionError({
|
67 | message: msg || ('expected ' + i(obj) + ' to not exist')
|
68 | , stackStartFunction: should.not.exist
|
69 | });
|
70 | }
|
71 | };
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | Object.defineProperty(Object.prototype, 'should', {
|
80 | set: function(){},
|
81 | get: function(){
|
82 | return new Assertion(Object(this).valueOf());
|
83 | },
|
84 | configurable: true
|
85 | });
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | var Assertion = exports.Assertion = function Assertion(obj) {
|
95 | this.obj = obj;
|
96 | };
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | Assertion.prototype = {
|
103 |
|
104 | |
105 |
|
106 |
|
107 |
|
108 | exports: exports,
|
109 |
|
110 | |
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | assert: function(expr, msg, negatedMsg, expected){
|
121 | var msg = this.negate ? negatedMsg : msg
|
122 | , ok = this.negate ? !expr : expr;
|
123 |
|
124 | if (!ok) {
|
125 | throw new AssertionError({
|
126 | message: msg
|
127 | , actual: this.obj
|
128 | , expected: expected
|
129 | , stackStartFunction: this.assert
|
130 | });
|
131 | }
|
132 | },
|
133 |
|
134 | |
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | get an() {
|
141 | return this;
|
142 | },
|
143 |
|
144 | |
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | get and() {
|
151 | return this;
|
152 | },
|
153 |
|
154 | |
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | get be() {
|
161 | return this;
|
162 | },
|
163 |
|
164 | |
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | get have() {
|
171 | return this;
|
172 | },
|
173 |
|
174 | |
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | get with() {
|
181 | return this;
|
182 | },
|
183 |
|
184 | |
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 | get not() {
|
191 | this.negate = true;
|
192 | return this;
|
193 | },
|
194 |
|
195 | |
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | get inspect() {
|
203 | return i(this.obj);
|
204 | },
|
205 |
|
206 | |
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 | get arguments() {
|
213 | this.assert(
|
214 | '[object Arguments]' == Object.prototype.toString.call(this.obj)
|
215 | , 'expected ' + this.inspect + ' to be arguments'
|
216 | , 'expected ' + this.inspect + ' to not be arguments');
|
217 | return this;
|
218 | },
|
219 |
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | get empty() {
|
227 | this.obj.should.have.property('length');
|
228 | this.assert(
|
229 | 0 === this.obj.length
|
230 | , 'expected ' + this.inspect + ' to be empty'
|
231 | , 'expected ' + this.inspect + ' not to be empty');
|
232 | return this;
|
233 | },
|
234 |
|
235 | |
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 | get ok() {
|
242 | this.assert(
|
243 | this.obj
|
244 | , 'expected ' + this.inspect + ' to be truthy'
|
245 | , 'expected ' + this.inspect + ' to be falsey');
|
246 | return this;
|
247 | },
|
248 |
|
249 | |
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 | get true() {
|
256 | this.assert(
|
257 | true === this.obj
|
258 | , 'expected ' + this.inspect + ' to be true'
|
259 | , 'expected ' + this.inspect + ' not to be true');
|
260 | return this;
|
261 | },
|
262 |
|
263 | |
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 | get false() {
|
270 | this.assert(
|
271 | false === this.obj
|
272 | , 'expected ' + this.inspect + ' to be false'
|
273 | , 'expected ' + this.inspect + ' not to be false');
|
274 | return this;
|
275 | },
|
276 |
|
277 | |
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 | eql: function(val, desc){
|
286 | this.assert(
|
287 | eql(val, this.obj)
|
288 | , 'expected ' + this.inspect + ' to equal ' + i(val) + (desc ? " | " + desc : "")
|
289 | , 'expected ' + this.inspect + ' to not equal ' + i(val) + (desc ? " | " + desc : "")
|
290 | , val);
|
291 | return this;
|
292 | },
|
293 |
|
294 | |
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | equal: function(val, desc){
|
303 | this.assert(
|
304 | val.valueOf() === this.obj
|
305 | , 'expected ' + this.inspect + ' to equal ' + i(val) + (desc ? " | " + desc : "")
|
306 | , 'expected ' + this.inspect + ' to not equal ' + i(val) + (desc ? " | " + desc : "")
|
307 | , val);
|
308 | return this;
|
309 | },
|
310 |
|
311 | |
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 | within: function(start, finish, desc){
|
321 | var range = start + '..' + finish;
|
322 | this.assert(
|
323 | this.obj >= start && this.obj <= finish
|
324 | , 'expected ' + this.inspect + ' to be within ' + range + (desc ? " | " + desc : "")
|
325 | , 'expected ' + this.inspect + ' to not be within ' + range + (desc ? " | " + desc : ""));
|
326 | return this;
|
327 | },
|
328 |
|
329 | |
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 | a: function(type, desc){
|
338 | this.assert(
|
339 | type == typeof this.obj
|
340 | , 'expected ' + this.inspect + ' to be a ' + type + (desc ? " | " + desc : "")
|
341 | , 'expected ' + this.inspect + ' not to be a ' + type + (desc ? " | " + desc : ""));
|
342 | return this;
|
343 | },
|
344 |
|
345 | |
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 | instanceof: function(constructor, desc){
|
354 | var name = constructor.name;
|
355 | this.assert(
|
356 | this.obj instanceof constructor
|
357 | , 'expected ' + this.inspect + ' to be an instance of ' + name + (desc ? " | " + desc : "")
|
358 | , 'expected ' + this.inspect + ' not to be an instance of ' + name + (desc ? " | " + desc : ""));
|
359 | return this;
|
360 | },
|
361 |
|
362 | /**
|
363 | * Assert numeric value above _n_.
|
364 | *
|
365 | * @param {Number} n
|
366 | * @param {String} description
|
367 | * @api public
|
368 | */
|
369 |
|
370 | above: function(n, desc){
|
371 | this.assert(
|
372 | this.obj > n
|
373 | , 'expected ' + this.inspect + ' to be above ' + n + (desc ? " | " + desc : "")
|
374 | , 'expected ' + this.inspect + ' to be below ' + n + (desc ? " | " + desc : ""));
|
375 | return this;
|
376 | },
|
377 |
|
378 | |
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 | below: function(n, desc){
|
387 | this.assert(
|
388 | this.obj < n
|
389 | , 'expected ' + this.inspect + ' to be below ' + n + (desc ? " | " + desc : "")
|
390 | , 'expected ' + this.inspect + ' to be above ' + n + (desc ? " | " + desc : ""));
|
391 | return this;
|
392 | },
|
393 |
|
394 | |
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 | match: function(regexp, desc){
|
403 | this.assert(
|
404 | regexp.exec(this.obj)
|
405 | , 'expected ' + this.inspect + ' to match ' + regexp + (desc ? " | " + desc : "")
|
406 | , 'expected ' + this.inspect + ' not to match ' + regexp + (desc ? " | " + desc : ""));
|
407 | return this;
|
408 | },
|
409 |
|
410 | |
411 |
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 | length: function(n, desc){
|
419 | this.obj.should.have.property('length');
|
420 | var len = this.obj.length;
|
421 | this.assert(
|
422 | n == len
|
423 | , 'expected ' + this.inspect + ' to have a length of ' + n + ' but got ' + len + (desc ? " | " + desc : "")
|
424 | , 'expected ' + this.inspect + ' to not have a length of ' + len + (desc ? " | " + desc : ""));
|
425 | return this;
|
426 | },
|
427 |
|
428 | |
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 | property: function(name, val, desc){
|
438 | if (this.negate && undefined !== val) {
|
439 | if (undefined === this.obj[name]) {
|
440 | throw new Error(this.inspect + ' has no property ' + i(name) + (desc ? " | " + desc : ""));
|
441 | }
|
442 | } else {
|
443 | this.assert(
|
444 | undefined !== this.obj[name]
|
445 | , 'expected ' + this.inspect + ' to have a property ' + i(name) + (desc ? " | " + desc : "")
|
446 | , 'expected ' + this.inspect + ' to not have a property ' + i(name) + (desc ? " | " + desc : ""));
|
447 | }
|
448 |
|
449 | if (undefined !== val) {
|
450 | this.assert(
|
451 | val === this.obj[name]
|
452 | , 'expected ' + this.inspect + ' to have a property ' + i(name)
|
453 | + ' of ' + i(val) + ', but got ' + i(this.obj[name]) + (desc ? " | " + desc : "")
|
454 | , 'expected ' + this.inspect + ' to not have a property ' + i(name) + ' of ' + i(val) + (desc ? " | " + desc : ""));
|
455 | }
|
456 |
|
457 | this.obj = this.obj[name];
|
458 | return this;
|
459 | },
|
460 |
|
461 | |
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 |
|
469 | ownProperty: function(name, desc){
|
470 | this.assert(
|
471 | this.obj.hasOwnProperty(name)
|
472 | , 'expected ' + this.inspect + ' to have own property ' + i(name) + (desc ? " | " + desc : "")
|
473 | , 'expected ' + this.inspect + ' to not have own property ' + i(name) + (desc ? " | " + desc : ""));
|
474 | return this;
|
475 | },
|
476 |
|
477 | |
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 | include: function(obj, desc){
|
486 | if (obj.constructor == Object){
|
487 | var cmp = {};
|
488 | for (var key in obj) cmp[key] = this.obj[key];
|
489 | this.assert(
|
490 | eql(cmp, obj)
|
491 | , 'expected ' + this.inspect + ' to include an object equal to ' + i(obj) + (desc ? " | " + desc : "")
|
492 | , 'expected ' + this.inspect + ' to not include an object equal to ' + i(obj) + (desc ? " | " + desc : ""));
|
493 | } else {
|
494 | this.assert(
|
495 | ~this.obj.indexOf(obj)
|
496 | , 'expected ' + this.inspect + ' to include ' + i(obj) + (desc ? " | " + desc : "")
|
497 | , 'expected ' + this.inspect + ' to not include ' + i(obj) + (desc ? " | " + desc : ""));
|
498 | }
|
499 | return this;
|
500 | },
|
501 |
|
502 | |
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 | includeEql: function(obj, desc){
|
511 | this.assert(
|
512 | this.obj.some(function(item) { return eql(obj, item); })
|
513 | , 'expected ' + this.inspect + ' to include an object equal to ' + i(obj) + (desc ? " | " + desc : "")
|
514 | , 'expected ' + this.inspect + ' to not include an object equal to ' + i(obj) + (desc ? " | " + desc : ""));
|
515 | return this;
|
516 | },
|
517 |
|
518 | |
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 | contain: function(obj){
|
526 | console.warn('should.contain() is deprecated, use should.include()');
|
527 | this.obj.should.be.an.instanceof(Array);
|
528 | this.assert(
|
529 | ~this.obj.indexOf(obj)
|
530 | , 'expected ' + this.inspect + ' to contain ' + i(obj)
|
531 | , 'expected ' + this.inspect + ' to not contain ' + i(obj));
|
532 | return this;
|
533 | },
|
534 |
|
535 | |
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 | keys: function(keys){
|
544 | var str
|
545 | , ok = true;
|
546 |
|
547 | keys = keys instanceof Array
|
548 | ? keys
|
549 | : Array.prototype.slice.call(arguments);
|
550 |
|
551 | if (!keys.length) throw new Error('keys required');
|
552 |
|
553 | var actual = Object.keys(this.obj)
|
554 | , len = keys.length;
|
555 |
|
556 |
|
557 | ok = keys.every(function(key){
|
558 | return ~actual.indexOf(key);
|
559 | });
|
560 |
|
561 |
|
562 | ok = ok && keys.length == actual.length;
|
563 |
|
564 |
|
565 | if (len > 1) {
|
566 | keys = keys.map(function(key){
|
567 | return i(key);
|
568 | });
|
569 | var last = keys.pop();
|
570 | str = keys.join(', ') + ', and ' + last;
|
571 | } else {
|
572 | str = i(keys[0]);
|
573 | }
|
574 |
|
575 |
|
576 | str = 'have ' + (len > 1 ? 'keys ' : 'key ') + str;
|
577 |
|
578 | this.assert(
|
579 | ok
|
580 | , 'expected ' + this.inspect + ' to ' + str
|
581 | , 'expected ' + this.inspect + ' to not ' + str);
|
582 |
|
583 | return this;
|
584 | },
|
585 |
|
586 | |
587 |
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 | header: function(field, val){
|
596 | this.obj.should
|
597 | .have.property('headers').and
|
598 | .have.property(field.toLowerCase(), val);
|
599 | return this;
|
600 | },
|
601 |
|
602 | |
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 | status: function(code){
|
611 | this.obj.should.have.property('statusCode');
|
612 | var status = this.obj.statusCode;
|
613 |
|
614 | this.assert(
|
615 | code == status
|
616 | , 'expected response code of ' + code + ' ' + i(statusCodes[code])
|
617 | + ', but got ' + status + ' ' + i(statusCodes[status])
|
618 | , 'expected to not respond with ' + code + ' ' + i(statusCodes[code]));
|
619 |
|
620 | return this;
|
621 | },
|
622 |
|
623 | |
624 |
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 | get json() {
|
631 | this.obj.should.have.property('headers');
|
632 | this.obj.headers.should.have.property('content-type');
|
633 | this.obj.headers['content-type'].should.include('application/json');
|
634 | return this;
|
635 | },
|
636 |
|
637 | |
638 |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 | get html() {
|
645 | this.obj.should.have.property('headers');
|
646 | this.obj.headers.should.have.property('content-type');
|
647 | this.obj.headers['content-type'].should.include('text/html');
|
648 | return this;
|
649 | },
|
650 |
|
651 | |
652 |
|
653 |
|
654 |
|
655 |
|
656 |
|
657 |
|
658 |
|
659 | throw: function(message){
|
660 | var fn = this.obj
|
661 | , err = {}
|
662 | , errorInfo = ''
|
663 | , ok = true;
|
664 |
|
665 | try {
|
666 | fn();
|
667 | ok = false;
|
668 | } catch (e) {
|
669 | err = e;
|
670 | }
|
671 |
|
672 | if (ok) {
|
673 | if ('string' == typeof message) {
|
674 | ok = message == err.message;
|
675 | } else if (message instanceof RegExp) {
|
676 | ok = message.test(err.message);
|
677 | } else if ('function' == typeof message) {
|
678 | ok = err instanceof message;
|
679 | }
|
680 |
|
681 | if (message && !ok) {
|
682 | if ('string' == typeof message) {
|
683 | errorInfo = " with a message matching '" + message + "', but got '" + err.message + "'";
|
684 | } else if (message instanceof RegExp) {
|
685 | errorInfo = " with a message matching " + message + ", but got '" + err.message + "'";
|
686 | } else if ('function' == typeof message) {
|
687 | errorInfo = " of type " + message.name + ", but got " + err.constructor.name;
|
688 | }
|
689 | }
|
690 | }
|
691 |
|
692 | this.assert(
|
693 | ok
|
694 | , 'expected an exception to be thrown' + errorInfo
|
695 | , 'expected no exception to be thrown, got "' + err.message + '"');
|
696 |
|
697 | return this;
|
698 | }
|
699 | };
|
700 |
|
701 | Assertion.prototype.instanceOf = Assertion.prototype.instanceof;
|
702 | Assertion.prototype.throwError = Assertion.prototype.throw;
|
703 |
|
704 |
|
705 |
|
706 |
|
707 |
|
708 | (function alias(name, as){
|
709 | Assertion.prototype[as] = Assertion.prototype[name];
|
710 | return alias;
|
711 | })
|
712 | ('length', 'lengthOf')
|
713 | ('keys', 'key')
|
714 | ('ownProperty', 'haveOwnProperty')
|
715 | ('above', 'greaterThan')
|
716 | ('below', 'lessThan');
|
717 |
|