Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81154 views
1
// Load modules
2
3
var Fs = require('fs');
4
var Path = require('path');
5
var Code = require('code');
6
var Hoek = require('../lib');
7
var Lab = require('lab');
8
9
10
// Declare internals
11
12
var internals = {};
13
14
15
// Test shortcuts
16
17
var lab = exports.lab = Lab.script();
18
var describe = lab.experiment;
19
var it = lab.test;
20
var expect = Code.expect;
21
22
23
var nestedObj = {
24
v: [7, 8, 9],
25
w: /^something$/igm,
26
x: {
27
a: [1, 2, 3],
28
b: 123456,
29
c: new Date(),
30
d: /hi/igm,
31
e: /hello/
32
},
33
y: 'y',
34
z: new Date(1378775452757)
35
};
36
37
var dupsArray = [nestedObj, { z: 'z' }, nestedObj];
38
var reducedDupsArray = [nestedObj, { z: 'z' }];
39
40
describe('clone()', function () {
41
42
it('clones a nested object', function (done) {
43
44
var a = nestedObj;
45
var b = Hoek.clone(a);
46
47
expect(a).to.deep.equal(b);
48
expect(a.z.getTime()).to.equal(b.z.getTime());
49
done();
50
});
51
52
it('clones a null object', function (done) {
53
54
var b = Hoek.clone(null);
55
56
expect(b).to.equal(null);
57
done();
58
});
59
60
it('should not convert undefined properties to null', function (done) {
61
62
var obj = { something: undefined };
63
var b = Hoek.clone(obj);
64
65
expect(typeof b.something).to.equal('undefined');
66
done();
67
});
68
69
it('should not throw on circular reference', function (done) {
70
71
var a = {};
72
a.x = a;
73
74
var test = function () {
75
76
var b = Hoek.clone(a);
77
};
78
79
expect(test).to.not.throw();
80
done();
81
});
82
83
it('clones circular reference', function (done) {
84
85
var x = {
86
'z': new Date()
87
};
88
x.y = x;
89
90
var b = Hoek.clone(x);
91
expect(Object.keys(b.y)).to.deep.equal(Object.keys(x));
92
expect(b.z).to.not.equal(x.z);
93
expect(b.y).to.not.equal(x.y);
94
expect(b.y.z).to.not.equal(x.y.z);
95
expect(b.y).to.equal(b);
96
expect(b.y.y.y.y).to.equal(b);
97
done();
98
});
99
100
it('clones an object with a null prototype', function (done) {
101
102
var obj = Object.create(null);
103
var b = Hoek.clone(obj);
104
105
expect(b).to.deep.equal(obj);
106
done();
107
});
108
109
it('clones deeply nested object', function (done) {
110
111
var a = {
112
x: {
113
y: {
114
a: [1, 2, 3],
115
b: 123456,
116
c: new Date(),
117
d: /hi/igm,
118
e: /hello/
119
}
120
}
121
};
122
123
var b = Hoek.clone(a);
124
125
expect(a).to.deep.equal(b);
126
expect(a.x.y.c.getTime()).to.equal(b.x.y.c.getTime());
127
done();
128
});
129
130
it('clones arrays', function (done) {
131
132
var a = [1, 2, 3];
133
134
var b = Hoek.clone(a);
135
136
expect(a).to.deep.equal(b);
137
done();
138
});
139
140
it('performs actual copy for shallow keys (no pass by reference)', function (done) {
141
142
var x = Hoek.clone(nestedObj);
143
var y = Hoek.clone(nestedObj);
144
145
// Date
146
expect(x.z).to.not.equal(nestedObj.z);
147
expect(x.z).to.not.equal(y.z);
148
149
// Regex
150
expect(x.w).to.not.equal(nestedObj.w);
151
expect(x.w).to.not.equal(y.w);
152
153
// Array
154
expect(x.v).to.not.equal(nestedObj.v);
155
expect(x.v).to.not.equal(y.v);
156
157
// Immutable(s)
158
x.y = 5;
159
expect(x.y).to.not.equal(nestedObj.y);
160
expect(x.y).to.not.equal(y.y);
161
162
done();
163
});
164
165
it('performs actual copy for deep keys (no pass by reference)', function (done) {
166
167
var x = Hoek.clone(nestedObj);
168
var y = Hoek.clone(nestedObj);
169
170
expect(x.x.c).to.not.equal(nestedObj.x.c);
171
expect(x.x.c).to.not.equal(y.x.c);
172
173
expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime());
174
expect(x.x.c.getTime()).to.equal(y.x.c.getTime());
175
done();
176
});
177
178
it('copies functions with properties', function (done) {
179
180
var a = {
181
x: function () {
182
183
return 1;
184
},
185
y: {}
186
};
187
a.x.z = 'string in function';
188
a.x.v = function () {
189
190
return 2;
191
};
192
a.y.u = a.x;
193
194
var b = Hoek.clone(a);
195
expect(b.x()).to.equal(1);
196
expect(b.x.v()).to.equal(2);
197
expect(b.y.u).to.equal(b.x);
198
expect(b.x.z).to.equal('string in function');
199
done();
200
});
201
202
it('should copy a buffer', function (done) {
203
204
var tls = {
205
key: new Buffer([1, 2, 3, 4, 5]),
206
cert: new Buffer([1, 2, 3, 4, 5, 6, 10])
207
};
208
209
var copiedTls = Hoek.clone(tls);
210
expect(Buffer.isBuffer(copiedTls.key)).to.equal(true);
211
expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key));
212
expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true);
213
expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert));
214
done();
215
});
216
217
it('clones an object with a prototype', function (done) {
218
219
var Obj = function () {
220
221
this.a = 5;
222
};
223
224
Obj.prototype.b = function () {
225
226
return 'c';
227
};
228
229
var a = new Obj();
230
var b = Hoek.clone(a);
231
232
expect(b.a).to.equal(5);
233
expect(b.b()).to.equal('c');
234
expect(a).to.deep.equal(b);
235
done();
236
});
237
238
it('reuses cloned Date object', function (done) {
239
240
var obj = {
241
a: new Date()
242
};
243
244
obj.b = obj.a;
245
246
var copy = Hoek.clone(obj);
247
expect(copy.a).to.equal(copy.b);
248
done();
249
});
250
251
it('shallow copies an object with a prototype and isImmutable flag', function (done) {
252
253
var Obj = function () {
254
255
this.value = 5;
256
};
257
258
Obj.prototype.b = function () {
259
260
return 'c';
261
};
262
263
Obj.prototype.isImmutable = true;
264
265
var obj = {
266
a: new Obj()
267
};
268
269
var copy = Hoek.clone(obj);
270
271
expect(obj.a.value).to.equal(5);
272
expect(copy.a.value).to.equal(5);
273
expect(copy.a.b()).to.equal('c');
274
expect(obj.a).to.equal(copy.a);
275
done();
276
});
277
278
it('clones an object with property getter without executing it', function (done) {
279
280
var obj = {};
281
var value = 1;
282
var execCount = 0;
283
284
Object.defineProperty(obj, 'test', {
285
enumerable: true,
286
configurable: true,
287
get: function () {
288
289
++execCount;
290
return value;
291
}
292
});
293
294
var copy = Hoek.clone(obj);
295
expect(execCount).to.equal(0);
296
expect(copy.test).to.equal(1);
297
expect(execCount).to.equal(1);
298
done();
299
});
300
301
it('clones an object with property getter and setter', function (done) {
302
303
var obj = {
304
_test: 0
305
};
306
307
Object.defineProperty(obj, 'test', {
308
enumerable: true,
309
configurable: true,
310
get: function () {
311
312
return this._test;
313
},
314
set: function (value) {
315
316
this._test = value - 1;
317
}
318
});
319
320
var copy = Hoek.clone(obj);
321
expect(copy.test).to.equal(0);
322
copy.test = 5;
323
expect(copy.test).to.equal(4);
324
done();
325
});
326
327
it('clones an object with only property setter', function (done) {
328
329
var obj = {
330
_test: 0
331
};
332
333
Object.defineProperty(obj, 'test', {
334
enumerable: true,
335
configurable: true,
336
set: function (value) {
337
338
this._test = value - 1;
339
}
340
});
341
342
var copy = Hoek.clone(obj);
343
expect(copy._test).to.equal(0);
344
copy.test = 5;
345
expect(copy._test).to.equal(4);
346
done();
347
});
348
349
it('clones an object with non-enumerable properties', function (done) {
350
351
var obj = {
352
_test: 0
353
};
354
355
Object.defineProperty(obj, 'test', {
356
enumerable: false,
357
configurable: true,
358
set: function (value) {
359
360
this._test = value - 1;
361
}
362
});
363
364
var copy = Hoek.clone(obj);
365
expect(copy._test).to.equal(0);
366
copy.test = 5;
367
expect(copy._test).to.equal(4);
368
done();
369
});
370
});
371
372
describe('merge()', function () {
373
374
it('deep copies source items', function (done) {
375
376
var target = {
377
b: 3,
378
d: []
379
};
380
381
var source = {
382
c: {
383
d: 1
384
},
385
d: [{ e: 1 }]
386
};
387
388
Hoek.merge(target, source);
389
expect(target.c).to.not.equal(source.c);
390
expect(target.c).to.deep.equal(source.c);
391
expect(target.d).to.not.equal(source.d);
392
expect(target.d[0]).to.not.equal(source.d[0]);
393
expect(target.d).to.deep.equal(source.d);
394
done();
395
});
396
397
it('merges array over an object', function (done) {
398
399
var a = {
400
x: ['n', 'm']
401
};
402
403
var b = {
404
x: {
405
n: '1',
406
m: '2'
407
}
408
};
409
410
Hoek.merge(b, a);
411
expect(a.x[0]).to.equal('n');
412
expect(a.x.n).to.not.exist();
413
done();
414
});
415
416
it('merges object over an array', function (done) {
417
418
var a = {
419
x: ['n', 'm']
420
};
421
422
var b = {
423
x: {
424
n: '1',
425
m: '2'
426
}
427
};
428
429
Hoek.merge(a, b);
430
expect(a.x.n).to.equal('1');
431
expect(a.x[0]).to.not.exist();
432
done();
433
});
434
435
it('does not throw if source is null', function (done) {
436
437
var a = {};
438
var b = null;
439
var c = null;
440
441
expect(function () {
442
443
c = Hoek.merge(a, b);
444
}).to.not.throw();
445
446
expect(c).to.equal(a);
447
done();
448
});
449
450
it('does not throw if source is undefined', function (done) {
451
452
var a = {};
453
var b;
454
var c = null;
455
456
expect(function () {
457
458
c = Hoek.merge(a, b);
459
}).to.not.throw();
460
461
expect(c).to.equal(a);
462
done();
463
});
464
465
it('throws if source is not an object', function (done) {
466
467
expect(function () {
468
469
var a = {};
470
var b = 0;
471
472
Hoek.merge(a, b);
473
}).to.throw('Invalid source value: must be null, undefined, or an object');
474
done();
475
});
476
477
it('throws if target is not an object', function (done) {
478
479
expect(function () {
480
481
var a = 0;
482
var b = {};
483
484
Hoek.merge(a, b);
485
}).to.throw('Invalid target value: must be an object');
486
done();
487
});
488
489
it('throws if target is not an array and source is', function (done) {
490
491
expect(function () {
492
493
var a = {};
494
var b = [1, 2];
495
496
Hoek.merge(a, b);
497
}).to.throw('Cannot merge array onto an object');
498
done();
499
});
500
501
it('returns the same object when merging arrays', function (done) {
502
503
var a = [];
504
var b = [1, 2];
505
506
expect(Hoek.merge(a, b)).to.equal(a);
507
done();
508
});
509
510
it('combines an empty object with a non-empty object', function (done) {
511
512
var a = {};
513
var b = nestedObj;
514
515
var c = Hoek.merge(a, b);
516
expect(a).to.deep.equal(b);
517
expect(c).to.deep.equal(b);
518
done();
519
});
520
521
it('overrides values in target', function (done) {
522
523
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
524
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
525
526
var c = Hoek.merge(a, b);
527
expect(c.x).to.equal(null);
528
expect(c.y).to.equal(2);
529
expect(c.z).to.equal(4);
530
expect(c.v).to.equal(0);
531
expect(c.m).to.equal('123');
532
expect(c.t).to.deep.equal({ u: 6 });
533
done();
534
});
535
536
it('overrides values in target (flip)', function (done) {
537
538
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
539
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
540
541
var d = Hoek.merge(b, a);
542
expect(d.x).to.equal(1);
543
expect(d.y).to.equal(2);
544
expect(d.z).to.equal(3);
545
expect(d.v).to.equal(5);
546
expect(d.m).to.equal('abc');
547
expect(d.t).to.deep.equal('test');
548
done();
549
});
550
551
it('retains Date properties', function (done) {
552
553
var a = { x: new Date(1378776452757) };
554
555
var b = Hoek.merge({}, a);
556
expect(a.x.getTime()).to.equal(b.x.getTime());
557
done();
558
});
559
560
it('retains Date properties when merging keys', function (done) {
561
562
var a = { x: new Date(1378776452757) };
563
564
var b = Hoek.merge({ x: {} }, a);
565
expect(a.x.getTime()).to.equal(b.x.getTime());
566
done();
567
});
568
569
it('overrides Buffer', function (done) {
570
571
var a = { x: new Buffer('abc') };
572
573
var b = Hoek.merge({ x: {} }, a);
574
expect(a.x.toString()).to.equal('abc');
575
done();
576
});
577
});
578
579
describe('applyToDefaults()', function () {
580
581
var defaults = {
582
a: 1,
583
b: 2,
584
c: {
585
d: 3,
586
e: [5, 6]
587
},
588
f: 6,
589
g: 'test'
590
};
591
592
it('throws when target is null', function (done) {
593
594
expect(function () {
595
596
Hoek.applyToDefaults(null, {});
597
}).to.throw('Invalid defaults value: must be an object');
598
done();
599
});
600
601
it('returns null if options is false', function (done) {
602
603
var result = Hoek.applyToDefaults(defaults, false);
604
expect(result).to.equal(null);
605
done();
606
});
607
608
it('returns null if options is null', function (done) {
609
610
var result = Hoek.applyToDefaults(defaults, null);
611
expect(result).to.equal(null);
612
done();
613
});
614
615
it('returns null if options is undefined', function (done) {
616
617
var result = Hoek.applyToDefaults(defaults, undefined);
618
expect(result).to.equal(null);
619
done();
620
});
621
622
it('returns a copy of defaults if options is true', function (done) {
623
624
var result = Hoek.applyToDefaults(defaults, true);
625
expect(result).to.deep.equal(defaults);
626
done();
627
});
628
629
it('applies object to defaults', function (done) {
630
631
var obj = {
632
a: null,
633
c: {
634
e: [4]
635
},
636
f: 0,
637
g: {
638
h: 5
639
}
640
};
641
642
var result = Hoek.applyToDefaults(defaults, obj);
643
expect(result.c.e).to.deep.equal([4]);
644
expect(result.a).to.equal(1);
645
expect(result.b).to.equal(2);
646
expect(result.f).to.equal(0);
647
expect(result.g).to.deep.equal({ h: 5 });
648
done();
649
});
650
651
it('applies object to defaults with null', function (done) {
652
653
var obj = {
654
a: null,
655
c: {
656
e: [4]
657
},
658
f: 0,
659
g: {
660
h: 5
661
}
662
};
663
664
var result = Hoek.applyToDefaults(defaults, obj, true);
665
expect(result.c.e).to.deep.equal([4]);
666
expect(result.a).to.equal(null);
667
expect(result.b).to.equal(2);
668
expect(result.f).to.equal(0);
669
expect(result.g).to.deep.equal({ h: 5 });
670
done();
671
});
672
});
673
674
describe('cloneWithShallow()', function () {
675
676
it('deep clones except for listed keys', function (done) {
677
678
var source = {
679
a: {
680
b: 5
681
},
682
c: {
683
d: 6
684
}
685
};
686
687
var copy = Hoek.cloneWithShallow(source, ['c']);
688
expect(copy).to.deep.equal(source);
689
expect(copy).to.not.equal(source);
690
expect(copy.a).to.not.equal(source.a);
691
expect(copy.b).to.equal(source.b);
692
done();
693
});
694
695
it('returns immutable value', function (done) {
696
697
expect(Hoek.cloneWithShallow(5)).to.equal(5);
698
done();
699
});
700
701
it('returns null value', function (done) {
702
703
expect(Hoek.cloneWithShallow(null)).to.equal(null);
704
done();
705
});
706
707
it('returns undefined value', function (done) {
708
709
expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined);
710
done();
711
});
712
713
it('deep clones except for listed keys (including missing keys)', function (done) {
714
715
var source = {
716
a: {
717
b: 5
718
},
719
c: {
720
d: 6
721
}
722
};
723
724
var copy = Hoek.cloneWithShallow(source, ['c', 'v']);
725
expect(copy).to.deep.equal(source);
726
expect(copy).to.not.equal(source);
727
expect(copy.a).to.not.equal(source.a);
728
expect(copy.b).to.equal(source.b);
729
done();
730
});
731
});
732
733
describe('applyToDefaultsWithShallow()', function () {
734
735
it('shallow copies the listed keys from options without merging', function (done) {
736
737
var defaults = {
738
a: {
739
b: 5,
740
e: 3
741
},
742
c: {
743
d: 7,
744
g: 1
745
}
746
};
747
748
var options = {
749
a: {
750
b: 4
751
},
752
c: {
753
d: 6,
754
f: 7
755
}
756
};
757
758
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['a']);
759
expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: 1, f: 7 } });
760
expect(merged.a).to.equal(options.a);
761
expect(merged.a).to.not.equal(defaults.a);
762
expect(merged.c).to.not.equal(options.c);
763
expect(merged.c).to.not.equal(defaults.c);
764
done();
765
});
766
767
it('shallow copies the nested keys (override)', function (done) {
768
769
var defaults = {
770
a: {
771
b: 5
772
},
773
c: {
774
d: 7,
775
g: 1
776
}
777
};
778
779
var options = {
780
a: {
781
b: 4
782
},
783
c: {
784
d: 6,
785
g: {
786
h: 8
787
}
788
}
789
};
790
791
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
792
expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: { h: 8 } } });
793
expect(merged.c.g).to.equal(options.c.g);
794
done();
795
});
796
797
it('shallow copies the nested keys (missing)', function (done) {
798
799
var defaults = {
800
a: {
801
b: 5
802
}
803
};
804
805
var options = {
806
a: {
807
b: 4
808
},
809
c: {
810
g: {
811
h: 8
812
}
813
}
814
};
815
816
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
817
expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
818
expect(merged.c.g).to.equal(options.c.g);
819
done();
820
});
821
822
it('shallow copies the nested keys (override)', function (done) {
823
824
var defaults = {
825
a: {
826
b: 5
827
},
828
c: {
829
g: {
830
d: 7
831
}
832
}
833
};
834
835
var options = {
836
a: {
837
b: 4
838
},
839
c: {
840
g: {
841
h: 8
842
}
843
}
844
};
845
846
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
847
expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
848
expect(merged.c.g).to.equal(options.c.g);
849
done();
850
});
851
852
it('shallow copies the nested keys (deeper)', function (done) {
853
854
var defaults = {
855
a: {
856
b: 5
857
}
858
};
859
860
var options = {
861
a: {
862
b: 4
863
},
864
c: {
865
g: {
866
r: {
867
h: 8
868
}
869
}
870
}
871
};
872
873
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g.r']);
874
expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
875
expect(merged.c.g.r).to.equal(options.c.g.r);
876
done();
877
});
878
879
it('shallow copies the nested keys (not present)', function (done) {
880
881
var defaults = {
882
a: {
883
b: 5
884
}
885
};
886
887
var options = {
888
a: {
889
b: 4
890
},
891
c: {
892
g: {
893
r: {
894
h: 8
895
}
896
}
897
}
898
};
899
900
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['x.y']);
901
expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
902
done();
903
});
904
905
it('shallow copies the listed keys in the defaults', function (done) {
906
907
var defaults = {
908
a: {
909
b: 1
910
}
911
};
912
913
var merged = Hoek.applyToDefaultsWithShallow(defaults, {}, ['a']);
914
expect(merged.a).to.equal(defaults.a);
915
done();
916
});
917
918
it('shallow copies the listed keys in the defaults (true)', function (done) {
919
920
var defaults = {
921
a: {
922
b: 1
923
}
924
};
925
926
var merged = Hoek.applyToDefaultsWithShallow(defaults, true, ['a']);
927
expect(merged.a).to.equal(defaults.a);
928
done();
929
});
930
931
it('returns null on false', function (done) {
932
933
var defaults = {
934
a: {
935
b: 1
936
}
937
};
938
939
var merged = Hoek.applyToDefaultsWithShallow(defaults, false, ['a']);
940
expect(merged).to.equal(null);
941
done();
942
});
943
944
it('throws on missing defaults', function (done) {
945
946
expect(function () {
947
948
Hoek.applyToDefaultsWithShallow(null, {}, ['a']);
949
}).to.throw('Invalid defaults value: must be an object');
950
done();
951
});
952
953
it('throws on invalid defaults', function (done) {
954
955
expect(function () {
956
957
Hoek.applyToDefaultsWithShallow('abc', {}, ['a']);
958
}).to.throw('Invalid defaults value: must be an object');
959
done();
960
});
961
962
it('throws on invalid options', function (done) {
963
964
expect(function () {
965
966
Hoek.applyToDefaultsWithShallow({}, 'abc', ['a']);
967
}).to.throw('Invalid options value: must be true, falsy or an object');
968
done();
969
});
970
971
it('throws on missing keys', function (done) {
972
973
expect(function () {
974
975
Hoek.applyToDefaultsWithShallow({}, true);
976
}).to.throw('Invalid keys');
977
done();
978
});
979
980
it('throws on invalid keys', function (done) {
981
982
expect(function () {
983
984
Hoek.applyToDefaultsWithShallow({}, true, 'a');
985
}).to.throw('Invalid keys');
986
done();
987
});
988
});
989
990
describe('deepEqual()', function () {
991
992
it('compares simple values', function (done) {
993
994
expect(Hoek.deepEqual('x', 'x')).to.be.true();
995
expect(Hoek.deepEqual('x', 'y')).to.be.false();
996
expect(Hoek.deepEqual('x1', 'x')).to.be.false();
997
expect(Hoek.deepEqual(-0, +0)).to.be.false();
998
expect(Hoek.deepEqual(-0, -0)).to.be.true();
999
expect(Hoek.deepEqual(+0, +0)).to.be.true();
1000
expect(Hoek.deepEqual(+0, -0)).to.be.false();
1001
expect(Hoek.deepEqual(1, 1)).to.be.true();
1002
expect(Hoek.deepEqual(0, 0)).to.be.true();
1003
expect(Hoek.deepEqual(-1, 1)).to.be.false();
1004
expect(Hoek.deepEqual(NaN, 0)).to.be.false();
1005
expect(Hoek.deepEqual(NaN, NaN)).to.be.true();
1006
done();
1007
});
1008
1009
it('compares different types', function (done) {
1010
1011
expect(Hoek.deepEqual([], 5)).to.be.false();
1012
expect(Hoek.deepEqual(5, [])).to.be.false();
1013
expect(Hoek.deepEqual({}, null)).to.be.false();
1014
expect(Hoek.deepEqual(null, {})).to.be.false();
1015
expect(Hoek.deepEqual('abc', {})).to.be.false();
1016
expect(Hoek.deepEqual({}, 'abc')).to.be.false();
1017
done();
1018
});
1019
1020
it('compares empty structures', function (done) {
1021
1022
expect(Hoek.deepEqual([], [])).to.be.true();
1023
expect(Hoek.deepEqual({}, {})).to.be.true();
1024
expect(Hoek.deepEqual([], {})).to.be.false();
1025
done();
1026
});
1027
1028
it('compares empty arguments object', function (done) {
1029
1030
var compare = function () {
1031
1032
expect(Hoek.deepEqual([], arguments)).to.be.false();
1033
};
1034
1035
compare();
1036
done();
1037
});
1038
1039
it('compares empty arguments objects', function (done) {
1040
1041
var compare = function () {
1042
1043
var arg1 = arguments;
1044
1045
var inner = function () {
1046
1047
expect(Hoek.deepEqual(arg1, arguments)).to.be.false(); // callee is not the same
1048
};
1049
1050
inner();
1051
};
1052
1053
compare();
1054
done();
1055
});
1056
1057
it('compares dates', function (done) {
1058
1059
expect(Hoek.deepEqual(new Date(2015, 1, 1), new Date(2015, 1, 1))).to.be.true();
1060
expect(Hoek.deepEqual(new Date(100), new Date(101))).to.be.false();
1061
expect(Hoek.deepEqual(new Date(), {})).to.be.false();
1062
done();
1063
});
1064
1065
it('compares regular expressions', function (done) {
1066
1067
expect(Hoek.deepEqual(/\s/, new RegExp('\\\s'))).to.be.true();
1068
expect(Hoek.deepEqual(/\s/g, /\s/g)).to.be.true();
1069
expect(Hoek.deepEqual(/a/, {})).to.be.false();
1070
expect(Hoek.deepEqual(/\s/g, /\s/i)).to.be.false();
1071
expect(Hoek.deepEqual(/a/g, /b/g)).to.be.false();
1072
done();
1073
});
1074
1075
it('compares arrays', function (done) {
1076
1077
expect(Hoek.deepEqual([[1]], [[1]])).to.be.true();
1078
expect(Hoek.deepEqual([1, 2, 3], [1, 2, 3])).to.be.true();
1079
expect(Hoek.deepEqual([1, 2, 3], [1, 3, 2])).to.be.false();
1080
expect(Hoek.deepEqual([1, 2, 3], [1, 2])).to.be.false();
1081
expect(Hoek.deepEqual([1], [1])).to.be.true();
1082
done();
1083
});
1084
1085
it('compares buffers', function (done) {
1086
1087
expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2, 3]))).to.be.true();
1088
expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 3, 2]))).to.be.false();
1089
expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2]))).to.be.false();
1090
expect(Hoek.deepEqual(new Buffer([1, 2, 3]), {})).to.be.false();
1091
expect(Hoek.deepEqual(new Buffer([1, 2, 3]), [1, 2, 3])).to.be.false();
1092
done();
1093
});
1094
1095
it('compares objects', function (done) {
1096
1097
expect(Hoek.deepEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).to.be.true();
1098
expect(Hoek.deepEqual({ foo: 'bar' }, { foo: 'baz' })).to.be.false();
1099
expect(Hoek.deepEqual({ foo: { bar: 'foo' } }, { foo: { bar: 'baz' } })).to.be.false();
1100
done();
1101
});
1102
1103
it('handles circular dependency', function (done) {
1104
1105
var a = {};
1106
a.x = a;
1107
1108
var b = Hoek.clone(a);
1109
expect(Hoek.deepEqual(a, b)).to.be.true();
1110
done();
1111
});
1112
1113
it('compares an object with property getter without executing it', function (done) {
1114
1115
var obj = {};
1116
var value = 1;
1117
var execCount = 0;
1118
1119
Object.defineProperty(obj, 'test', {
1120
enumerable: true,
1121
configurable: true,
1122
get: function () {
1123
1124
++execCount;
1125
return value;
1126
}
1127
});
1128
1129
var copy = Hoek.clone(obj);
1130
expect(Hoek.deepEqual(obj, copy)).to.be.true();
1131
expect(execCount).to.equal(0);
1132
expect(copy.test).to.equal(1);
1133
expect(execCount).to.equal(1);
1134
done();
1135
});
1136
1137
it('compares objects with property getters', function (done) {
1138
1139
var obj = {};
1140
Object.defineProperty(obj, 'test', {
1141
enumerable: true,
1142
configurable: true,
1143
get: function () {
1144
1145
return 1;
1146
}
1147
});
1148
1149
var ref = {};
1150
Object.defineProperty(ref, 'test', {
1151
enumerable: true,
1152
configurable: true,
1153
get: function () {
1154
1155
return 2;
1156
}
1157
});
1158
1159
expect(Hoek.deepEqual(obj, ref)).to.be.false();
1160
done();
1161
});
1162
1163
it('compares object prototypes', function (done) {
1164
1165
var Obj = function () {
1166
1167
this.a = 5;
1168
};
1169
1170
Obj.prototype.b = function () {
1171
1172
return this.a;
1173
};
1174
1175
var Ref = function () {
1176
1177
this.a = 5;
1178
};
1179
1180
Ref.prototype.b = function () {
1181
1182
return this.a;
1183
};
1184
1185
expect(Hoek.deepEqual(new Obj(), new Ref())).to.be.false();
1186
expect(Hoek.deepEqual(new Obj(), new Obj())).to.be.true();
1187
expect(Hoek.deepEqual(new Ref(), new Ref())).to.be.true();
1188
done();
1189
});
1190
1191
it('compares plain objects', function (done) {
1192
1193
var a = Object.create(null);
1194
var b = Object.create(null);
1195
1196
a.b = 'c';
1197
b.b = 'c';
1198
1199
expect(Hoek.deepEqual(a, b)).to.be.true();
1200
expect(Hoek.deepEqual(a, { b: 'c' })).to.be.false();
1201
done();
1202
});
1203
1204
it('compares an object with an empty object', function (done) {
1205
1206
var a = { a: 1, b: 2 };
1207
1208
expect(Hoek.deepEqual({}, a)).to.be.false();
1209
expect(Hoek.deepEqual(a, {})).to.be.false();
1210
done();
1211
});
1212
1213
it('compares an object ignoring the prototype', function (done) {
1214
1215
var a = Object.create(null);
1216
var b = {};
1217
1218
expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
1219
done();
1220
});
1221
1222
it('compares an object ignoring the prototype recursively', function (done) {
1223
1224
var a = [Object.create(null)];
1225
var b = [{}];
1226
1227
expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
1228
done();
1229
});
1230
});
1231
1232
describe('unique()', function () {
1233
1234
it('ensures uniqueness within array of objects based on subkey', function (done) {
1235
1236
var a = Hoek.unique(dupsArray, 'x');
1237
expect(a).to.deep.equal(reducedDupsArray);
1238
done();
1239
});
1240
1241
it('removes duplicated without key', function (done) {
1242
1243
expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]);
1244
done();
1245
});
1246
});
1247
1248
describe('mapToObject()', function () {
1249
1250
it('returns null on null array', function (done) {
1251
1252
var a = Hoek.mapToObject(null);
1253
expect(a).to.equal(null);
1254
done();
1255
});
1256
1257
it('converts basic array to existential object', function (done) {
1258
1259
var keys = [1, 2, 3, 4];
1260
var a = Hoek.mapToObject(keys);
1261
for (var i in keys) {
1262
expect(a[keys[i]]).to.equal(true);
1263
}
1264
done();
1265
});
1266
1267
it('converts array of objects to existential object', function (done) {
1268
1269
var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }];
1270
var subkey = 'x';
1271
var a = Hoek.mapToObject(keys, subkey);
1272
expect(a).to.deep.equal({ 1: true, 2: true, 3: true });
1273
done();
1274
});
1275
});
1276
1277
describe('intersect()', function () {
1278
1279
it('returns the common objects of two arrays', function (done) {
1280
1281
var array1 = [1, 2, 3, 4, 4, 5, 5];
1282
var array2 = [5, 4, 5, 6, 7];
1283
var common = Hoek.intersect(array1, array2);
1284
expect(common.length).to.equal(2);
1285
done();
1286
});
1287
1288
it('returns just the first common object of two arrays', function (done) {
1289
1290
var array1 = [1, 2, 3, 4, 4, 5, 5];
1291
var array2 = [5, 4, 5, 6, 7];
1292
var common = Hoek.intersect(array1, array2, true);
1293
expect(common).to.equal(5);
1294
done();
1295
});
1296
1297
it('returns null when no common and returning just the first common object of two arrays', function (done) {
1298
1299
var array1 = [1, 2, 3, 4, 4, 5, 5];
1300
var array2 = [6, 7];
1301
var common = Hoek.intersect(array1, array2, true);
1302
expect(common).to.equal(null);
1303
done();
1304
});
1305
1306
it('returns an empty array if either input is null', function (done) {
1307
1308
expect(Hoek.intersect([1], null).length).to.equal(0);
1309
expect(Hoek.intersect(null, [1]).length).to.equal(0);
1310
done();
1311
});
1312
1313
it('returns the common objects of object and array', function (done) {
1314
1315
var array1 = [1, 2, 3, 4, 4, 5, 5];
1316
var array2 = [5, 4, 5, 6, 7];
1317
var common = Hoek.intersect(Hoek.mapToObject(array1), array2);
1318
expect(common.length).to.equal(2);
1319
done();
1320
});
1321
});
1322
1323
describe('contain()', function () {
1324
1325
it('tests strings', function (done) {
1326
1327
expect(Hoek.contain('abc', 'ab')).to.be.true();
1328
expect(Hoek.contain('abc', 'abc', { only: true })).to.be.true();
1329
expect(Hoek.contain('aaa', 'a', { only: true })).to.be.true();
1330
expect(Hoek.contain('abc', 'b', { once: true })).to.be.true();
1331
expect(Hoek.contain('abc', ['a', 'c'])).to.be.true();
1332
expect(Hoek.contain('abc', ['a', 'd'], { part: true })).to.be.true();
1333
1334
expect(Hoek.contain('abc', 'ac')).to.be.false();
1335
expect(Hoek.contain('abcd', 'abc', { only: true })).to.be.false();
1336
expect(Hoek.contain('aab', 'a', { only: true })).to.be.false();
1337
expect(Hoek.contain('abb', 'b', { once: true })).to.be.false();
1338
expect(Hoek.contain('abc', ['a', 'd'])).to.be.false();
1339
expect(Hoek.contain('abc', ['ab', 'bc'])).to.be.false(); // Overlapping values not supported
1340
done();
1341
});
1342
1343
it('tests arrays', function (done) {
1344
1345
expect(Hoek.contain([1, 2, 3], 1)).to.be.true();
1346
expect(Hoek.contain([{ a: 1 }], { a: 1 }, { deep: true })).to.be.true();
1347
expect(Hoek.contain([1, 2, 3], [1, 2])).to.be.true();
1348
expect(Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true })).to.be.true();
1349
expect(Hoek.contain([1, 1, 2], [1, 2], { only: true })).to.be.true();
1350
expect(Hoek.contain([1, 2], [1, 2], { once: true })).to.be.true();
1351
expect(Hoek.contain([1, 2, 3], [1, 4], { part: true })).to.be.true();
1352
expect(Hoek.contain([[1], [2]], [[1]], { deep: true })).to.be.true();
1353
1354
expect(Hoek.contain([1, 2, 3], 4)).to.be.false();
1355
expect(Hoek.contain([{ a: 1 }], { a: 2 }, { deep: true })).to.be.false();
1356
expect(Hoek.contain([{ a: 1 }], { a: 1 })).to.be.false();
1357
expect(Hoek.contain([1, 2, 3], [4, 5])).to.be.false();
1358
expect(Hoek.contain([[3], [2]], [[1]])).to.be.false();
1359
expect(Hoek.contain([[1], [2]], [[1]])).to.be.false();
1360
expect(Hoek.contain([{ a: 1 }], [{ a: 2 }], { deep: true })).to.be.false();
1361
expect(Hoek.contain([1, 3, 2], [1, 2], { only: true })).to.be.false();
1362
expect(Hoek.contain([1, 2, 2], [1, 2], { once: true })).to.be.false();
1363
expect(Hoek.contain([0, 2, 3], [1, 4], { part: true })).to.be.false();
1364
done();
1365
});
1366
1367
it('tests objects', function (done) {
1368
1369
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'a')).to.be.true();
1370
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'c'])).to.be.true();
1371
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'b', 'c'], { only: true })).to.be.true();
1372
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1 })).to.be.true();
1373
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 })).to.be.true();
1374
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true })).to.be.true();
1375
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, { only: true })).to.be.true();
1376
expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] }, { deep: true })).to.be.true();
1377
expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true })).to.be.true();
1378
expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: true })).to.be.true();
1379
expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: false })).to.be.false();
1380
expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: true })).to.be.false();
1381
expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: false })).to.be.true();
1382
1383
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'd')).to.be.false();
1384
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'd'])).to.be.false();
1385
expect(Hoek.contain({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'b', 'c'], { only: true })).to.be.false();
1386
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2 })).to.be.false();
1387
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2, b: 2 }, { part: true })).to.be.false(); // part does not ignore bad value
1388
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 3 })).to.be.false();
1389
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 })).to.be.false();
1390
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 }, { only: true })).to.be.false();
1391
expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] })).to.be.false();
1392
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } })).to.be.false();
1393
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true })).to.be.true();
1394
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: true })).to.be.false();
1395
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: false })).to.be.true();
1396
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: true })).to.be.true();
1397
expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: false })).to.be.false();
1398
1399
// Getter check
1400
var Foo = function (bar) {
1401
1402
this.bar = bar;
1403
};
1404
1405
Object.defineProperty(Foo.prototype, 'baz', {
1406
enumerable: true,
1407
get: function () {
1408
1409
return this.bar;
1410
}
1411
});
1412
1413
expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true })).to.be.true();
1414
expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true, part: true })).to.be.true();
1415
expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true })).to.be.true();
1416
expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, only: true })).to.be.false();
1417
expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: false })).to.be.false();
1418
expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: true })).to.be.true();
1419
1420
done();
1421
});
1422
});
1423
1424
describe('flatten()', function () {
1425
1426
it('returns a flat array', function (done) {
1427
1428
var result = Hoek.flatten([1, 2, [3, 4, [5, 6], [7], 8], [9], [10, [11, 12]], 13]);
1429
expect(result.length).to.equal(13);
1430
expect(result).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
1431
done();
1432
});
1433
});
1434
1435
describe('reach()', function () {
1436
1437
var obj = {
1438
a: {
1439
b: {
1440
c: {
1441
d: 1,
1442
e: 2
1443
},
1444
f: 'hello'
1445
},
1446
g: {
1447
h: 3
1448
}
1449
},
1450
i: function () { },
1451
j: null,
1452
k: [4, 8, 9, 1]
1453
};
1454
1455
obj.i.x = 5;
1456
1457
it('returns first value of array', function (done) {
1458
1459
expect(Hoek.reach(obj, 'k.0')).to.equal(4);
1460
done();
1461
});
1462
1463
it('returns last value of array using negative index', function (done) {
1464
1465
expect(Hoek.reach(obj, 'k.-2')).to.equal(9);
1466
done();
1467
});
1468
1469
it('returns a valid member', function (done) {
1470
1471
expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1);
1472
done();
1473
});
1474
1475
it('returns a valid member with separator override', function (done) {
1476
1477
expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1);
1478
done();
1479
});
1480
1481
it('returns undefined on null object', function (done) {
1482
1483
expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined);
1484
done();
1485
});
1486
1487
it('returns undefined on missing object member', function (done) {
1488
1489
expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined);
1490
done();
1491
});
1492
1493
it('returns undefined on missing function member', function (done) {
1494
1495
expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined);
1496
done();
1497
});
1498
1499
it('throws on missing member in strict mode', function (done) {
1500
1501
expect(function () {
1502
1503
Hoek.reach(obj, 'a.b.c.o.x', { strict: true });
1504
}).to.throw('Missing segment o in reach path a.b.c.o.x');
1505
1506
done();
1507
});
1508
1509
it('returns undefined on invalid member', function (done) {
1510
1511
expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined);
1512
done();
1513
});
1514
1515
it('returns function member', function (done) {
1516
1517
expect(typeof Hoek.reach(obj, 'i')).to.equal('function');
1518
done();
1519
});
1520
1521
it('returns function property', function (done) {
1522
1523
expect(Hoek.reach(obj, 'i.x')).to.equal(5);
1524
done();
1525
});
1526
1527
it('returns null', function (done) {
1528
1529
expect(Hoek.reach(obj, 'j')).to.equal(null);
1530
done();
1531
});
1532
1533
it('throws on function property when functions not allowed', function (done) {
1534
1535
expect(function () {
1536
1537
Hoek.reach(obj, 'i.x', { functions: false });
1538
}).to.throw('Invalid segment x in reach path i.x');
1539
1540
done();
1541
});
1542
1543
it('will return a default value if property is not found', function (done) {
1544
1545
expect(Hoek.reach(obj, 'a.b.q', { default: 'defaultValue' })).to.equal('defaultValue');
1546
done();
1547
});
1548
1549
it('will return a default value if path is not found', function (done) {
1550
1551
expect(Hoek.reach(obj, 'q', { default: 'defaultValue' })).to.equal('defaultValue');
1552
done();
1553
});
1554
1555
it('allows a falsey value to be used as the default value', function (done) {
1556
1557
expect(Hoek.reach(obj, 'q', { default: '' })).to.equal('');
1558
done();
1559
});
1560
});
1561
1562
describe('reachTemplate()', function () {
1563
1564
it('applies object to template', function (done) {
1565
1566
var obj = {
1567
a: {
1568
b: {
1569
c: {
1570
d: 1
1571
}
1572
}
1573
},
1574
j: null,
1575
k: [4, 8, 9, 1]
1576
};
1577
1578
var template = '{k.0}:{k.-2}:{a.b.c.d}:{x.y}:{j}';
1579
1580
expect(Hoek.reachTemplate(obj, template)).to.equal('4:9:1::');
1581
done();
1582
});
1583
1584
it('applies object to template (options)', function (done) {
1585
1586
var obj = {
1587
a: {
1588
b: {
1589
c: {
1590
d: 1
1591
}
1592
}
1593
},
1594
j: null,
1595
k: [4, 8, 9, 1]
1596
};
1597
1598
var template = '{k/0}:{k/-2}:{a/b/c/d}:{x/y}:{j}';
1599
1600
expect(Hoek.reachTemplate(obj, template, '/')).to.equal('4:9:1::');
1601
done();
1602
});
1603
});
1604
1605
describe('callStack()', function () {
1606
1607
it('returns the full call stack', function (done) {
1608
1609
var stack = Hoek.callStack();
1610
expect(stack[0][0]).to.contain('index.js');
1611
expect(stack[0][2]).to.equal(26);
1612
done();
1613
});
1614
});
1615
1616
describe('displayStack ()', function () {
1617
1618
it('returns the full call stack for display', function (done) {
1619
1620
var stack = Hoek.displayStack();
1621
expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':');
1622
done();
1623
});
1624
1625
it('includes constructor functions correctly', function (done) {
1626
1627
var Something = function (next) {
1628
1629
next();
1630
};
1631
1632
var something = new Something(function () {
1633
1634
var stack = Hoek.displayStack();
1635
expect(stack[1]).to.contain('new Something');
1636
done();
1637
});
1638
});
1639
});
1640
1641
describe('abort()', function () {
1642
1643
it('exits process when not in test mode', function (done) {
1644
1645
var env = process.env.NODE_ENV;
1646
var write = process.stdout.write;
1647
var exit = process.exit;
1648
1649
process.env.NODE_ENV = 'nottatest';
1650
process.stdout.write = function () { };
1651
process.exit = function (state) {
1652
1653
process.exit = exit;
1654
process.env.NODE_ENV = env;
1655
process.stdout.write = write;
1656
1657
expect(state).to.equal(1);
1658
done();
1659
};
1660
1661
Hoek.abort('Boom');
1662
});
1663
1664
it('throws when not in test mode and abortThrow is true', function (done) {
1665
1666
var env = process.env.NODE_ENV;
1667
process.env.NODE_ENV = 'nottatest';
1668
Hoek.abortThrow = true;
1669
1670
var fn = function () {
1671
1672
Hoek.abort('my error message');
1673
};
1674
1675
expect(fn).to.throw('my error message');
1676
Hoek.abortThrow = false;
1677
process.env.NODE_ENV = env;
1678
1679
done();
1680
});
1681
1682
it('respects hideStack argument', function (done) {
1683
1684
var env = process.env.NODE_ENV;
1685
var write = process.stdout.write;
1686
var exit = process.exit;
1687
var output = '';
1688
1689
process.exit = function () { };
1690
process.env.NODE_ENV = '';
1691
process.stdout.write = function (message) {
1692
1693
output = message;
1694
};
1695
1696
Hoek.abort('my error message', true);
1697
1698
process.env.NODE_ENV = env;
1699
process.stdout.write = write;
1700
process.exit = exit;
1701
1702
expect(output).to.equal('ABORT: my error message\n\t\n');
1703
1704
done();
1705
});
1706
1707
it('throws in test mode', function (done) {
1708
1709
var env = process.env.NODE_ENV;
1710
process.env.NODE_ENV = 'test';
1711
1712
expect(function () {
1713
1714
Hoek.abort('my error message', true);
1715
}).to.throw('my error message');
1716
1717
process.env.NODE_ENV = env;
1718
done();
1719
});
1720
1721
it('throws in test mode with default message', function (done) {
1722
1723
var env = process.env.NODE_ENV;
1724
process.env.NODE_ENV = 'test';
1725
1726
expect(function () {
1727
1728
Hoek.abort('', true);
1729
}).to.throw('Unknown error');
1730
1731
process.env.NODE_ENV = env;
1732
done();
1733
});
1734
1735
it('defaults to showing stack', function (done) {
1736
1737
var env = process.env.NODE_ENV;
1738
var write = process.stdout.write;
1739
var exit = process.exit;
1740
var output = '';
1741
1742
process.exit = function () { };
1743
process.env.NODE_ENV = '';
1744
process.stdout.write = function (message) {
1745
1746
output = message;
1747
};
1748
1749
Hoek.abort('my error message');
1750
1751
process.env.NODE_ENV = env;
1752
process.stdout.write = write;
1753
process.exit = exit;
1754
1755
expect(output).to.contain('index.js');
1756
1757
done();
1758
});
1759
});
1760
1761
describe('assert()', function () {
1762
1763
it('throws an Error when using assert in a test', function (done) {
1764
1765
var fn = function () {
1766
1767
Hoek.assert(false, 'my error message');
1768
};
1769
1770
expect(fn).to.throw('my error message');
1771
done();
1772
});
1773
1774
it('throws an Error when using assert in a test with no message', function (done) {
1775
1776
var fn = function () {
1777
1778
Hoek.assert(false);
1779
};
1780
1781
expect(fn).to.throw('Unknown error');
1782
done();
1783
});
1784
1785
it('throws an Error when using assert in a test with multipart message', function (done) {
1786
1787
var fn = function () {
1788
1789
Hoek.assert(false, 'This', 'is', 'my message');
1790
};
1791
1792
expect(fn).to.throw('This is my message');
1793
done();
1794
});
1795
1796
it('throws an Error when using assert in a test with multipart message (empty)', function (done) {
1797
1798
var fn = function () {
1799
1800
Hoek.assert(false, 'This', 'is', '', 'my message');
1801
};
1802
1803
expect(fn).to.throw('This is my message');
1804
done();
1805
});
1806
1807
it('throws an Error when using assert in a test with object message', function (done) {
1808
1809
var fn = function () {
1810
1811
Hoek.assert(false, 'This', 'is', { spinal: 'tap' });
1812
};
1813
1814
expect(fn).to.throw('This is {"spinal":"tap"}');
1815
done();
1816
});
1817
1818
it('throws an Error when using assert in a test with multipart string and error messages', function (done) {
1819
1820
var fn = function () {
1821
1822
Hoek.assert(false, 'This', 'is', new Error('spinal'), new Error('tap'));
1823
};
1824
1825
expect(fn).to.throw('This is spinal tap');
1826
done();
1827
});
1828
1829
it('throws an Error when using assert in a test with error object message', function (done) {
1830
1831
var fn = function () {
1832
1833
Hoek.assert(false, new Error('This is spinal tap'));
1834
};
1835
1836
expect(fn).to.throw('This is spinal tap');
1837
done();
1838
});
1839
1840
it('throws the same Error that is passed to it if there is only one error passed', function (done) {
1841
1842
var error = new Error('ruh roh');
1843
var error2 = new Error('ruh roh');
1844
1845
var fn = function () {
1846
1847
Hoek.assert(false, error);
1848
};
1849
1850
try {
1851
fn();
1852
} catch (err) {
1853
expect(error).to.equal(error); // should be the same reference
1854
expect(error).to.not.equal(error2); // error with the same message should not match
1855
}
1856
1857
done();
1858
});
1859
});
1860
1861
describe('Timer', function () {
1862
1863
it('returns time elapsed', function (done) {
1864
1865
var timer = new Hoek.Timer();
1866
setTimeout(function () {
1867
1868
expect(timer.elapsed()).to.be.above(9);
1869
done();
1870
}, 12);
1871
});
1872
});
1873
1874
describe('Bench', function () {
1875
1876
it('returns time elapsed', function (done) {
1877
1878
var timer = new Hoek.Bench();
1879
setTimeout(function () {
1880
1881
expect(timer.elapsed()).to.be.above(9);
1882
done();
1883
}, 12);
1884
});
1885
});
1886
1887
describe('escapeRegex()', function () {
1888
1889
it('escapes all special regular expression characters', function (done) {
1890
1891
var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,');
1892
expect(a).to.equal('4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`"\\(>\\)\\[<\\]d\\{\\}s\\,');
1893
done();
1894
});
1895
});
1896
1897
describe('Base64Url', function () {
1898
1899
var base64str = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w';
1900
var str = unescape('%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28%29*+%2C-./0123456789%3A%3B%3C%3D%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF');
1901
1902
describe('base64urlEncode()', function () {
1903
1904
it('should base64 URL-safe a string', function (done) {
1905
1906
expect(Hoek.base64urlEncode(str)).to.equal(base64str);
1907
done();
1908
});
1909
1910
it('encodes a buffer', function (done) {
1911
1912
expect(Hoek.base64urlEncode(new Buffer(str, 'binary'))).to.equal(base64str);
1913
done();
1914
});
1915
1916
it('should base64 URL-safe a hex string', function (done) {
1917
1918
var buffer = new Buffer(str, 'binary');
1919
expect(Hoek.base64urlEncode(buffer.toString('hex'), 'hex')).to.equal(base64str);
1920
done();
1921
});
1922
1923
it('works on larger input strings', function (done) {
1924
1925
var input = Fs.readFileSync(Path.join(__dirname, 'index.js')).toString();
1926
var encoded = Hoek.base64urlEncode(input);
1927
1928
expect(encoded).to.not.contain('+');
1929
expect(encoded).to.not.contain('/');
1930
1931
var decoded = Hoek.base64urlDecode(encoded);
1932
1933
expect(decoded).to.equal(input);
1934
done();
1935
});
1936
});
1937
1938
describe('base64urlDecode()', function () {
1939
1940
it('should un-base64 URL-safe a string', function (done) {
1941
1942
expect(Hoek.base64urlDecode(base64str)).to.equal(str);
1943
done();
1944
});
1945
1946
it('should un-base64 URL-safe a string into hex', function (done) {
1947
1948
expect(Hoek.base64urlDecode(base64str, 'hex')).to.equal(new Buffer(str, 'binary').toString('hex'));
1949
done();
1950
});
1951
1952
it('should un-base64 URL-safe a string and return a buffer', function (done) {
1953
1954
var buf = Hoek.base64urlDecode(base64str, 'buffer');
1955
expect(buf instanceof Buffer).to.equal(true);
1956
expect(buf.toString('binary')).to.equal(str);
1957
done();
1958
});
1959
1960
it('returns error on undefined input', function (done) {
1961
1962
expect(Hoek.base64urlDecode().message).to.exist();
1963
done();
1964
});
1965
1966
it('returns error on invalid input', function (done) {
1967
1968
expect(Hoek.base64urlDecode('*').message).to.exist();
1969
done();
1970
});
1971
});
1972
});
1973
1974
describe('escapeHeaderAttribute()', function () {
1975
1976
it('should not alter ascii values', function (done) {
1977
1978
var a = Hoek.escapeHeaderAttribute('My Value');
1979
expect(a).to.equal('My Value');
1980
done();
1981
});
1982
1983
it('escapes all special HTTP header attribute characters', function (done) {
1984
1985
var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92));
1986
expect(a).to.equal('I said go!!!#\\"\\\\');
1987
done();
1988
});
1989
1990
it('throws on large unicode characters', function (done) {
1991
1992
var fn = function () {
1993
1994
Hoek.escapeHeaderAttribute('this is a test' + String.fromCharCode(500) + String.fromCharCode(300));
1995
};
1996
1997
expect(fn).to.throw(Error);
1998
done();
1999
});
2000
2001
it('throws on CRLF to prevent response splitting', function (done) {
2002
2003
var fn = function () {
2004
2005
Hoek.escapeHeaderAttribute('this is a test\r\n');
2006
};
2007
2008
expect(fn).to.throw(Error);
2009
done();
2010
});
2011
});
2012
2013
describe('escapeHtml()', function () {
2014
2015
it('escapes all special HTML characters', function (done) {
2016
2017
var a = Hoek.escapeHtml('&<>"\'`');
2018
expect(a).to.equal('&amp;&lt;&gt;&quot;&#x27;&#x60;');
2019
done();
2020
});
2021
2022
it('returns empty string on falsy input', function (done) {
2023
2024
var a = Hoek.escapeHtml('');
2025
expect(a).to.equal('');
2026
done();
2027
});
2028
2029
it('returns unchanged string on no reserved input', function (done) {
2030
2031
var a = Hoek.escapeHtml('abc');
2032
expect(a).to.equal('abc');
2033
done();
2034
});
2035
});
2036
2037
describe('nextTick()', function () {
2038
2039
it('calls the provided callback on nextTick', function (done) {
2040
2041
var a = 0;
2042
2043
var inc = function (step, next) {
2044
2045
a += step;
2046
next();
2047
};
2048
2049
var ticked = Hoek.nextTick(inc);
2050
2051
ticked(5, function () {
2052
2053
expect(a).to.equal(6);
2054
done();
2055
});
2056
2057
expect(a).to.equal(0);
2058
inc(1, function () {
2059
2060
expect(a).to.equal(1);
2061
});
2062
});
2063
});
2064
2065
describe('once()', function () {
2066
2067
it('allows function to only execute once', function (done) {
2068
2069
var gen = 0;
2070
var add = function (x) {
2071
2072
gen += x;
2073
};
2074
2075
add(5);
2076
expect(gen).to.equal(5);
2077
add = Hoek.once(add);
2078
add(5);
2079
expect(gen).to.equal(10);
2080
add(5);
2081
expect(gen).to.equal(10);
2082
done();
2083
});
2084
2085
it('double once wraps one time', function (done) {
2086
2087
var method = function () { };
2088
method = Hoek.once(method);
2089
method.x = 1;
2090
method = Hoek.once(method);
2091
expect(method.x).to.equal(1);
2092
done();
2093
});
2094
});
2095
2096
describe('isAbsoltePath()', function () {
2097
2098
it('identifies if path is absolute on Unix without node support', { parallel: false }, function (done) {
2099
2100
var orig = Path.isAbsolute;
2101
Path.isAbsolute = undefined;
2102
2103
expect(Hoek.isAbsolutePath('')).to.equal(false);
2104
expect(Hoek.isAbsolutePath('a')).to.equal(false);
2105
expect(Hoek.isAbsolutePath('./a')).to.equal(false);
2106
expect(Hoek.isAbsolutePath('/a')).to.equal(true);
2107
expect(Hoek.isAbsolutePath('/')).to.equal(true);
2108
2109
Path.isAbsolute = orig;
2110
2111
done();
2112
});
2113
2114
it('identifies if path is absolute with fake node support', { parallel: false }, function (done) {
2115
2116
var orig = Path.isAbsolute;
2117
Path.isAbsolute = function (path) {
2118
2119
return path[0] === '/';
2120
};
2121
2122
expect(Hoek.isAbsolutePath('', 'linux')).to.equal(false);
2123
expect(Hoek.isAbsolutePath('a', 'linux')).to.equal(false);
2124
expect(Hoek.isAbsolutePath('./a', 'linux')).to.equal(false);
2125
expect(Hoek.isAbsolutePath('/a', 'linux')).to.equal(true);
2126
expect(Hoek.isAbsolutePath('/', 'linux')).to.equal(true);
2127
2128
Path.isAbsolute = orig;
2129
2130
done();
2131
});
2132
2133
it('identifies if path is absolute on Windows without node support', { parallel: false }, function (done) {
2134
2135
var orig = Path.isAbsolute;
2136
Path.isAbsolute = undefined;
2137
2138
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
2139
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
2140
expect(Hoek.isAbsolutePath('\\\\server\\file', 'win32')).to.equal(true);
2141
expect(Hoek.isAbsolutePath('C:/Users/', 'win32')).to.equal(true);
2142
expect(Hoek.isAbsolutePath('C:\\Users\\', 'win32')).to.equal(true);
2143
expect(Hoek.isAbsolutePath('C:cwd/another', 'win32')).to.equal(false);
2144
expect(Hoek.isAbsolutePath('C:cwd\\another', 'win32')).to.equal(false);
2145
expect(Hoek.isAbsolutePath('directory/directory', 'win32')).to.equal(false);
2146
expect(Hoek.isAbsolutePath('directory\\directory', 'win32')).to.equal(false);
2147
2148
Path.isAbsolute = orig;
2149
2150
done();
2151
});
2152
});
2153
2154
describe('isInteger()', function () {
2155
2156
it('validates integers', function (done) {
2157
2158
expect(Hoek.isInteger(0)).to.equal(true);
2159
expect(Hoek.isInteger(1)).to.equal(true);
2160
expect(Hoek.isInteger(1394035612500)).to.equal(true);
2161
expect(Hoek.isInteger('0')).to.equal(false);
2162
expect(Hoek.isInteger(1.0)).to.equal(true);
2163
expect(Hoek.isInteger(1.1)).to.equal(false);
2164
done();
2165
});
2166
});
2167
2168
describe('ignore()', function () {
2169
2170
it('exists', function (done) {
2171
2172
expect(Hoek.ignore).to.exist();
2173
expect(typeof Hoek.ignore).to.equal('function');
2174
done();
2175
});
2176
});
2177
2178
describe('inherits()', function () {
2179
2180
it('exists', function (done) {
2181
2182
expect(Hoek.inherits).to.exist();
2183
expect(typeof Hoek.inherits).to.equal('function');
2184
done();
2185
});
2186
});
2187
2188
describe('format()', function () {
2189
2190
it('exists', function (done) {
2191
2192
expect(Hoek.format).to.exist();
2193
expect(typeof Hoek.format).to.equal('function');
2194
done();
2195
});
2196
2197
it('is a reference to Util.format', function (done) {
2198
2199
expect(Hoek.format('hello %s', 'world')).to.equal('hello world');
2200
done();
2201
});
2202
});
2203
2204
describe('transform()', function () {
2205
2206
var source = {
2207
address: {
2208
one: '123 main street',
2209
two: 'PO Box 1234'
2210
},
2211
zip: {
2212
code: 3321232,
2213
province: null
2214
},
2215
title: 'Warehouse',
2216
state: 'CA'
2217
};
2218
2219
it('transforms an object based on the input object', function (done) {
2220
2221
var result = Hoek.transform(source, {
2222
'person.address.lineOne': 'address.one',
2223
'person.address.lineTwo': 'address.two',
2224
'title': 'title',
2225
'person.address.region': 'state',
2226
'person.address.zip': 'zip.code',
2227
'person.address.location': 'zip.province'
2228
});
2229
2230
expect(result).to.deep.equal({
2231
person: {
2232
address: {
2233
lineOne: '123 main street',
2234
lineTwo: 'PO Box 1234',
2235
region: 'CA',
2236
zip: 3321232,
2237
location: null
2238
}
2239
},
2240
title: 'Warehouse'
2241
});
2242
2243
done();
2244
});
2245
2246
it('uses the reach options passed into it', function (done) {
2247
2248
var schema = {
2249
'person.address.lineOne': 'address-one',
2250
'person.address.lineTwo': 'address-two',
2251
'title': 'title',
2252
'person.address.region': 'state',
2253
'person.prefix': 'person-title',
2254
'person.zip': 'zip-code'
2255
};
2256
var options = {
2257
separator: '-',
2258
default: 'unknown'
2259
};
2260
var result = Hoek.transform(source, schema, options);
2261
2262
expect(result).to.deep.equal({
2263
person: {
2264
address: {
2265
lineOne: '123 main street',
2266
lineTwo: 'PO Box 1234',
2267
region: 'CA'
2268
},
2269
prefix: 'unknown',
2270
zip: 3321232
2271
},
2272
title: 'Warehouse'
2273
});
2274
2275
done();
2276
});
2277
2278
it('works to create shallow objects', function (done) {
2279
2280
var result = Hoek.transform(source, {
2281
lineOne: 'address.one',
2282
lineTwo: 'address.two',
2283
title: 'title',
2284
region: 'state',
2285
province: 'zip.province'
2286
});
2287
2288
expect(result).to.deep.equal({
2289
lineOne: '123 main street',
2290
lineTwo: 'PO Box 1234',
2291
title: 'Warehouse',
2292
region: 'CA',
2293
province: null
2294
});
2295
2296
done();
2297
});
2298
2299
it('only allows strings in the map', function (done) {
2300
2301
expect(function () {
2302
2303
var result = Hoek.transform(source, {
2304
lineOne: {}
2305
});
2306
}).to.throw('All mappings must be "." delineated strings');
2307
2308
done();
2309
});
2310
2311
it('throws an error on invalid arguments', function (done) {
2312
2313
expect(function () {
2314
2315
var result = Hoek.transform(NaN, {});
2316
}).to.throw('Invalid source object: must be null, undefined, or an object');
2317
2318
done();
2319
});
2320
2321
it('is safe to pass null', function (done) {
2322
2323
var result = Hoek.transform(null, {});
2324
expect(result).to.deep.equal({});
2325
2326
done();
2327
});
2328
2329
it('is safe to pass undefined', function (done) {
2330
2331
var result = Hoek.transform(undefined, {});
2332
expect(result).to.deep.equal({});
2333
2334
done();
2335
});
2336
});
2337
2338
describe('uniqueFilename()', function () {
2339
2340
it('generates a random file path', function (done) {
2341
2342
var result = Hoek.uniqueFilename('./test/modules');
2343
2344
expect(result).to.exist();
2345
expect(result).to.be.a.string();
2346
expect(result).to.contain('test/modules');
2347
done();
2348
});
2349
2350
it('is random enough to use in fast loops', function (done) {
2351
2352
var results = [];
2353
2354
for (var i = 0; i < 10; ++i) {
2355
results[i] = Hoek.uniqueFilename('./test/modules');
2356
}
2357
2358
var filter = results.filter(function (item, index, array) {
2359
2360
return array.indexOf(item) === index;
2361
});
2362
2363
expect(filter.length).to.equal(10);
2364
expect(results.length).to.equal(10);
2365
done();
2366
2367
});
2368
2369
it('combines the random elements with a supplied character', function (done) {
2370
2371
var result = Hoek.uniqueFilename('./test', 'txt');
2372
2373
expect(result).to.contain('test/');
2374
expect(result).to.contain('.txt');
2375
2376
done();
2377
});
2378
2379
it('accepts extensions with a "." in it', function (done) {
2380
2381
var result = Hoek.uniqueFilename('./test', '.mp3');
2382
2383
expect(result).to.contain('test/');
2384
expect(result).to.contain('.mp3');
2385
2386
done();
2387
});
2388
});
2389
2390
describe('stringify()', function (done) {
2391
2392
it('converts object to string', function (done) {
2393
2394
var obj = { a: 1 };
2395
expect(Hoek.stringify(obj)).to.equal('{"a":1}');
2396
done();
2397
});
2398
2399
it('returns error in result string', function (done) {
2400
2401
var obj = { a: 1 };
2402
obj.b = obj;
2403
expect(Hoek.stringify(obj)).to.equal('[Cannot display object: Converting circular structure to JSON]');
2404
done();
2405
});
2406
});
2407
2408
describe('shallow()', function (done) {
2409
2410
it('shallow copies an object', function (done) {
2411
2412
var obj = {
2413
a: 5,
2414
b: {
2415
c: 6
2416
}
2417
};
2418
2419
var shallow = Hoek.shallow(obj);
2420
expect(shallow).to.not.equal(obj);
2421
expect(shallow).to.deep.equal(obj);
2422
expect(shallow.b).to.equal(obj.b);
2423
done();
2424
});
2425
});
2426
2427