hoek
Advanced tools
Comparing version 2.4.1 to 2.5.0
// Load modules | ||
var Crypto = require('crypto'); | ||
var Path = require('path'); | ||
@@ -49,4 +50,3 @@ var Util = require('util'); | ||
else { | ||
newObj = {}; | ||
newObj.__proto__ = proto; | ||
newObj = Object.create(proto); | ||
cloneDeep = true; | ||
@@ -639,1 +639,16 @@ } | ||
}; | ||
exports.uniqueFilename = function (path, extension) { | ||
if (extension) { | ||
extension = extension[0] !== '.' ? '.' + extension : extension; | ||
} | ||
else { | ||
extension = ''; | ||
} | ||
path = Path.resolve(path); | ||
var name = [Date.now(), process.pid, Crypto.randomBytes(8).toString('hex')].join('-') + extension; | ||
return Path.join(path, name); | ||
}; |
{ | ||
"name": "hoek", | ||
"description": "General purpose node utilities", | ||
"version": "2.4.1", | ||
"version": "2.5.0", | ||
"repository": "git://github.com/hapijs/hoek", | ||
@@ -13,6 +13,5 @@ "main": "index", | ||
}, | ||
"dependencies": { | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"lab": "3.x.x" | ||
"lab": "4.x.x" | ||
}, | ||
@@ -19,0 +18,0 @@ "scripts": { |
131
README.md
@@ -7,3 +7,3 @@ ![hoek Logo](https://raw.github.com/hapijs/hoek/master/images/hoek.png) | ||
Lead Maintainer: [Wyatt Preul](https://github.com/wpreul) | ||
Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf) | ||
@@ -15,4 +15,6 @@ # Table of Contents | ||
* [clone](#cloneobj "clone") | ||
* [cloneWithShallow](#clonewithshallowobj-keys "cloneWithShallow") | ||
* [merge](#mergetarget-source-isnulloverride-ismergearrays "merge") | ||
* [applyToDefaults](#applytodefaultsdefaults-options "applyToDefaults") | ||
* [applyToDefaultsWithShallow](#applytodefaultswithshallowdefaults-options-keys "applyToDefaultsWithShallow") | ||
* [unique](#uniquearray-key "unique") | ||
@@ -38,5 +40,8 @@ * [mapToObject](#maptoobjectarray-key "mapToObject") | ||
* [callStack](#callstackslice "callStack") | ||
* [Load files](#load-files "Load Files") | ||
* [loadPackage](#loadpackagedir "loadpackage") | ||
* [loadDirModules](#loaddirmodulespath-excludefiles-target "loaddirmodules") | ||
* [Function](#function "Function") | ||
* [nextTick](#nexttickfn "nextTick") | ||
* [once](#oncefn "once") | ||
* [ignore](#ignore "ignore") | ||
* [Miscellaneous](#miscellaneous "Miscellaneous") | ||
* [uniqueFilename](#uniquefilename "uniqueFilename") | ||
@@ -92,2 +97,29 @@ | ||
### cloneWithShallow(obj, keys) | ||
keys is an array of key names to shallow copy | ||
This method is also used to clone an object or array, however any keys listed in the `keys` array are shallow copied while those not listed are deep copied. | ||
```javascript | ||
var nestedObj = { | ||
w: /^something$/ig, | ||
x: { | ||
a: [1, 2, 3], | ||
b: 123456, | ||
c: new Date() | ||
}, | ||
y: 'y', | ||
z: new Date() | ||
}; | ||
var copy = Hoek.cloneWithShallow(nestedObj, ['x']); | ||
copy.x.b = 100; | ||
console.log(copy.y); // results in 'y' | ||
console.log(nestedObj.x.b); // results in 100 | ||
console.log(copy.x.b); // results in 100 | ||
``` | ||
### merge(target, source, isNullOverride, isMergeArrays) | ||
@@ -123,8 +155,28 @@ isNullOverride, isMergeArrays default to true | ||
var defaults = {host: "localhost", port: 8000}; | ||
var options = {port: 8080}; | ||
var defaults = { host: "localhost", port: 8000 }; | ||
var options = { port: 8080 }; | ||
var config = Hoek.applyToDefaults(defaults, options); // results in {host: "localhost", port: 8080} | ||
var config = Hoek.applyToDefaults(defaults, options); // results in { host: "localhost", port: 8080 } | ||
``` | ||
### applyToDefaultsWithShallow(defaults, options, keys) | ||
keys is an array of key names to shallow copy | ||
Apply options to a copy of the defaults. Keys specified in the last parameter are shallow copied from options instead of merged. | ||
```javascript | ||
var defaults = { | ||
server: { | ||
host: "localhost", | ||
port: 8000 | ||
}, | ||
name: 'example' | ||
}; | ||
var options = { server: { port: 8080 } }; | ||
var config = Hoek.applyToDefaults(defaults, options); // results in { server: { port: 8080 }, name: 'example' } | ||
``` | ||
### unique(array, key) | ||
@@ -170,3 +222,3 @@ | ||
### flatten(array, target) | ||
### flatten(array, [target]) | ||
@@ -177,6 +229,10 @@ Flatten an array | ||
var array = [1, 2, 3]; | ||
var target = [4, 5]; | ||
var array = [1, [2, 3]]; | ||
var flattenedArray = Hoek.flatten(array, target); // results in [4, 5, 1, 2, 3] | ||
var flattenedArray = Hoek.flatten(array); // results in [1, 2, 3] | ||
array = [1, [2, 3]]; | ||
target = [4, [5]]; | ||
flattenedArray = Hoek.flatten(array, target); // results in [4, [5], 1, 2, 3] | ||
``` | ||
@@ -361,1 +417,54 @@ | ||
``` | ||
## Function | ||
### nextTick(fn) | ||
Returns a new function that wraps `fn` in `process.nextTick`. | ||
```javascript | ||
var myFn = function () { | ||
console.log('Do this later'); | ||
}; | ||
var nextFn = Hoek.nextTick(myFn); | ||
nextFn(); | ||
console.log('Do this first'); | ||
// Results in: | ||
// | ||
// Do this first | ||
// Do this later | ||
``` | ||
### once(fn) | ||
Returns a new function that can be run multiple times, but makes sure `fn` is only run once. | ||
```javascript | ||
var myFn = function () { | ||
console.log('Ran myFn'); | ||
}; | ||
var onceFn = Hoek.once(myFn); | ||
onceFn(); // results in "Ran myFn" | ||
onceFn(); // results in undefined | ||
``` | ||
### ignore | ||
A simple no-op function. It does nothing at all. | ||
## Miscellaneous | ||
### uniqueFilename(path, extension) | ||
`path` to prepend with the randomly generated file name. `extension` is the optional file extension, defaults to `''`. | ||
Returns a randomly generated file name at the specified `path`. The result is a fully resolved path to a file. | ||
```javascript | ||
var result = Hoek.uniqueFilename('./test/modules', 'txt'); // results in "full/path/test/modules/{random}.txt" | ||
``` |
@@ -14,78 +14,74 @@ // Load modules | ||
var lab = exports.lab = Lab.script(); | ||
var expect = Lab.expect; | ||
var before = Lab.before; | ||
var after = Lab.after; | ||
var describe = Lab.experiment; | ||
var it = Lab.test; | ||
var describe = lab.experiment; | ||
var it = lab.test; | ||
describe('Hoek', function () { | ||
describe('escapeJavaScript()', function () { | ||
describe('#escapeJavaScript', function () { | ||
it('encodes / characters', function (done) { | ||
it('encodes / characters', function (done) { | ||
var encoded = Hoek.escapeJavaScript('<script>alert(1)</script>'); | ||
expect(encoded).to.equal('\\x3cscript\\x3ealert\\x281\\x29\\x3c\\x2fscript\\x3e'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeJavaScript('<script>alert(1)</script>'); | ||
expect(encoded).to.equal('\\x3cscript\\x3ealert\\x281\\x29\\x3c\\x2fscript\\x3e'); | ||
done(); | ||
}); | ||
it('encodes \' characters', function (done) { | ||
it('encodes \' characters', function (done) { | ||
var encoded = Hoek.escapeJavaScript('something(\'param\')'); | ||
expect(encoded).to.equal('something\\x28\\x27param\\x27\\x29'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeJavaScript('something(\'param\')'); | ||
expect(encoded).to.equal('something\\x28\\x27param\\x27\\x29'); | ||
done(); | ||
}); | ||
it('encodes large unicode characters with the correct padding', function (done) { | ||
it('encodes large unicode characters with the correct padding', function (done) { | ||
var encoded = Hoek.escapeJavaScript(String.fromCharCode(500) + String.fromCharCode(1000)); | ||
expect(encoded).to.equal('\\u0500\\u1000'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeJavaScript(String.fromCharCode(500) + String.fromCharCode(1000)); | ||
expect(encoded).to.equal('\\u0500\\u1000'); | ||
done(); | ||
}); | ||
it('doesn\'t throw an exception when passed null', function (done) { | ||
it('doesn\'t throw an exception when passed null', function (done) { | ||
var encoded = Hoek.escapeJavaScript(null); | ||
expect(encoded).to.equal(''); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeJavaScript(null); | ||
expect(encoded).to.equal(''); | ||
done(); | ||
}); | ||
}); | ||
describe('#escapeHtml', function () { | ||
describe('escapeHtml()', function () { | ||
it('encodes / characters', function (done) { | ||
it('encodes / characters', function (done) { | ||
var encoded = Hoek.escapeHtml('<script>alert(1)</script>'); | ||
expect(encoded).to.equal('<script>alert(1)</script>'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeHtml('<script>alert(1)</script>'); | ||
expect(encoded).to.equal('<script>alert(1)</script>'); | ||
done(); | ||
}); | ||
it('encodes < and > as named characters', function (done) { | ||
it('encodes < and > as named characters', function (done) { | ||
var encoded = Hoek.escapeHtml('<script><>'); | ||
expect(encoded).to.equal('<script><>'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeHtml('<script><>'); | ||
expect(encoded).to.equal('<script><>'); | ||
done(); | ||
}); | ||
it('encodes large unicode characters', function (done) { | ||
it('encodes large unicode characters', function (done) { | ||
var encoded = Hoek.escapeHtml(String.fromCharCode(500) + String.fromCharCode(1000)); | ||
expect(encoded).to.equal('ǴϨ'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeHtml(String.fromCharCode(500) + String.fromCharCode(1000)); | ||
expect(encoded).to.equal('ǴϨ'); | ||
done(); | ||
}); | ||
it('doesn\'t throw an exception when passed null', function (done) { | ||
it('doesn\'t throw an exception when passed null', function (done) { | ||
var encoded = Hoek.escapeHtml(null); | ||
expect(encoded).to.equal(''); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeHtml(null); | ||
expect(encoded).to.equal(''); | ||
done(); | ||
}); | ||
it('encodes {} characters', function (done) { | ||
it('encodes {} characters', function (done) { | ||
var encoded = Hoek.escapeHtml('{}'); | ||
expect(encoded).to.equal('{}'); | ||
done(); | ||
}); | ||
var encoded = Hoek.escapeHtml('{}'); | ||
expect(encoded).to.equal('{}'); | ||
done(); | ||
}); | ||
@@ -95,1 +91,2 @@ }); | ||
2154
test/index.js
@@ -16,1044 +16,1132 @@ // Load modules | ||
var lab = exports.lab = Lab.script(); | ||
var expect = Lab.expect; | ||
var before = Lab.before; | ||
var after = Lab.after; | ||
var describe = Lab.experiment; | ||
var it = Lab.test; | ||
var describe = lab.experiment; | ||
var it = lab.test; | ||
describe('Hoek', function () { | ||
var nestedObj = { | ||
v: [7, 8, 9], | ||
w: /^something$/igm, | ||
x: { | ||
a: [1, 2, 3], | ||
b: 123456, | ||
c: new Date(), | ||
d: /hi/igm, | ||
e: /hello/ | ||
}, | ||
y: 'y', | ||
z: new Date(1378775452757) | ||
}; | ||
var nestedObj = { | ||
v: [7, 8, 9], | ||
w: /^something$/igm, | ||
x: { | ||
a: [1, 2, 3], | ||
b: 123456, | ||
c: new Date(), | ||
d: /hi/igm, | ||
e: /hello/ | ||
}, | ||
y: 'y', | ||
z: new Date(1378775452757) | ||
}; | ||
var dupsArray = [nestedObj, { z: 'z' }, nestedObj]; | ||
var reducedDupsArray = [nestedObj, { z: 'z' }]; | ||
var dupsArray = [nestedObj, { z: 'z' }, nestedObj]; | ||
var reducedDupsArray = [nestedObj, { z: 'z' }]; | ||
describe('clone()', function () { | ||
describe('#clone', function () { | ||
it('clones a nested object', function (done) { | ||
it('clones a nested object', function (done) { | ||
var a = nestedObj; | ||
var b = Hoek.clone(a); | ||
var a = nestedObj; | ||
var b = Hoek.clone(a); | ||
expect(a).to.deep.equal(b); | ||
expect(a.z.getTime()).to.equal(b.z.getTime()); | ||
done(); | ||
}); | ||
expect(a).to.deep.equal(b); | ||
expect(a.z.getTime()).to.equal(b.z.getTime()); | ||
done(); | ||
}); | ||
it('clones a null object', function (done) { | ||
it('clones a null object', function (done) { | ||
var b = Hoek.clone(null); | ||
var b = Hoek.clone(null); | ||
expect(b).to.equal(null); | ||
done(); | ||
}); | ||
expect(b).to.equal(null); | ||
done(); | ||
}); | ||
it('should not convert undefined properties to null', function (done) { | ||
it('should not convert undefined properties to null', function (done) { | ||
var obj = { something: undefined }; | ||
var b = Hoek.clone(obj); | ||
var obj = { something: undefined }; | ||
var b = Hoek.clone(obj); | ||
expect(typeof b.something).to.equal('undefined'); | ||
done(); | ||
}); | ||
expect(typeof b.something).to.equal('undefined'); | ||
done(); | ||
it('should not throw on circular reference', function (done) { | ||
var a = {}; | ||
a.x = a; | ||
var test = (function () { | ||
var b = Hoek.clone(a); | ||
}); | ||
it('should not throw on circular reference', function (done) { | ||
expect(test).to.not.throw(); | ||
done(); | ||
}); | ||
var a = {}; | ||
a.x = a; | ||
it('clones circular reference', function (done) { | ||
var test = (function () { | ||
var x = { | ||
'z': new Date() | ||
}; | ||
x.y = x; | ||
var b = Hoek.clone(a); | ||
}); | ||
var b = Hoek.clone(x); | ||
expect(Object.keys(b.y)).to.deep.equal(Object.keys(x)); | ||
expect(b.z).to.not.equal(x.z); | ||
expect(b.y).to.not.equal(x.y); | ||
expect(b.y.z).to.not.equal(x.y.z); | ||
expect(b.y).to.equal(b); | ||
expect(b.y.y.y.y).to.equal(b); | ||
done(); | ||
}); | ||
expect(test).to.not.throw(); | ||
done(); | ||
}); | ||
it('clones an object with a null prototype', function (done) { | ||
it('clones circular reference', function (done) { | ||
var obj = {}; | ||
obj.__proto__ = null; | ||
var b = Hoek.clone(obj); | ||
var x = { | ||
'z': new Date() | ||
}; | ||
x.y = x; | ||
expect(b).to.deep.equal(obj); | ||
done(); | ||
}); | ||
var b = Hoek.clone(x); | ||
expect(Object.keys(b.y)).to.deep.equal(Object.keys(x)); | ||
expect(b.z).to.not.equal(x.z); | ||
expect(b.y).to.not.equal(x.y); | ||
expect(b.y.z).to.not.equal(x.y.z); | ||
expect(b.y).to.equal(b); | ||
expect(b.y.y.y.y).to.equal(b); | ||
done(); | ||
}); | ||
it('clones an object with an undefined prototype', function (done) { | ||
it('clones an object with a null prototype', function (done) { | ||
var obj = {}; | ||
obj.__proto__ = undefined; | ||
var b = Hoek.clone(obj); | ||
var obj = {}; | ||
obj.__proto__ = null; | ||
var b = Hoek.clone(obj); | ||
expect(b).to.deep.equal(obj); | ||
done(); | ||
}); | ||
expect(b).to.deep.equal(obj); | ||
done(); | ||
}); | ||
it('clones deeply nested object', function (done) { | ||
it('clones an object with an undefined prototype', function (done) { | ||
var a = { | ||
x: { | ||
y: { | ||
a: [1, 2, 3], | ||
b: 123456, | ||
c: new Date(), | ||
d: /hi/igm, | ||
e: /hello/ | ||
} | ||
} | ||
}; | ||
var obj = {}; | ||
obj.__proto__ = undefined; | ||
var b = Hoek.clone(obj); | ||
var b = Hoek.clone(a); | ||
expect(b).to.deep.equal(obj); | ||
done(); | ||
}); | ||
expect(a).to.deep.equal(b); | ||
expect(a.x.y.c.getTime()).to.equal(b.x.y.c.getTime()); | ||
done(); | ||
}); | ||
it('clones deeply nested object', function (done) { | ||
it('clones arrays', function (done) { | ||
var a = { | ||
x: { | ||
y: { | ||
a: [1, 2, 3], | ||
b: 123456, | ||
c: new Date(), | ||
d: /hi/igm, | ||
e: /hello/ | ||
} | ||
} | ||
}; | ||
var a = [1, 2, 3]; | ||
var b = Hoek.clone(a); | ||
var b = Hoek.clone(a); | ||
expect(a).to.deep.equal(b); | ||
expect(a.x.y.c.getTime()).to.equal(b.x.y.c.getTime()); | ||
done(); | ||
}); | ||
expect(a).to.deep.equal(b); | ||
done(); | ||
}); | ||
it('clones arrays', function (done) { | ||
it('performs actual copy for shallow keys (no pass by reference)', function (done) { | ||
var a = [1, 2, 3]; | ||
var x = Hoek.clone(nestedObj); | ||
var y = Hoek.clone(nestedObj); | ||
var b = Hoek.clone(a); | ||
// Date | ||
expect(x.z).to.not.equal(nestedObj.z); | ||
expect(x.z).to.not.equal(y.z); | ||
expect(a).to.deep.equal(b); | ||
done(); | ||
}); | ||
// Regex | ||
expect(x.w).to.not.equal(nestedObj.w); | ||
expect(x.w).to.not.equal(y.w); | ||
it('performs actual copy for shallow keys (no pass by reference)', function (done) { | ||
// Array | ||
expect(x.v).to.not.equal(nestedObj.v); | ||
expect(x.v).to.not.equal(y.v); | ||
var x = Hoek.clone(nestedObj); | ||
var y = Hoek.clone(nestedObj); | ||
// Immutable(s) | ||
x.y = 5; | ||
expect(x.y).to.not.equal(nestedObj.y); | ||
expect(x.y).to.not.equal(y.y); | ||
// Date | ||
expect(x.z).to.not.equal(nestedObj.z); | ||
expect(x.z).to.not.equal(y.z); | ||
done(); | ||
}); | ||
// Regex | ||
expect(x.w).to.not.equal(nestedObj.w); | ||
expect(x.w).to.not.equal(y.w); | ||
it('performs actual copy for deep keys (no pass by reference)', function (done) { | ||
// Array | ||
expect(x.v).to.not.equal(nestedObj.v); | ||
expect(x.v).to.not.equal(y.v); | ||
var x = Hoek.clone(nestedObj); | ||
var y = Hoek.clone(nestedObj); | ||
// Immutable(s) | ||
x.y = 5; | ||
expect(x.y).to.not.equal(nestedObj.y); | ||
expect(x.y).to.not.equal(y.y); | ||
expect(x.x.c).to.not.equal(nestedObj.x.c); | ||
expect(x.x.c).to.not.equal(y.x.c); | ||
done(); | ||
}); | ||
expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime()); | ||
expect(x.x.c.getTime()).to.equal(y.x.c.getTime()); | ||
done(); | ||
}); | ||
it('performs actual copy for deep keys (no pass by reference)', function (done) { | ||
it('copies functions with properties', function (done) { | ||
var x = Hoek.clone(nestedObj); | ||
var y = Hoek.clone(nestedObj); | ||
var a = { | ||
x: function () { return 1; }, | ||
y: {} | ||
}; | ||
a.x.z = 'string in function'; | ||
a.x.v = function () { return 2; }; | ||
a.y.u = a.x; | ||
expect(x.x.c).to.not.equal(nestedObj.x.c); | ||
expect(x.x.c).to.not.equal(y.x.c); | ||
var b = Hoek.clone(a); | ||
expect(b.x()).to.equal(1); | ||
expect(b.x.v()).to.equal(2); | ||
expect(b.y.u).to.equal(b.x); | ||
expect(b.x.z).to.equal('string in function'); | ||
done(); | ||
}); | ||
expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime()); | ||
expect(x.x.c.getTime()).to.equal(y.x.c.getTime()); | ||
done(); | ||
}); | ||
it('should copy a buffer', function (done) { | ||
var tls = { | ||
key: new Buffer([1, 2, 3, 4, 5]), | ||
cert: new Buffer([1, 2, 3, 4, 5, 6, 10]) | ||
}; | ||
it('copies functions with properties', function (done) { | ||
var copiedTls = Hoek.clone(tls); | ||
expect(Buffer.isBuffer(copiedTls.key)).to.equal(true); | ||
expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key)); | ||
expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true); | ||
expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert)); | ||
done(); | ||
}); | ||
var a = { | ||
x: function () { return 1; }, | ||
y: {} | ||
}; | ||
a.x.z = 'string in function'; | ||
a.x.v = function () { return 2; }; | ||
a.y.u = a.x; | ||
it('clones an object with a prototype', function (done) { | ||
var b = Hoek.clone(a); | ||
expect(b.x()).to.equal(1); | ||
expect(b.x.v()).to.equal(2); | ||
expect(b.y.u).to.equal(b.x); | ||
expect(b.x.z).to.equal('string in function'); | ||
done(); | ||
}); | ||
var Obj = function () { | ||
it('should copy a buffer', function (done) { | ||
var tls = { | ||
key: new Buffer([1, 2, 3, 4, 5]), | ||
cert: new Buffer([1, 2, 3, 4, 5, 6, 10]) | ||
}; | ||
this.a = 5; | ||
}; | ||
var copiedTls = Hoek.clone(tls); | ||
expect(Buffer.isBuffer(copiedTls.key)).to.equal(true); | ||
expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key)); | ||
expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true); | ||
expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert)); | ||
done(); | ||
}); | ||
Obj.prototype.b = function () { return 'c'; }; | ||
it('clones an object with a prototype', function (done) { | ||
var a = new Obj(); | ||
var b = Hoek.clone(a); | ||
var Obj = function () { | ||
expect(b.a).to.equal(5); | ||
expect(b.b()).to.equal('c'); | ||
expect(a).to.deep.equal(b); | ||
done(); | ||
}); | ||
this.a = 5; | ||
}; | ||
it('reuses cloned Date object', function (done) { | ||
Obj.prototype.b = function () { return 'c'; }; | ||
var obj = { | ||
a: new Date() | ||
}; | ||
var a = new Obj(); | ||
var b = Hoek.clone(a); | ||
obj.b = obj.a; | ||
expect(b.a).to.equal(5); | ||
expect(b.b()).to.equal('c'); | ||
expect(a).to.deep.equal(b); | ||
done(); | ||
}); | ||
var copy = Hoek.clone(obj); | ||
expect(copy.a).to.equal(copy.b); | ||
done(); | ||
}); | ||
it('reuses cloned Date object', function (done) { | ||
it('shallow copies an object with a prototype and isImmutable flag', function (done) { | ||
var obj = { | ||
a: new Date() | ||
}; | ||
var Obj = function () { | ||
obj.b = obj.a; | ||
this.value = 5; | ||
}; | ||
var copy = Hoek.clone(obj); | ||
expect(copy.a).to.equal(copy.b); | ||
done(); | ||
}); | ||
Obj.prototype.b = function () { return 'c'; }; | ||
Obj.prototype.isImmutable = true; | ||
it('shallow copies an object with a prototype and isImmutable flag', function (done) { | ||
var obj = { | ||
a: new Obj() | ||
}; | ||
var Obj = function () { | ||
var copy = Hoek.clone(obj); | ||
this.value = 5; | ||
}; | ||
expect(obj.a.value).to.equal(5); | ||
expect(copy.a.value).to.equal(5); | ||
expect(copy.a.b()).to.equal('c'); | ||
expect(obj.a).to.equal(copy.a); | ||
done(); | ||
}); | ||
}); | ||
Obj.prototype.b = function () { return 'c'; }; | ||
Obj.prototype.isImmutable = true; | ||
describe('merge()', function () { | ||
var obj = { | ||
a: new Obj() | ||
}; | ||
it('deep copies source items', function (done) { | ||
var copy = Hoek.clone(obj); | ||
var target = { | ||
b: 3, | ||
d: [] | ||
}; | ||
expect(obj.a.value).to.equal(5); | ||
expect(copy.a.value).to.equal(5); | ||
expect(copy.a.b()).to.equal('c'); | ||
expect(obj.a).to.equal(copy.a); | ||
done(); | ||
}); | ||
var source = { | ||
c: { | ||
d: 1 | ||
}, | ||
d: [{ e: 1}] | ||
}; | ||
Hoek.merge(target, source); | ||
expect(target.c).to.not.equal(source.c); | ||
expect(target.c).to.deep.equal(source.c); | ||
expect(target.d).to.not.equal(source.d); | ||
expect(target.d[0]).to.not.equal(source.d[0]); | ||
expect(target.d).to.deep.equal(source.d); | ||
done(); | ||
}); | ||
describe('#merge', function () { | ||
it('merges array over an object', function (done) { | ||
it('deep copies source items', function (done) { | ||
var a = { | ||
x: ['n', 'm'] | ||
}; | ||
var target = { | ||
b: 3, | ||
d: [] | ||
}; | ||
var b = { | ||
x: { | ||
n: '1', | ||
m: '2' | ||
} | ||
}; | ||
var source = { | ||
c: { | ||
d: 1 | ||
}, | ||
d: [{ e: 1}] | ||
}; | ||
Hoek.merge(b, a); | ||
expect(a.x[0]).to.equal('n'); | ||
expect(a.x.n).to.not.exist; | ||
done(); | ||
}); | ||
Hoek.merge(target, source); | ||
expect(target.c).to.not.equal(source.c); | ||
expect(target.c).to.deep.equal(source.c); | ||
expect(target.d).to.not.equal(source.d); | ||
expect(target.d[0]).to.not.equal(source.d[0]); | ||
expect(target.d).to.deep.equal(source.d); | ||
done(); | ||
}); | ||
it('merges object over an array', function (done) { | ||
it('merges array over an object', function (done) { | ||
var a = { | ||
x: ['n', 'm'] | ||
}; | ||
var a = { | ||
x: ['n', 'm'] | ||
}; | ||
var b = { | ||
x: { | ||
n: '1', | ||
m: '2' | ||
} | ||
}; | ||
var b = { | ||
x: { | ||
n: '1', | ||
m: '2' | ||
} | ||
}; | ||
Hoek.merge(a, b); | ||
expect(a.x.n).to.equal('1'); | ||
expect(a.x[0]).to.not.exist; | ||
done(); | ||
}); | ||
Hoek.merge(b, a); | ||
expect(a.x[0]).to.equal('n'); | ||
expect(a.x.n).to.not.exist; | ||
done(); | ||
}); | ||
it('does not throw if source is null', function (done) { | ||
it('merges object over an array', function (done) { | ||
var a = {}; | ||
var b = null; | ||
var c = null; | ||
var a = { | ||
x: ['n', 'm'] | ||
}; | ||
expect(function () { | ||
var b = { | ||
x: { | ||
n: '1', | ||
m: '2' | ||
} | ||
}; | ||
c = Hoek.merge(a, b); | ||
}).to.not.throw(); | ||
Hoek.merge(a, b); | ||
expect(a.x.n).to.equal('1'); | ||
expect(a.x[0]).to.not.exist; | ||
done(); | ||
}); | ||
expect(c).to.equal(a); | ||
done(); | ||
}); | ||
it('does not throw if source is null', function (done) { | ||
it('does not throw if source is undefined', function (done) { | ||
var a = {}; | ||
var b = null; | ||
var c = null; | ||
var a = {}; | ||
var b = undefined; | ||
var c = null; | ||
expect(function () { | ||
expect(function () { | ||
c = Hoek.merge(a, b); | ||
}).to.not.throw(); | ||
c = Hoek.merge(a, b); | ||
}).to.not.throw(); | ||
expect(c).to.equal(a); | ||
done(); | ||
}); | ||
expect(c).to.equal(a); | ||
done(); | ||
}); | ||
it('does not throw if source is undefined', function (done) { | ||
it('throws if source is not an object', function (done) { | ||
expect(function () { | ||
var a = {}; | ||
var b = undefined; | ||
var c = null; | ||
var b = 0; | ||
expect(function () { | ||
Hoek.merge(a, b); | ||
}).to.throw('Invalid source value: must be null, undefined, or an object'); | ||
done(); | ||
}); | ||
c = Hoek.merge(a, b); | ||
}).to.not.throw(); | ||
it('throws if target is not an object', function (done) { | ||
expect(c).to.equal(a); | ||
done(); | ||
}); | ||
expect(function () { | ||
it('throws if source is not an object', function (done) { | ||
var a = 0; | ||
var b = {}; | ||
expect(function () { | ||
Hoek.merge(a, b); | ||
}).to.throw('Invalid target value: must be an object'); | ||
done(); | ||
}); | ||
var a = {}; | ||
var b = 0; | ||
it('throws if target is not an array and source is', function (done) { | ||
Hoek.merge(a, b); | ||
}).to.throw('Invalid source value: must be null, undefined, or an object'); | ||
done(); | ||
}); | ||
expect(function () { | ||
it('throws if target is not an object', function (done) { | ||
var a = {}; | ||
var b = [1, 2]; | ||
expect(function () { | ||
Hoek.merge(a, b); | ||
}).to.throw('Cannot merge array onto an object'); | ||
done(); | ||
}); | ||
var a = 0; | ||
var b = {}; | ||
it('returns the same object when merging arrays', function (done) { | ||
Hoek.merge(a, b); | ||
}).to.throw('Invalid target value: must be an object'); | ||
done(); | ||
}); | ||
var a = []; | ||
var b = [1, 2]; | ||
it('throws if target is not an array and source is', function (done) { | ||
expect(Hoek.merge(a, b)).to.equal(a); | ||
done(); | ||
}); | ||
expect(function () { | ||
it('should combine an empty object with a non-empty object', function (done) { | ||
var a = {}; | ||
var b = [1, 2]; | ||
var a = {}; | ||
var b = nestedObj; | ||
Hoek.merge(a, b); | ||
}).to.throw('Cannot merge array onto an object'); | ||
done(); | ||
}); | ||
var c = Hoek.merge(a, b); | ||
expect(a).to.deep.equal(b); | ||
expect(c).to.deep.equal(b); | ||
done(); | ||
}); | ||
it('returns the same object when merging arrays', function (done) { | ||
it('should override values in target', function (done) { | ||
var a = []; | ||
var b = [1, 2]; | ||
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' }; | ||
expect(Hoek.merge(a, b)).to.equal(a); | ||
done(); | ||
}); | ||
var c = Hoek.merge(a, b); | ||
expect(c.x).to.equal(null); | ||
expect(c.y).to.equal(2); | ||
expect(c.z).to.equal(4); | ||
expect(c.v).to.equal(0); | ||
expect(c.m).to.equal('123'); | ||
expect(c.t).to.deep.equal({ u: 6 }); | ||
done(); | ||
}); | ||
it('should combine an empty object with a non-empty object', function (done) { | ||
it('should override values in target (flip)', function (done) { | ||
var a = {}; | ||
var b = nestedObj; | ||
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' }; | ||
var c = Hoek.merge(a, b); | ||
expect(a).to.deep.equal(b); | ||
expect(c).to.deep.equal(b); | ||
done(); | ||
}); | ||
var d = Hoek.merge(b, a); | ||
expect(d.x).to.equal(1); | ||
expect(d.y).to.equal(2); | ||
expect(d.z).to.equal(3); | ||
expect(d.v).to.equal(5); | ||
expect(d.m).to.equal('abc'); | ||
expect(d.t).to.deep.equal('test'); | ||
done(); | ||
}); | ||
it('should override values in target', function (done) { | ||
it('retains Date properties', function (done) { | ||
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' }; | ||
var a = { x: new Date(1378776452757) }; | ||
var c = Hoek.merge(a, b); | ||
expect(c.x).to.equal(null); | ||
expect(c.y).to.equal(2); | ||
expect(c.z).to.equal(4); | ||
expect(c.v).to.equal(0); | ||
expect(c.m).to.equal('123'); | ||
expect(c.t).to.deep.equal({ u: 6 }); | ||
done(); | ||
}); | ||
var b = Hoek.merge({}, a); | ||
expect(a.x.getTime()).to.equal(b.x.getTime()); | ||
done(); | ||
}); | ||
it('should override values in target (flip)', function (done) { | ||
it('retains Date properties when merging keys', function (done) { | ||
var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' }; | ||
var a = { x: new Date(1378776452757) }; | ||
var d = Hoek.merge(b, a); | ||
expect(d.x).to.equal(1); | ||
expect(d.y).to.equal(2); | ||
expect(d.z).to.equal(3); | ||
expect(d.v).to.equal(5); | ||
expect(d.m).to.equal('abc'); | ||
expect(d.t).to.deep.equal('test'); | ||
done(); | ||
}); | ||
var b = Hoek.merge({ x: {} }, a); | ||
expect(a.x.getTime()).to.equal(b.x.getTime()); | ||
done(); | ||
}); | ||
it('retains Date properties', function (done) { | ||
it('overrides Buffer', function (done) { | ||
var a = { x: new Date(1378776452757) }; | ||
var a = { x: new Buffer('abc') }; | ||
var b = Hoek.merge({}, a); | ||
expect(a.x.getTime()).to.equal(b.x.getTime()); | ||
done(); | ||
}); | ||
var b = Hoek.merge({ x: {} }, a); | ||
expect(a.x.toString()).to.equal('abc'); | ||
done(); | ||
}); | ||
}); | ||
it('retains Date properties when merging keys', function (done) { | ||
describe('applyToDefaults()', function () { | ||
var a = { x: new Date(1378776452757) }; | ||
var defaults = { | ||
a: 1, | ||
b: 2, | ||
c: { | ||
d: 3, | ||
e: [5, 6] | ||
}, | ||
f: 6, | ||
g: 'test' | ||
}; | ||
var b = Hoek.merge({ x: {} }, a); | ||
expect(a.x.getTime()).to.equal(b.x.getTime()); | ||
done(); | ||
}); | ||
it('throws when target is null', function (done) { | ||
it('overrides Buffer', function (done) { | ||
expect(function () { | ||
var a = { x: new Buffer('abc') }; | ||
Hoek.applyToDefaults(null, {}); | ||
}).to.throw('Invalid defaults value: must be an object'); | ||
done(); | ||
}); | ||
var b = Hoek.merge({ x: {} }, a); | ||
expect(a.x.toString()).to.equal('abc'); | ||
done(); | ||
}); | ||
it('returns null if options is false', function (done) { | ||
var result = Hoek.applyToDefaults(defaults, false); | ||
expect(result).to.equal(null); | ||
done(); | ||
}); | ||
describe('#applyToDefaults', function () { | ||
it('returns null if options is null', function (done) { | ||
var defaults = { | ||
a: 1, | ||
b: 2, | ||
var result = Hoek.applyToDefaults(defaults, null); | ||
expect(result).to.equal(null); | ||
done(); | ||
}); | ||
it('returns null if options is undefined', function (done) { | ||
var result = Hoek.applyToDefaults(defaults, undefined); | ||
expect(result).to.equal(null); | ||
done(); | ||
}); | ||
it('returns a copy of defaults if options is true', function (done) { | ||
var result = Hoek.applyToDefaults(defaults, true); | ||
expect(result).to.deep.equal(defaults); | ||
done(); | ||
}); | ||
it('applies object to defaults', function (done) { | ||
var obj = { | ||
a: null, | ||
c: { | ||
d: 3, | ||
e: [5, 6] | ||
e: [4] | ||
}, | ||
f: 6, | ||
g: 'test' | ||
f: 0, | ||
g: { | ||
h: 5 | ||
} | ||
}; | ||
it('throws when target is null', function (done) { | ||
var result = Hoek.applyToDefaults(defaults, obj); | ||
expect(result.c.e).to.deep.equal([4]); | ||
expect(result.a).to.equal(1); | ||
expect(result.b).to.equal(2); | ||
expect(result.f).to.equal(0); | ||
expect(result.g).to.deep.equal({ h: 5 }); | ||
done(); | ||
}); | ||
}); | ||
expect(function () { | ||
describe('cloneWithShallow()', function () { | ||
Hoek.applyToDefaults(null, {}); | ||
}).to.throw('Invalid defaults value: must be an object'); | ||
done(); | ||
}); | ||
it('deep clones except for listed keys', function (done) { | ||
it('returns null if options is false', function (done) { | ||
var source = { | ||
a: { | ||
b: 5 | ||
}, | ||
c: { | ||
d: 6 | ||
} | ||
}; | ||
var result = Hoek.applyToDefaults(defaults, false); | ||
expect(result).to.equal(null); | ||
done(); | ||
}); | ||
var copy = Hoek.cloneWithShallow(source, ['c']); | ||
expect(copy).to.deep.equal(source); | ||
expect(copy).to.not.equal(source); | ||
expect(copy.a).to.not.equal(source.a); | ||
expect(copy.b).to.equal(source.b); | ||
done(); | ||
}); | ||
it('returns a copy of defaults if options is true', function (done) { | ||
it('returns immutable value', function (done) { | ||
var result = Hoek.applyToDefaults(defaults, true); | ||
expect(result).to.deep.equal(result); | ||
done(); | ||
}); | ||
expect(Hoek.cloneWithShallow(5)).to.equal(5); | ||
done(); | ||
}); | ||
it('applies object to defaults', function (done) { | ||
it('returns null value', function (done) { | ||
var obj = { | ||
a: null, | ||
c: { | ||
e: [4] | ||
}, | ||
f: 0, | ||
g: { | ||
h: 5 | ||
} | ||
}; | ||
expect(Hoek.cloneWithShallow(null)).to.equal(null); | ||
done(); | ||
}); | ||
var result = Hoek.applyToDefaults(defaults, obj); | ||
expect(result.c.e).to.deep.equal([4]); | ||
expect(result.a).to.equal(1); | ||
expect(result.b).to.equal(2); | ||
expect(result.f).to.equal(0); | ||
expect(result.g).to.deep.equal({ h: 5 }); | ||
done(); | ||
}); | ||
it('returns undefined value', function (done) { | ||
expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined); | ||
done(); | ||
}); | ||
describe('#cloneWithShallow', function () { | ||
it('deep clones except for listed keys (including missing keys)', function (done) { | ||
it('deep clones except for listed keys', function (done) { | ||
var source = { | ||
a: { | ||
b: 5 | ||
}, | ||
c: { | ||
d: 6 | ||
} | ||
}; | ||
var source = { | ||
a: { | ||
b: 5 | ||
}, | ||
c: { | ||
d: 6 | ||
} | ||
}; | ||
var copy = Hoek.cloneWithShallow(source, ['c', 'v']); | ||
expect(copy).to.deep.equal(source); | ||
expect(copy).to.not.equal(source); | ||
expect(copy.a).to.not.equal(source.a); | ||
expect(copy.b).to.equal(source.b); | ||
done(); | ||
}); | ||
}); | ||
var copy = Hoek.cloneWithShallow(source, ['c']); | ||
expect(copy).to.deep.equal(source); | ||
expect(copy).to.not.equal(source); | ||
expect(copy.a).to.not.equal(source.a); | ||
expect(copy.b).to.equal(source.b); | ||
done(); | ||
}); | ||
describe('applyToDefaultsWithShallow()', function () { | ||
it('returns immutable value', function (done) { | ||
it('shallow copies the listed keys from options without merging', function (done) { | ||
expect(Hoek.cloneWithShallow(5)).to.equal(5); | ||
done(); | ||
}); | ||
var defaults = { | ||
a: { | ||
b: 5, | ||
e: 3 | ||
}, | ||
c: { | ||
d: 7, | ||
g: 1 | ||
} | ||
}; | ||
it('returns null value', function (done) { | ||
var options = { | ||
a: { | ||
b: 4 | ||
}, | ||
c: { | ||
d: 6, | ||
f: 7 | ||
} | ||
}; | ||
expect(Hoek.cloneWithShallow(null)).to.equal(null); | ||
done(); | ||
}); | ||
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['a']); | ||
expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: 1, f: 7 } }); | ||
expect(merged.a).to.equal(options.a); | ||
expect(merged.a).to.not.equal(defaults.a); | ||
expect(merged.c).to.not.equal(options.c); | ||
expect(merged.c).to.not.equal(defaults.c); | ||
done(); | ||
}); | ||
}); | ||
it('returns undefined value', function (done) { | ||
describe('unique()', function () { | ||
expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined); | ||
done(); | ||
}); | ||
it('should ensure uniqueness within array of objects based on subkey', function (done) { | ||
it('deep clones except for listed keys (including missing keys)', function (done) { | ||
var a = Hoek.unique(dupsArray, 'x'); | ||
expect(a).to.deep.equal(reducedDupsArray); | ||
done(); | ||
}); | ||
var source = { | ||
a: { | ||
b: 5 | ||
}, | ||
c: { | ||
d: 6 | ||
} | ||
}; | ||
it('removes duplicated without key', function (done) { | ||
var copy = Hoek.cloneWithShallow(source, ['c', 'v']); | ||
expect(copy).to.deep.equal(source); | ||
expect(copy).to.not.equal(source); | ||
expect(copy.a).to.not.equal(source.a); | ||
expect(copy.b).to.equal(source.b); | ||
done(); | ||
}); | ||
expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]); | ||
done(); | ||
}); | ||
}); | ||
describe('#applyToDefaultsWithShallow', function () { | ||
describe('mapToObject()', function () { | ||
it('shallow copies the listed keys from options without merging', function (done) { | ||
it('returns null on null array', function (done) { | ||
var defaults = { | ||
a: { | ||
b: 5, | ||
e: 3 | ||
}, | ||
c: { | ||
d: 7, | ||
g: 1 | ||
} | ||
}; | ||
var a = Hoek.mapToObject(null); | ||
expect(a).to.equal(null); | ||
done(); | ||
}); | ||
var options = { | ||
a: { | ||
b: 4 | ||
}, | ||
c: { | ||
d: 6, | ||
f: 7 | ||
} | ||
}; | ||
it('should convert basic array to existential object', function (done) { | ||
var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['a']); | ||
expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: 1, f: 7 } }); | ||
expect(merged.a).to.equal(options.a); | ||
expect(merged.a).to.not.equal(defaults.a); | ||
expect(merged.c).to.not.equal(options.c); | ||
expect(merged.c).to.not.equal(defaults.c); | ||
done(); | ||
}); | ||
var keys = [1, 2, 3, 4]; | ||
var a = Hoek.mapToObject(keys); | ||
for (var i in keys) { | ||
expect(a[keys[i]]).to.equal(true); | ||
} | ||
done(); | ||
}); | ||
describe('#unique', function () { | ||
it('should convert array of objects to existential object', function (done) { | ||
it('should ensure uniqueness within array of objects based on subkey', function (done) { | ||
var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }]; | ||
var subkey = 'x'; | ||
var a = Hoek.mapToObject(keys, subkey); | ||
expect(a).to.deep.equal({ 1: true, 2: true, 3: true }); | ||
done(); | ||
}); | ||
}); | ||
var a = Hoek.unique(dupsArray, 'x'); | ||
expect(a).to.deep.equal(reducedDupsArray); | ||
done(); | ||
}); | ||
describe('intersect()', function () { | ||
it('removes duplicated without key', function (done) { | ||
it('returns the common objects of two arrays', function (done) { | ||
expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]); | ||
done(); | ||
}); | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(array1, array2); | ||
expect(common.length).to.equal(2); | ||
done(); | ||
}); | ||
describe('#mapToObject', function () { | ||
it('returns just the first common object of two arrays', function (done) { | ||
it('returns null on null array', function (done) { | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(array1, array2, true); | ||
expect(common).to.equal(5); | ||
done(); | ||
}); | ||
var a = Hoek.mapToObject(null); | ||
expect(a).to.equal(null); | ||
done(); | ||
}); | ||
it('returns null when no common and returning just the first common object of two arrays', function (done) { | ||
it('should convert basic array to existential object', function (done) { | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [6, 7]; | ||
var common = Hoek.intersect(array1, array2, true); | ||
expect(common).to.equal(null); | ||
done(); | ||
}); | ||
var keys = [1, 2, 3, 4]; | ||
var a = Hoek.mapToObject(keys); | ||
for (var i in keys) { | ||
expect(a[keys[i]]).to.equal(true); | ||
} | ||
done(); | ||
}); | ||
it('returns an empty array if either input is null', function (done) { | ||
it('should convert array of objects to existential object', function (done) { | ||
expect(Hoek.intersect([1], null).length).to.equal(0); | ||
expect(Hoek.intersect(null, [1]).length).to.equal(0); | ||
done(); | ||
}); | ||
var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }]; | ||
var subkey = 'x'; | ||
var a = Hoek.mapToObject(keys, subkey); | ||
expect(a).to.deep.equal({ 1: true, 2: true, 3: true }); | ||
done(); | ||
}); | ||
it('returns the common objects of object and array', function (done) { | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(Hoek.mapToObject(array1), array2); | ||
expect(common.length).to.equal(2); | ||
done(); | ||
}); | ||
}); | ||
describe('#intersect', function () { | ||
describe('flatten()', function () { | ||
it('returns the common objects of two arrays', function (done) { | ||
it('returns a flat array', function (done) { | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(array1, array2); | ||
expect(common.length).to.equal(2); | ||
done(); | ||
}); | ||
var result = Hoek.flatten([1, 2, [3, 4, [5, 6], [7], 8], [9], [10, [11, 12]], 13]); | ||
expect(result.length).to.equal(13); | ||
expect(result).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); | ||
done(); | ||
}); | ||
}); | ||
it('returns just the first common object of two arrays', function (done) { | ||
describe('reach()', function () { | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(array1, array2, true); | ||
expect(common).to.equal(5); | ||
done(); | ||
}); | ||
var obj = { | ||
a: { | ||
b: { | ||
c: { | ||
d: 1, | ||
e: 2 | ||
}, | ||
f: 'hello' | ||
}, | ||
g: { | ||
h: 3 | ||
} | ||
}, | ||
i: function () { }, | ||
j: null | ||
}; | ||
it('returns null when no common and returning just the first common object of two arrays', function (done) { | ||
obj.i.x = 5; | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [6, 7]; | ||
var common = Hoek.intersect(array1, array2, true); | ||
expect(common).to.equal(null); | ||
done(); | ||
}); | ||
it('returns a valid member', function (done) { | ||
it('returns an empty array if either input is null', function (done) { | ||
expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1); | ||
done(); | ||
}); | ||
expect(Hoek.intersect([1], null).length).to.equal(0); | ||
expect(Hoek.intersect(null, [1]).length).to.equal(0); | ||
done(); | ||
}); | ||
it('returns a valid member with separator override', function (done) { | ||
it('returns the common objects of object and array', function (done) { | ||
expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1); | ||
done(); | ||
}); | ||
var array1 = [1, 2, 3, 4, 4, 5, 5]; | ||
var array2 = [5, 4, 5, 6, 7]; | ||
var common = Hoek.intersect(Hoek.mapToObject(array1), array2); | ||
expect(common.length).to.equal(2); | ||
done(); | ||
}); | ||
it('returns undefined on null object', function (done) { | ||
expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined); | ||
done(); | ||
}); | ||
describe('#flatten', function () { | ||
it('returns undefined on missing object member', function (done) { | ||
it('returns a flat array', function (done) { | ||
expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined); | ||
done(); | ||
}); | ||
var result = Hoek.flatten([1, 2, [3, 4, [5, 6], [7], 8], [9], [10, [11, 12]], 13]); | ||
expect(result.length).to.equal(13); | ||
expect(result).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); | ||
done(); | ||
}); | ||
it('returns undefined on missing function member', function (done) { | ||
expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined); | ||
done(); | ||
}); | ||
describe('#reach', function () { | ||
it('throws on missing member in strict mode', function (done) { | ||
var obj = { | ||
a: { | ||
b: { | ||
c: { | ||
d: 1, | ||
e: 2 | ||
}, | ||
f: 'hello' | ||
}, | ||
g: { | ||
h: 3 | ||
} | ||
}, | ||
i: function () { }, | ||
j: null | ||
}; | ||
expect(function () { | ||
obj.i.x = 5; | ||
Hoek.reach(obj, 'a.b.c.o.x', { strict: true }); | ||
}).to.throw('Missing segment o in reach path a.b.c.o.x'); | ||
it('returns a valid member', function (done) { | ||
done(); | ||
}); | ||
expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1); | ||
done(); | ||
}); | ||
it('returns undefined on invalid member', function (done) { | ||
it('returns a valid member with separator override', function (done) { | ||
expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined); | ||
done(); | ||
}); | ||
expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1); | ||
done(); | ||
}); | ||
it('returns function member', function (done) { | ||
it('returns undefined on null object', function (done) { | ||
expect(typeof Hoek.reach(obj, 'i')).to.equal('function'); | ||
done(); | ||
}); | ||
expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined); | ||
done(); | ||
}); | ||
it('returns function property', function (done) { | ||
it('returns undefined on missing object member', function (done) { | ||
expect(Hoek.reach(obj, 'i.x')).to.equal(5); | ||
done(); | ||
}); | ||
expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined); | ||
done(); | ||
}); | ||
it('returns null', function (done) { | ||
it('returns undefined on missing function member', function (done) { | ||
expect(Hoek.reach(obj, 'j')).to.equal(null); | ||
done(); | ||
}); | ||
expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined); | ||
done(); | ||
}); | ||
it('throws on function property when functions not allowed', function (done) { | ||
it('throws on missing member in strict mode', function (done) { | ||
expect(function () { | ||
expect(function () { | ||
Hoek.reach(obj, 'i.x', { functions: false }); | ||
}).to.throw('Invalid segment x in reach path i.x'); | ||
Hoek.reach(obj, 'a.b.c.o.x', { strict: true }); | ||
}).to.throw('Missing segment o in reach path a.b.c.o.x'); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
it('will return a default value if property is not found', function (done) { | ||
it('returns undefined on invalid member', function (done) { | ||
expect(Hoek.reach(obj, 'a.b.q', {default: 'defaultValue'})).to.equal('defaultValue'); | ||
done(); | ||
}); | ||
expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined); | ||
done(); | ||
}); | ||
it('will return a default value if path is not found', function (done) { | ||
it('returns function member', function (done) { | ||
expect(Hoek.reach(obj, 'q', {default: 'defaultValue'})).to.equal('defaultValue'); | ||
done(); | ||
}); | ||
}); | ||
expect(typeof Hoek.reach(obj, 'i')).to.equal('function'); | ||
done(); | ||
}); | ||
describe('callStack()', function () { | ||
it('returns function property', function (done) { | ||
it('returns the full call stack', function (done) { | ||
expect(Hoek.reach(obj, 'i.x')).to.equal(5); | ||
done(); | ||
}); | ||
var stack = Hoek.callStack(); | ||
expect(stack[0][0]).to.contain('index.js'); | ||
expect(stack[0][2]).to.equal(26); | ||
done(); | ||
}); | ||
}); | ||
it('returns null', function (done) { | ||
describe('displayStack ()', function () { | ||
expect(Hoek.reach(obj, 'j')).to.equal(null); | ||
done(); | ||
}); | ||
it('returns the full call stack for display', function (done) { | ||
it('throws on function property when functions not allowed', function (done) { | ||
var stack = Hoek.displayStack(); | ||
expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':'); | ||
done(); | ||
}); | ||
expect(function () { | ||
it('should include constructor functions correctly', function (done) { | ||
Hoek.reach(obj, 'i.x', { functions: false }); | ||
}).to.throw('Invalid segment x in reach path i.x'); | ||
var Something = function (next) { | ||
done(); | ||
}); | ||
next(); | ||
}; | ||
it('will return a default value if property is not found', function (done) { | ||
var something = new Something(function () { | ||
expect(Hoek.reach(obj, 'a.b.q', {default: 'defaultValue'})).to.equal('defaultValue'); | ||
var stack = Hoek.displayStack(); | ||
expect(stack[1]).to.contain('new Something'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('will return a default value if path is not found', function (done) { | ||
describe('abort()', function () { | ||
expect(Hoek.reach(obj, 'q', {default: 'defaultValue'})).to.equal('defaultValue'); | ||
it('should exit process when not in test mode', function (done) { | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
process.env.NODE_ENV = 'nottatest'; | ||
process.stdout.write = function () { }; | ||
process.exit = function (state) { | ||
process.exit = exit; | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
expect(state).to.equal(1); | ||
done(); | ||
}); | ||
}; | ||
Hoek.abort('Boom'); | ||
}); | ||
describe('#callStack', function () { | ||
it('should throw when not in test mode and abortThrow is true', function (done) { | ||
it('returns the full call stack', function (done) { | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'nottatest'; | ||
Hoek.abortThrow = true; | ||
var stack = Hoek.callStack(); | ||
expect(stack[0][0]).to.contain('index.js'); | ||
expect(stack[0][2]).to.equal(30); | ||
done(); | ||
}); | ||
var fn = function () { | ||
Hoek.abort('my error message'); | ||
}; | ||
expect(fn).to.throw('my error message'); | ||
Hoek.abortThrow = false; | ||
process.env.NODE_ENV = env; | ||
done(); | ||
}); | ||
describe('#displayStack ', function () { | ||
it('should respect hideStack argument', function (done) { | ||
it('returns the full call stack for display', function (done) { | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
var output = ''; | ||
var stack = Hoek.displayStack(); | ||
expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':'); | ||
done(); | ||
}); | ||
process.exit = function () { }; | ||
process.env.NODE_ENV = ''; | ||
process.stdout.write = function (message) { | ||
it('should include constructor functions correctly', function (done) { | ||
output = message; | ||
}; | ||
var Something = function (next) { | ||
Hoek.abort('my error message', true); | ||
next(); | ||
}; | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
process.exit = exit; | ||
var something = new Something(function () { | ||
expect(output).to.equal('ABORT: my error message\n\t\n'); | ||
var stack = Hoek.displayStack(); | ||
expect(stack[1]).to.contain('new Something'); | ||
done(); | ||
}); | ||
}); | ||
done(); | ||
}); | ||
describe('#abort', function () { | ||
it('throws in test mode', function (done) { | ||
it('should exit process when not in test mode', function (done) { | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'test'; | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
expect(function () { | ||
process.env.NODE_ENV = 'nottatest'; | ||
process.stdout.write = function () { }; | ||
process.exit = function (state) { | ||
Hoek.abort('my error message', true); | ||
}).to.throw('my error message'); | ||
process.exit = exit; | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
process.env.NODE_ENV = env; | ||
done(); | ||
}); | ||
expect(state).to.equal(1); | ||
done(); | ||
}; | ||
it('throws in test mode with default message', function (done) { | ||
Hoek.abort('Boom'); | ||
}); | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'test'; | ||
it('should throw when not in test mode and abortThrow is true', function (done) { | ||
expect(function () { | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'nottatest'; | ||
Hoek.abortThrow = true; | ||
Hoek.abort('', true); | ||
}).to.throw('Unknown error'); | ||
var fn = function () { | ||
process.env.NODE_ENV = env; | ||
done(); | ||
}); | ||
Hoek.abort('my error message'); | ||
}; | ||
it('should default to showing stack', function (done) { | ||
expect(fn).to.throw('my error message'); | ||
Hoek.abortThrow = false; | ||
process.env.NODE_ENV = env; | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
var output = ''; | ||
done(); | ||
}); | ||
process.exit = function () { }; | ||
process.env.NODE_ENV = ''; | ||
process.stdout.write = function (message) { | ||
it('should respect hideStack argument', function (done) { | ||
output = message; | ||
}; | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
var output = ''; | ||
Hoek.abort('my error message'); | ||
process.exit = function () { }; | ||
process.env.NODE_ENV = ''; | ||
process.stdout.write = function (message) { | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
process.exit = exit; | ||
output = message; | ||
}; | ||
expect(output).to.contain('index.js'); | ||
Hoek.abort('my error message', true); | ||
done(); | ||
}); | ||
}); | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
process.exit = exit; | ||
describe('assert()', function () { | ||
expect(output).to.equal('ABORT: my error message\n\t\n'); | ||
it('should throw an Error when using assert in a test', function (done) { | ||
done(); | ||
}); | ||
var fn = function () { | ||
it('throws in test mode', function (done) { | ||
Hoek.assert(false, 'my error message'); | ||
}; | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'test'; | ||
expect(fn).to.throw('my error message'); | ||
done(); | ||
}); | ||
expect(function () { | ||
it('should throw an Error when using assert in a test with no message', function (done) { | ||
Hoek.abort('my error message', true); | ||
}).to.throw('my error message'); | ||
var fn = function () { | ||
process.env.NODE_ENV = env; | ||
done(); | ||
}); | ||
Hoek.assert(false); | ||
}; | ||
it('throws in test mode with default message', function (done) { | ||
expect(fn).to.throw('Unknown error'); | ||
done(); | ||
}); | ||
var env = process.env.NODE_ENV; | ||
process.env.NODE_ENV = 'test'; | ||
it('should throw an Error when using assert in a test with multipart message', function (done) { | ||
expect(function () { | ||
var fn = function () { | ||
Hoek.abort('', true); | ||
}).to.throw('Unknown error'); | ||
Hoek.assert(false, 'This', 'is', 'my message'); | ||
}; | ||
process.env.NODE_ENV = env; | ||
done(); | ||
}); | ||
expect(fn).to.throw('This is my message'); | ||
done(); | ||
}); | ||
it('should default to showing stack', function (done) { | ||
it('should throw an Error when using assert in a test with object message', function (done) { | ||
var env = process.env.NODE_ENV; | ||
var write = process.stdout.write; | ||
var exit = process.exit; | ||
var output = ''; | ||
var fn = function () { | ||
process.exit = function () { }; | ||
process.env.NODE_ENV = ''; | ||
process.stdout.write = function (message) { | ||
Hoek.assert(false, 'This', 'is', { spinal: 'tap' }); | ||
}; | ||
output = message; | ||
}; | ||
expect(fn).to.throw('This is {"spinal":"tap"}'); | ||
done(); | ||
}); | ||
Hoek.abort('my error message'); | ||
it('should throw an Error when using assert in a test with error object message', function (done) { | ||
process.env.NODE_ENV = env; | ||
process.stdout.write = write; | ||
process.exit = exit; | ||
var fn = function () { | ||
expect(output).to.contain('index.js'); | ||
Hoek.assert(false, new Error('This is spinal tap')); | ||
}; | ||
done(); | ||
}); | ||
expect(fn).to.throw('This is spinal tap'); | ||
done(); | ||
}); | ||
}); | ||
describe('#assert', function () { | ||
describe('Timer', function () { | ||
it('should throw an Error when using assert in a test', function (done) { | ||
it('returns time elapsed', function (done) { | ||
var fn = function () { | ||
var timer = new Hoek.Timer(); | ||
setTimeout(function () { | ||
Hoek.assert(false, 'my error message'); | ||
}; | ||
expect(fn).to.throw('my error message'); | ||
expect(timer.elapsed()).to.be.above(9); | ||
done(); | ||
}); | ||
}, 12); | ||
}); | ||
}); | ||
it('should throw an Error when using assert in a test with no message', function (done) { | ||
describe('Bench', function () { | ||
var fn = function () { | ||
it('returns time elapsed', function (done) { | ||
Hoek.assert(false); | ||
}; | ||
var timer = new Hoek.Bench(); | ||
setTimeout(function () { | ||
expect(fn).to.throw('Unknown error'); | ||
expect(timer.elapsed()).to.be.above(9); | ||
done(); | ||
}); | ||
}, 12); | ||
}); | ||
}); | ||
it('should throw an Error when using assert in a test with multipart message', function (done) { | ||
describe('escapeRegex()', function () { | ||
var fn = function () { | ||
it('should escape all special regular expression characters', function (done) { | ||
Hoek.assert(false, 'This', 'is', 'my message'); | ||
}; | ||
var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); | ||
expect(a).to.equal('4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`"\\(>\\)\\[<\\]d\\{\\}s\\,'); | ||
done(); | ||
}); | ||
}); | ||
expect(fn).to.throw('This is my message'); | ||
describe('Base64Url', function () { | ||
var base64str = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w'; | ||
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'); | ||
describe('base64urlEncode()', function () { | ||
it('should base64 URL-safe a string', function (done) { | ||
expect(Hoek.base64urlEncode(str)).to.equal(base64str); | ||
done(); | ||
}); | ||
it('should throw an Error when using assert in a test with object message', function (done) { | ||
it('encodes a buffer', function (done) { | ||
var fn = function () { | ||
expect(Hoek.base64urlEncode(new Buffer(str, 'binary'))).to.equal(base64str); | ||
done(); | ||
}); | ||
Hoek.assert(false, 'This', 'is', { spinal: 'tap' }); | ||
}; | ||
it('should base64 URL-safe a hex string', function (done) { | ||
expect(fn).to.throw('This is {"spinal":"tap"}'); | ||
var buffer = new Buffer(str, 'binary'); | ||
expect(Hoek.base64urlEncode(buffer.toString('hex'), 'hex')).to.equal(base64str); | ||
done(); | ||
}); | ||
it('should throw an Error when using assert in a test with error object message', function (done) { | ||
it('works on larger input strings', function (done) { | ||
var fn = function () { | ||
var input = Fs.readFileSync(Path.join(__dirname, 'index.js')).toString(); | ||
var encoded = Hoek.base64urlEncode(input); | ||
Hoek.assert(false, new Error('This is spinal tap')); | ||
}; | ||
expect(encoded).to.not.contain('+'); | ||
expect(encoded).to.not.contain('/'); | ||
expect(fn).to.throw('This is spinal tap'); | ||
var decoded = Hoek.base64urlDecode(encoded); | ||
expect(decoded).to.equal(input); | ||
done(); | ||
@@ -1063,450 +1151,424 @@ }); | ||
describe('Timer', function () { | ||
describe('base64urlDecode()', function () { | ||
it('returns time elapsed', function (done) { | ||
it('should un-base64 URL-safe a string', function (done) { | ||
var timer = new Hoek.Timer(); | ||
setTimeout(function () { | ||
expect(Hoek.base64urlDecode(base64str)).to.equal(str); | ||
done(); | ||
}); | ||
expect(timer.elapsed()).to.be.above(9); | ||
done(); | ||
}, 12); | ||
it('should un-base64 URL-safe a string into hex', function (done) { | ||
expect(Hoek.base64urlDecode(base64str, 'hex')).to.equal(new Buffer(str, 'binary').toString('hex')); | ||
done(); | ||
}); | ||
}); | ||
describe('Bench', function () { | ||
it('should un-base64 URL-safe a string and return a buffer', function (done) { | ||
it('returns time elapsed', function (done) { | ||
var buf = Hoek.base64urlDecode(base64str, 'buffer'); | ||
expect(buf instanceof Buffer).to.equal(true); | ||
expect(buf.toString('binary')).to.equal(str); | ||
done(); | ||
}); | ||
var timer = new Hoek.Bench(); | ||
setTimeout(function () { | ||
it('returns error on undefined input', function (done) { | ||
expect(timer.elapsed()).to.be.above(9); | ||
done(); | ||
}, 12); | ||
expect(Hoek.base64urlDecode().message).to.exist; | ||
done(); | ||
}); | ||
}); | ||
describe('#escapeRegex', function () { | ||
it('returns error on invalid input', function (done) { | ||
it('should escape all special regular expression characters', function (done) { | ||
var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); | ||
expect(a).to.equal('4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`"\\(>\\)\\[<\\]d\\{\\}s\\,'); | ||
expect(Hoek.base64urlDecode('*').message).to.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('Base64Url', function () { | ||
describe('escapeHeaderAttribute()', function () { | ||
var base64str = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w'; | ||
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'); | ||
it('should not alter ascii values', function (done) { | ||
describe('#base64urlEncode', function () { | ||
var a = Hoek.escapeHeaderAttribute('My Value'); | ||
expect(a).to.equal('My Value'); | ||
done(); | ||
}); | ||
it('should base64 URL-safe a string', function (done) { | ||
it('should escape all special HTTP header attribute characters', function (done) { | ||
expect(Hoek.base64urlEncode(str)).to.equal(base64str); | ||
done(); | ||
}); | ||
var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92)); | ||
expect(a).to.equal('I said go!!!#\\"\\\\'); | ||
done(); | ||
}); | ||
it('encodes a buffer', function (done) { | ||
it('should throw on large unicode characters', function (done) { | ||
expect(Hoek.base64urlEncode(new Buffer(str, 'binary'))).to.equal(base64str); | ||
done(); | ||
}); | ||
var fn = function () { | ||
it('should base64 URL-safe a hex string', function (done) { | ||
Hoek.escapeHeaderAttribute('this is a test' + String.fromCharCode(500) + String.fromCharCode(300)); | ||
}; | ||
var buffer = new Buffer(str, 'binary'); | ||
expect(Hoek.base64urlEncode(buffer.toString('hex'), 'hex')).to.equal(base64str); | ||
done(); | ||
}); | ||
expect(fn).to.throw(Error); | ||
done(); | ||
}); | ||
it('works on larger input strings', function (done) { | ||
it('should throw on CRLF to prevent response splitting', function (done) { | ||
var input = Fs.readFileSync(Path.join(__dirname, 'index.js')).toString(); | ||
var encoded = Hoek.base64urlEncode(input); | ||
var fn = function () { | ||
expect(encoded).to.not.contain('+'); | ||
expect(encoded).to.not.contain('/'); | ||
Hoek.escapeHeaderAttribute('this is a test\r\n'); | ||
}; | ||
var decoded = Hoek.base64urlDecode(encoded); | ||
expect(fn).to.throw(Error); | ||
done(); | ||
}); | ||
}); | ||
expect(decoded).to.equal(input); | ||
done(); | ||
}); | ||
}); | ||
describe('escapeHtml()', function () { | ||
describe('#base64urlDecode', function () { | ||
it('should escape all special HTML characters', function (done) { | ||
it('should un-base64 URL-safe a string', function (done) { | ||
var a = Hoek.escapeHtml('&<>"\'`'); | ||
expect(a).to.equal('&<>"'`'); | ||
done(); | ||
}); | ||
expect(Hoek.base64urlDecode(base64str)).to.equal(str); | ||
done(); | ||
}); | ||
it('returns empty string on falsy input', function (done) { | ||
it('should un-base64 URL-safe a string into hex', function (done) { | ||
var a = Hoek.escapeHtml(''); | ||
expect(a).to.equal(''); | ||
done(); | ||
}); | ||
expect(Hoek.base64urlDecode(base64str, 'hex')).to.equal(new Buffer(str, 'binary').toString('hex')); | ||
done(); | ||
}); | ||
it('returns unchanged string on no reserved input', function (done) { | ||
it('should un-base64 URL-safe a string and return a buffer', function (done) { | ||
var a = Hoek.escapeHtml('abc'); | ||
expect(a).to.equal('abc'); | ||
done(); | ||
}); | ||
}); | ||
var buf = Hoek.base64urlDecode(base64str, 'buffer'); | ||
expect(buf instanceof Buffer).to.equal(true); | ||
expect(buf.toString('binary')).to.equal(str); | ||
done(); | ||
}); | ||
describe('nextTick()', function () { | ||
it('returns error on undefined input', function (done) { | ||
it('calls the provided callback on nextTick', function (done) { | ||
expect(Hoek.base64urlDecode().message).to.exist; | ||
done(); | ||
}); | ||
var a = 0; | ||
it('returns error on invalid input', function (done) { | ||
var inc = function (step, next) { | ||
expect(Hoek.base64urlDecode('*').message).to.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
a += step; | ||
next(); | ||
}; | ||
describe('#escapeHeaderAttribute', function () { | ||
var ticked = Hoek.nextTick(inc); | ||
it('should not alter ascii values', function (done) { | ||
ticked(5, function () { | ||
var a = Hoek.escapeHeaderAttribute('My Value'); | ||
expect(a).to.equal('My Value'); | ||
expect(a).to.equal(6); | ||
done(); | ||
}); | ||
it('should escape all special HTTP header attribute characters', function (done) { | ||
expect(a).to.equal(0); | ||
inc(1, function () { | ||
var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92)); | ||
expect(a).to.equal('I said go!!!#\\"\\\\'); | ||
done(); | ||
expect(a).to.equal(1); | ||
}); | ||
}); | ||
}); | ||
it('should throw on large unicode characters', function (done) { | ||
describe('once()', function () { | ||
var fn = function () { | ||
it('allows function to only execute once', function (done) { | ||
Hoek.escapeHeaderAttribute('this is a test' + String.fromCharCode(500) + String.fromCharCode(300)); | ||
}; | ||
var gen = 0; | ||
var add = function (x) { | ||
expect(fn).to.throw(Error); | ||
done(); | ||
}); | ||
gen += x; | ||
}; | ||
it('should throw on CRLF to prevent response splitting', function (done) { | ||
add(5); | ||
expect(gen).to.equal(5); | ||
add = Hoek.once(add); | ||
add(5); | ||
expect(gen).to.equal(10); | ||
add(5); | ||
expect(gen).to.equal(10); | ||
done(); | ||
}); | ||
var fn = function () { | ||
it('double once wraps one time', function (done) { | ||
Hoek.escapeHeaderAttribute('this is a test\r\n'); | ||
}; | ||
expect(fn).to.throw(Error); | ||
done(); | ||
}); | ||
var method = function () { }; | ||
method = Hoek.once(method); | ||
method.x = 1; | ||
method = Hoek.once(method); | ||
expect(method.x).to.equal(1); | ||
done(); | ||
}); | ||
}); | ||
describe('#escapeHtml', function () { | ||
describe('isAbsoltePath()', function () { | ||
it('should escape all special HTML characters', function (done) { | ||
it('identifies if path is absolute on Unix without node support', { parallel: false }, function (done) { | ||
var a = Hoek.escapeHtml('&<>"\'`'); | ||
expect(a).to.equal('&<>"'`'); | ||
done(); | ||
}); | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = undefined; | ||
it('returns empty string on falsy input', function (done) { | ||
expect(Hoek.isAbsolutePath('')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('a')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('./a')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('/a')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('/')).to.equal(true); | ||
var a = Hoek.escapeHtml(''); | ||
expect(a).to.equal(''); | ||
done(); | ||
}); | ||
Path.isAbsolute = orig; | ||
it('returns unchanged string on no reserved input', function (done) { | ||
var a = Hoek.escapeHtml('abc'); | ||
expect(a).to.equal('abc'); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
describe('#nextTick', function () { | ||
it('identifies if path is absolute with fake node support', { parallel: false }, function (done) { | ||
it('calls the provided callback on nextTick', function (done) { | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = function (path) { return path[0] === '/'; }; | ||
var a = 0; | ||
expect(Hoek.isAbsolutePath('', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('a', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('./a', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('/a', 'linux')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('/', 'linux')).to.equal(true); | ||
var inc = function (step, next) { | ||
Path.isAbsolute = orig; | ||
a += step; | ||
next(); | ||
}; | ||
done(); | ||
}); | ||
var ticked = Hoek.nextTick(inc); | ||
it('identifies if path is absolute on Windows without node support', { parallel: false }, function (done) { | ||
ticked(5, function () { | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = undefined; | ||
expect(a).to.equal(6); | ||
done(); | ||
}); | ||
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('\\\\server\\file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:/Users/', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:\\Users\\', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:cwd/another', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('C:cwd\\another', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('directory/directory', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('directory\\directory', 'win32')).to.equal(false); | ||
expect(a).to.equal(0); | ||
inc(1, function () { | ||
Path.isAbsolute = orig; | ||
expect(a).to.equal(1); | ||
}); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
describe('#once', function () { | ||
describe('isInteger()', function () { | ||
it('allows function to only execute once', function (done) { | ||
it('validates integers', function (done) { | ||
var gen = 0; | ||
var add = function (x) { | ||
expect(Hoek.isInteger(0)).to.equal(true); | ||
expect(Hoek.isInteger(1)).to.equal(true); | ||
expect(Hoek.isInteger(1394035612500)).to.equal(true); | ||
expect(Hoek.isInteger('0')).to.equal(false); | ||
expect(Hoek.isInteger(1.0)).to.equal(true); | ||
expect(Hoek.isInteger(1.1)).to.equal(false); | ||
done(); | ||
}); | ||
}); | ||
gen += x; | ||
}; | ||
describe('ignore()', function () { | ||
add(5); | ||
expect(gen).to.equal(5); | ||
add = Hoek.once(add); | ||
add(5); | ||
expect(gen).to.equal(10); | ||
add(5); | ||
expect(gen).to.equal(10); | ||
done(); | ||
}); | ||
it('exists', function (done) { | ||
it('double once wraps one time', function (done) { | ||
var method = function () { }; | ||
method = Hoek.once(method); | ||
method.x = 1; | ||
method = Hoek.once(method); | ||
expect(method.x).to.equal(1); | ||
done(); | ||
}); | ||
expect(Hoek.ignore).to.exist; | ||
expect(typeof Hoek.ignore).to.equal('function'); | ||
done(); | ||
}); | ||
}); | ||
describe('#isAbsoltePath', function () { | ||
describe('inherits()', function () { | ||
it('identifies if path is absolute on Unix without node support', { parallel: false }, function (done) { | ||
it('exists', function (done) { | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = undefined; | ||
expect(Hoek.inherits).to.exist; | ||
expect(typeof Hoek.inherits).to.equal('function'); | ||
done(); | ||
}); | ||
}); | ||
expect(Hoek.isAbsolutePath('')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('a')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('./a')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('/a')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('/')).to.equal(true); | ||
describe('transform()', function () { | ||
Path.isAbsolute = orig; | ||
var source = { | ||
address: { | ||
one: '123 main street', | ||
two: 'PO Box 1234' | ||
}, | ||
zip: { | ||
code: 3321232, | ||
province: null | ||
}, | ||
title: 'Warehouse', | ||
state: 'CA', | ||
}; | ||
done(); | ||
it('transforms an object based on the input object', function (done) { | ||
var result = Hoek.transform(source, { | ||
'person.address.lineOne': 'address.one', | ||
'person.address.lineTwo': 'address.two', | ||
'title': 'title', | ||
'person.address.region': 'state', | ||
'person.address.zip': 'zip.code', | ||
'person.address.location': 'zip.province' | ||
}); | ||
it('identifies if path is absolute with fake node support', { parallel: false }, function (done) { | ||
expect(result).to.deep.equal({ | ||
person: { | ||
address: { | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
region: 'CA', | ||
zip: 3321232, | ||
location: null | ||
} | ||
}, | ||
title: 'Warehouse' | ||
}); | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = function (path) { return path[0] === '/'; }; | ||
done(); | ||
}); | ||
expect(Hoek.isAbsolutePath('', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('a', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('./a', 'linux')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('/a', 'linux')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('/', 'linux')).to.equal(true); | ||
it('uses the reach options passed into it', function (done) { | ||
Path.isAbsolute = orig; | ||
var schema = { | ||
'person.address.lineOne': 'address-one', | ||
'person.address.lineTwo': 'address-two', | ||
'title': 'title', | ||
'person.address.region': 'state', | ||
'person.prefix': 'person-title', | ||
'person.zip': 'zip-code' | ||
}; | ||
var options = { | ||
separator: '-', | ||
default: 'unknown' | ||
}; | ||
var result = Hoek.transform(source, schema, options); | ||
done(); | ||
expect(result).to.deep.equal({ | ||
person: { | ||
address: { | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
region: 'CA' | ||
}, | ||
prefix: 'unknown', | ||
zip: 3321232 | ||
}, | ||
title: 'Warehouse' | ||
}); | ||
it('identifies if path is absolute on Windows without node support', { parallel: false }, function (done) { | ||
done(); | ||
}); | ||
var orig = Path.isAbsolute; | ||
Path.isAbsolute = undefined; | ||
it('works to create shallow objects', function (done) { | ||
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('\\\\server\\file', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:/Users/', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:\\Users\\', 'win32')).to.equal(true); | ||
expect(Hoek.isAbsolutePath('C:cwd/another', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('C:cwd\\another', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('directory/directory', 'win32')).to.equal(false); | ||
expect(Hoek.isAbsolutePath('directory\\directory', 'win32')).to.equal(false); | ||
var result = Hoek.transform(source, { | ||
lineOne: 'address.one', | ||
lineTwo: 'address.two', | ||
title: 'title', | ||
region: 'state', | ||
province: 'zip.province' | ||
}); | ||
Path.isAbsolute = orig; | ||
expect(result).to.deep.equal({ | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
title: 'Warehouse', | ||
region: 'CA', | ||
province: null | ||
}); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
describe('#isInteger', function () { | ||
it('only allows strings in the map', function (done) { | ||
it('validates integers', function (done) { | ||
expect(function () { | ||
var result = Hoek.transform(source, { | ||
lineOne: {} | ||
}); | ||
}).to.throw('All mappings must be "." delineated string'); | ||
expect(Hoek.isInteger(0)).to.equal(true); | ||
expect(Hoek.isInteger(1)).to.equal(true); | ||
expect(Hoek.isInteger(1394035612500)).to.equal(true); | ||
expect(Hoek.isInteger('0')).to.equal(false); | ||
expect(Hoek.isInteger(1.0)).to.equal(true); | ||
expect(Hoek.isInteger(1.1)).to.equal(false); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
describe('#ignore', function () { | ||
it('throws an error on invalid arguments', function (done) { | ||
it('exists', function (done) { | ||
expect(function () { | ||
expect(Hoek.ignore).to.exist; | ||
expect(typeof Hoek.ignore).to.equal('function'); | ||
done(); | ||
}); | ||
var result = Hoek.transform(NaN, {}); | ||
}).to.throw('Invalid source object: must be null, undefined, or an object'); | ||
done(); | ||
}); | ||
describe('#inherits', function () { | ||
it('is safe to pass null or undefined', function (done) { | ||
it('exists', function (done) { | ||
var result = Hoek.transform(null, {}); | ||
expect(result).to.deep.equal({}); | ||
expect(Hoek.inherits).to.exist; | ||
expect(typeof Hoek.inherits).to.equal('function'); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
describe('#transform', function () { | ||
describe('uniqueFilename()', function () { | ||
var source = { | ||
address: { | ||
one: '123 main street', | ||
two: 'PO Box 1234' | ||
}, | ||
zip: { | ||
code: 3321232, | ||
province: null | ||
}, | ||
title: 'Warehouse', | ||
state: 'CA', | ||
}; | ||
it('generates a random file path', function (done) { | ||
it('transforms an object based on the input object', function (done) { | ||
var result = Hoek.uniqueFilename('./test/modules'); | ||
var result = Hoek.transform(source, { | ||
'person.address.lineOne': 'address.one', | ||
'person.address.lineTwo': 'address.two', | ||
'title': 'title', | ||
'person.address.region': 'state', | ||
'person.address.zip': 'zip.code', | ||
'person.address.location': 'zip.province' | ||
}); | ||
expect(result).to.exist; | ||
expect(result).to.be.a('string'); | ||
expect(result).to.contain('test/modules'); | ||
done(); | ||
}); | ||
expect(result).to.deep.equal({ | ||
person: { | ||
address: { | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
region: 'CA', | ||
zip: 3321232, | ||
location: null | ||
} | ||
}, | ||
title: 'Warehouse' | ||
}); | ||
it('is random enough to use in fast loops', function (done) { | ||
done(); | ||
}); | ||
var results = []; | ||
it('uses the reach options passed into it', function (done) { | ||
for (var i = 0; i < 10; ++i) { | ||
results[i] = Hoek.uniqueFilename('./test/modules'); | ||
} | ||
var schema = { | ||
'person.address.lineOne': 'address-one', | ||
'person.address.lineTwo': 'address-two', | ||
'title': 'title', | ||
'person.address.region': 'state', | ||
'person.prefix': 'person-title', | ||
'person.zip': 'zip-code' | ||
}; | ||
var options = { | ||
separator: '-', | ||
default: 'unknown' | ||
}; | ||
var result = Hoek.transform(source, schema, options); | ||
var filter = results.filter(function (item, index, array) { | ||
expect(result).to.deep.equal({ | ||
person: { | ||
address: { | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
region: 'CA' | ||
}, | ||
prefix: 'unknown', | ||
zip: 3321232 | ||
}, | ||
title: 'Warehouse' | ||
}); | ||
done(); | ||
return array.indexOf(item) === index; | ||
}); | ||
it('works to create shallow objects', function (done) { | ||
expect(filter.length).to.equal(10); | ||
expect(results.length).to.equal(10); | ||
done(); | ||
var result = Hoek.transform(source, { | ||
lineOne: 'address.one', | ||
lineTwo: 'address.two', | ||
title: 'title', | ||
region: 'state', | ||
province: 'zip.province' | ||
}); | ||
}); | ||
expect(result).to.deep.equal({ | ||
lineOne: '123 main street', | ||
lineTwo: 'PO Box 1234', | ||
title: 'Warehouse', | ||
region: 'CA', | ||
province: null | ||
}); | ||
it('combines the random elements with a supplied character', function (done) { | ||
done(); | ||
}); | ||
var result = Hoek.uniqueFilename('./test', 'txt'); | ||
it('only allows strings in the map', function (done) { | ||
expect(result).to.contain('test/'); | ||
expect(result).to.contain('.txt'); | ||
expect(function () { | ||
var result = Hoek.transform(source, { | ||
lineOne: {} | ||
}); | ||
}).to.throw('All mappings must be "." delineated string'); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
it('accepts extensions with a "." in it', function (done) { | ||
it('throws an error on invalid arguments', function (done) { | ||
var result = Hoek.uniqueFilename('./test', '.mp3'); | ||
expect(function () { | ||
expect(result).to.contain('test/'); | ||
expect(result).to.contain('.mp3'); | ||
var result = Hoek.transform(NaN, {}); | ||
}).to.throw('Invalid source object: must be null, undefined, or an object'); | ||
done(); | ||
}); | ||
it('is safe to pass null or undefined', function (done) { | ||
var result = Hoek.transform(null, {}); | ||
expect(result).to.deep.equal({}); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
113082
1727
463
1