UNPKG

dynatrace-cordova-outsystems-plugin

Version:

This plugin gives you the ability to use the Dynatrace instrumentation in your hybrid application (Cordova, Ionic, ..). It uses the Mobile Agent, the JavaScript Agent and the Javascript Bridge. The Mobile Agent will give you all device specific values con

1,889 lines (1,404 loc) 67.3 kB
// Load modules var Fs = require('fs'); var Path = require('path'); var Code = require('code'); var Hoek = require('../lib'); var Lab = require('lab'); // Declare internals var internals = {}; // Test shortcuts var lab = exports.lab = Lab.script(); var describe = lab.experiment; var it = lab.test; var expect = Code.expect; 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' }]; describe('clone()', function () { it('clones a nested object', function (done) { var a = nestedObj; var b = Hoek.clone(a); expect(a).to.deep.equal(b); expect(a.z.getTime()).to.equal(b.z.getTime()); done(); }); it('clones a null object', function (done) { var b = Hoek.clone(null); expect(b).to.equal(null); done(); }); it('should not convert undefined properties to null', function (done) { var obj = { something: undefined }; var b = Hoek.clone(obj); 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); }; expect(test).to.not.throw(); done(); }); it('clones circular reference', function (done) { var x = { 'z': new Date() }; x.y = x; 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 a null prototype', function (done) { var obj = Object.create(null); var b = Hoek.clone(obj); expect(b).to.deep.equal(obj); done(); }); it('clones deeply nested object', function (done) { var a = { x: { y: { a: [1, 2, 3], b: 123456, c: new Date(), d: /hi/igm, e: /hello/ } } }; 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(); }); it('clones arrays', function (done) { var a = [1, 2, 3]; var b = Hoek.clone(a); expect(a).to.deep.equal(b); done(); }); it('performs actual copy for shallow keys (no pass by reference)', function (done) { var x = Hoek.clone(nestedObj); var y = Hoek.clone(nestedObj); // Date expect(x.z).to.not.equal(nestedObj.z); expect(x.z).to.not.equal(y.z); // Regex expect(x.w).to.not.equal(nestedObj.w); expect(x.w).to.not.equal(y.w); // Array expect(x.v).to.not.equal(nestedObj.v); expect(x.v).to.not.equal(y.v); // Immutable(s) x.y = 5; expect(x.y).to.not.equal(nestedObj.y); expect(x.y).to.not.equal(y.y); done(); }); it('performs actual copy for deep keys (no pass by reference)', function (done) { var x = Hoek.clone(nestedObj); var y = Hoek.clone(nestedObj); expect(x.x.c).to.not.equal(nestedObj.x.c); expect(x.x.c).to.not.equal(y.x.c); expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime()); expect(x.x.c.getTime()).to.equal(y.x.c.getTime()); done(); }); it('copies functions with properties', function (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; 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(); }); 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]) }; 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(); }); it('clones an object with a prototype', function (done) { var Obj = function () { this.a = 5; }; Obj.prototype.b = function () { return 'c'; }; var a = new Obj(); var b = Hoek.clone(a); expect(b.a).to.equal(5); expect(b.b()).to.equal('c'); expect(a).to.deep.equal(b); done(); }); it('reuses cloned Date object', function (done) { var obj = { a: new Date() }; obj.b = obj.a; var copy = Hoek.clone(obj); expect(copy.a).to.equal(copy.b); done(); }); it('shallow copies an object with a prototype and isImmutable flag', function (done) { var Obj = function () { this.value = 5; }; Obj.prototype.b = function () { return 'c'; }; Obj.prototype.isImmutable = true; var obj = { a: new Obj() }; var copy = Hoek.clone(obj); 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(); }); it('clones an object with property getter without executing it', function (done) { var obj = {}; var value = 1; var execCount = 0; Object.defineProperty(obj, 'test', { enumerable: true, configurable: true, get: function () { ++execCount; return value; } }); var copy = Hoek.clone(obj); expect(execCount).to.equal(0); expect(copy.test).to.equal(1); expect(execCount).to.equal(1); done(); }); it('clones an object with property getter and setter', function (done) { var obj = { _test: 0 }; Object.defineProperty(obj, 'test', { enumerable: true, configurable: true, get: function () { return this._test; }, set: function (value) { this._test = value - 1; } }); var copy = Hoek.clone(obj); expect(copy.test).to.equal(0); copy.test = 5; expect(copy.test).to.equal(4); done(); }); it('clones an object with only property setter', function (done) { var obj = { _test: 0 }; Object.defineProperty(obj, 'test', { enumerable: true, configurable: true, set: function (value) { this._test = value - 1; } }); var copy = Hoek.clone(obj); expect(copy._test).to.equal(0); copy.test = 5; expect(copy._test).to.equal(4); done(); }); it('clones an object with non-enumerable properties', function (done) { var obj = { _test: 0 }; Object.defineProperty(obj, 'test', { enumerable: false, configurable: true, set: function (value) { this._test = value - 1; } }); var copy = Hoek.clone(obj); expect(copy._test).to.equal(0); copy.test = 5; expect(copy._test).to.equal(4); done(); }); it('clones an object where getOwnPropertyDescriptor returns undefined', function (done) { var oldGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var obj = { a: 'b' }; Object.getOwnPropertyDescriptor = function () { return undefined; }; var copy = Hoek.clone(obj); Object.getOwnPropertyDescriptor = oldGetOwnPropertyDescriptor; expect(copy).to.deep.equal(obj); done(); }); }); describe('merge()', function () { it('deep copies source items', function (done) { var target = { b: 3, d: [] }; 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(); }); it('merges array over an object', function (done) { var a = { x: ['n', 'm'] }; var b = { x: { n: '1', m: '2' } }; Hoek.merge(b, a); expect(a.x[0]).to.equal('n'); expect(a.x.n).to.not.exist(); done(); }); it('merges object over an array', function (done) { var a = { x: ['n', 'm'] }; 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(); }); it('does not throw if source is null', function (done) { var a = {}; var b = null; var c = null; expect(function () { c = Hoek.merge(a, b); }).to.not.throw(); expect(c).to.equal(a); done(); }); it('does not throw if source is undefined', function (done) { var a = {}; var b; var c = null; expect(function () { c = Hoek.merge(a, b); }).to.not.throw(); expect(c).to.equal(a); done(); }); it('throws if source is not an object', function (done) { expect(function () { var a = {}; var b = 0; Hoek.merge(a, b); }).to.throw('Invalid source value: must be null, undefined, or an object'); done(); }); it('throws if target is not an object', function (done) { expect(function () { var a = 0; var b = {}; Hoek.merge(a, b); }).to.throw('Invalid target value: must be an object'); done(); }); it('throws if target is not an array and source is', function (done) { expect(function () { var a = {}; var b = [1, 2]; Hoek.merge(a, b); }).to.throw('Cannot merge array onto an object'); done(); }); it('returns the same object when merging arrays', function (done) { var a = []; var b = [1, 2]; expect(Hoek.merge(a, b)).to.equal(a); done(); }); it('combines an empty object with a non-empty object', function (done) { var a = {}; var b = nestedObj; var c = Hoek.merge(a, b); expect(a).to.deep.equal(b); expect(c).to.deep.equal(b); done(); }); it('overrides values in target', 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 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('overrides values in target (flip)', 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 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('retains Date properties', function (done) { var a = { x: new Date(1378776452757) }; var b = Hoek.merge({}, a); expect(a.x.getTime()).to.equal(b.x.getTime()); done(); }); it('retains Date properties when merging keys', function (done) { var a = { x: new Date(1378776452757) }; var b = Hoek.merge({ x: {} }, a); expect(a.x.getTime()).to.equal(b.x.getTime()); done(); }); it('overrides Buffer', function (done) { var a = { x: new Buffer('abc') }; var b = Hoek.merge({ x: {} }, a); expect(a.x.toString()).to.equal('abc'); done(); }); }); describe('applyToDefaults()', function () { var defaults = { a: 1, b: 2, c: { d: 3, e: [5, 6] }, f: 6, g: 'test' }; it('throws when target is null', function (done) { expect(function () { Hoek.applyToDefaults(null, {}); }).to.throw('Invalid defaults value: must be an object'); done(); }); it('returns null if options is false', function (done) { var result = Hoek.applyToDefaults(defaults, false); expect(result).to.equal(null); done(); }); it('returns null if options is null', function (done) { 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: { e: [4] }, f: 0, g: { h: 5 } }; 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('applies object to defaults with null', function (done) { var obj = { a: null, c: { e: [4] }, f: 0, g: { h: 5 } }; var result = Hoek.applyToDefaults(defaults, obj, true); expect(result.c.e).to.deep.equal([4]); expect(result.a).to.equal(null); expect(result.b).to.equal(2); expect(result.f).to.equal(0); expect(result.g).to.deep.equal({ h: 5 }); done(); }); }); describe('cloneWithShallow()', function () { it('deep clones except for listed keys', function (done) { var source = { a: { b: 5 }, c: { d: 6 } }; 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 immutable value', function (done) { expect(Hoek.cloneWithShallow(5)).to.equal(5); done(); }); it('returns null value', function (done) { expect(Hoek.cloneWithShallow(null)).to.equal(null); done(); }); it('returns undefined value', function (done) { expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined); done(); }); it('deep clones except for listed keys (including missing keys)', function (done) { 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(); }); }); describe('applyToDefaultsWithShallow()', function () { it('shallow copies the listed keys from options without merging', function (done) { var defaults = { a: { b: 5, e: 3 }, c: { d: 7, g: 1 } }; var options = { a: { b: 4 }, c: { d: 6, f: 7 } }; 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('shallow copies the nested keys (override)', function (done) { var defaults = { a: { b: 5 }, c: { d: 7, g: 1 } }; var options = { a: { b: 4 }, c: { d: 6, g: { h: 8 } } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']); expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: { h: 8 } } }); expect(merged.c.g).to.equal(options.c.g); done(); }); it('shallow copies the nested keys (missing)', function (done) { var defaults = { a: { b: 5 } }; var options = { a: { b: 4 }, c: { g: { h: 8 } } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']); expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } }); expect(merged.c.g).to.equal(options.c.g); done(); }); it('shallow copies the nested keys (override)', function (done) { var defaults = { a: { b: 5 }, c: { g: { d: 7 } } }; var options = { a: { b: 4 }, c: { g: { h: 8 } } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']); expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } }); expect(merged.c.g).to.equal(options.c.g); done(); }); it('shallow copies the nested keys (deeper)', function (done) { var defaults = { a: { b: 5 } }; var options = { a: { b: 4 }, c: { g: { r: { h: 8 } } } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g.r']); expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } }); expect(merged.c.g.r).to.equal(options.c.g.r); done(); }); it('shallow copies the nested keys (not present)', function (done) { var defaults = { a: { b: 5 } }; var options = { a: { b: 4 }, c: { g: { r: { h: 8 } } } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['x.y']); expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } }); done(); }); it('shallow copies the listed keys in the defaults', function (done) { var defaults = { a: { b: 1 } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, {}, ['a']); expect(merged.a).to.equal(defaults.a); done(); }); it('shallow copies the listed keys in the defaults (true)', function (done) { var defaults = { a: { b: 1 } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, true, ['a']); expect(merged.a).to.equal(defaults.a); done(); }); it('returns null on false', function (done) { var defaults = { a: { b: 1 } }; var merged = Hoek.applyToDefaultsWithShallow(defaults, false, ['a']); expect(merged).to.equal(null); done(); }); it('throws on missing defaults', function (done) { expect(function () { Hoek.applyToDefaultsWithShallow(null, {}, ['a']); }).to.throw('Invalid defaults value: must be an object'); done(); }); it('throws on invalid defaults', function (done) { expect(function () { Hoek.applyToDefaultsWithShallow('abc', {}, ['a']); }).to.throw('Invalid defaults value: must be an object'); done(); }); it('throws on invalid options', function (done) { expect(function () { Hoek.applyToDefaultsWithShallow({}, 'abc', ['a']); }).to.throw('Invalid options value: must be true, falsy or an object'); done(); }); it('throws on missing keys', function (done) { expect(function () { Hoek.applyToDefaultsWithShallow({}, true); }).to.throw('Invalid keys'); done(); }); it('throws on invalid keys', function (done) { expect(function () { Hoek.applyToDefaultsWithShallow({}, true, 'a'); }).to.throw('Invalid keys'); done(); }); }); describe('deepEqual()', function () { it('compares simple values', function (done) { expect(Hoek.deepEqual('x', 'x')).to.be.true(); expect(Hoek.deepEqual('x', 'y')).to.be.false(); expect(Hoek.deepEqual('x1', 'x')).to.be.false(); expect(Hoek.deepEqual(-0, +0)).to.be.false(); expect(Hoek.deepEqual(-0, -0)).to.be.true(); expect(Hoek.deepEqual(+0, +0)).to.be.true(); expect(Hoek.deepEqual(+0, -0)).to.be.false(); expect(Hoek.deepEqual(1, 1)).to.be.true(); expect(Hoek.deepEqual(0, 0)).to.be.true(); expect(Hoek.deepEqual(-1, 1)).to.be.false(); expect(Hoek.deepEqual(NaN, 0)).to.be.false(); expect(Hoek.deepEqual(NaN, NaN)).to.be.true(); done(); }); it('compares different types', function (done) { expect(Hoek.deepEqual([], 5)).to.be.false(); expect(Hoek.deepEqual(5, [])).to.be.false(); expect(Hoek.deepEqual({}, null)).to.be.false(); expect(Hoek.deepEqual(null, {})).to.be.false(); expect(Hoek.deepEqual('abc', {})).to.be.false(); expect(Hoek.deepEqual({}, 'abc')).to.be.false(); done(); }); it('compares empty structures', function (done) { expect(Hoek.deepEqual([], [])).to.be.true(); expect(Hoek.deepEqual({}, {})).to.be.true(); expect(Hoek.deepEqual([], {})).to.be.false(); done(); }); it('compares empty arguments object', function (done) { var compare = function () { expect(Hoek.deepEqual([], arguments)).to.be.false(); }; compare(); done(); }); it('compares empty arguments objects', function (done) { var compare = function () { var arg1 = arguments; var inner = function () { expect(Hoek.deepEqual(arg1, arguments)).to.be.false(); // callee is not the same }; inner(); }; compare(); done(); }); it('compares dates', function (done) { expect(Hoek.deepEqual(new Date(2015, 1, 1), new Date(2015, 1, 1))).to.be.true(); expect(Hoek.deepEqual(new Date(100), new Date(101))).to.be.false(); expect(Hoek.deepEqual(new Date(), {})).to.be.false(); done(); }); it('compares regular expressions', function (done) { expect(Hoek.deepEqual(/\s/, new RegExp('\\\s'))).to.be.true(); expect(Hoek.deepEqual(/\s/g, /\s/g)).to.be.true(); expect(Hoek.deepEqual(/a/, {})).to.be.false(); expect(Hoek.deepEqual(/\s/g, /\s/i)).to.be.false(); expect(Hoek.deepEqual(/a/g, /b/g)).to.be.false(); done(); }); it('compares arrays', function (done) { expect(Hoek.deepEqual([[1]], [[1]])).to.be.true(); expect(Hoek.deepEqual([1, 2, 3], [1, 2, 3])).to.be.true(); expect(Hoek.deepEqual([1, 2, 3], [1, 3, 2])).to.be.false(); expect(Hoek.deepEqual([1, 2, 3], [1, 2])).to.be.false(); expect(Hoek.deepEqual([1], [1])).to.be.true(); done(); }); it('compares buffers', function (done) { expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2, 3]))).to.be.true(); expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 3, 2]))).to.be.false(); expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2]))).to.be.false(); expect(Hoek.deepEqual(new Buffer([1, 2, 3]), {})).to.be.false(); expect(Hoek.deepEqual(new Buffer([1, 2, 3]), [1, 2, 3])).to.be.false(); done(); }); it('compares objects', function (done) { expect(Hoek.deepEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).to.be.true(); expect(Hoek.deepEqual({ foo: 'bar' }, { foo: 'baz' })).to.be.false(); expect(Hoek.deepEqual({ foo: { bar: 'foo' } }, { foo: { bar: 'baz' } })).to.be.false(); done(); }); it('handles circular dependency', function (done) { var a = {}; a.x = a; var b = Hoek.clone(a); expect(Hoek.deepEqual(a, b)).to.be.true(); done(); }); it('compares an object with property getter without executing it', function (done) { var obj = {}; var value = 1; var execCount = 0; Object.defineProperty(obj, 'test', { enumerable: true, configurable: true, get: function () { ++execCount; return value; } }); var copy = Hoek.clone(obj); expect(Hoek.deepEqual(obj, copy)).to.be.true(); expect(execCount).to.equal(0); expect(copy.test).to.equal(1); expect(execCount).to.equal(1); done(); }); it('compares objects with property getters', function (done) { var obj = {}; Object.defineProperty(obj, 'test', { enumerable: true, configurable: true, get: function () { return 1; } }); var ref = {}; Object.defineProperty(ref, 'test', { enumerable: true, configurable: true, get: function () { return 2; } }); expect(Hoek.deepEqual(obj, ref)).to.be.false(); done(); }); it('compares object prototypes', function (done) { var Obj = function () { this.a = 5; }; Obj.prototype.b = function () { return this.a; }; var Ref = function () { this.a = 5; }; Ref.prototype.b = function () { return this.a; }; expect(Hoek.deepEqual(new Obj(), new Ref())).to.be.false(); expect(Hoek.deepEqual(new Obj(), new Obj())).to.be.true(); expect(Hoek.deepEqual(new Ref(), new Ref())).to.be.true(); done(); }); it('compares plain objects', function (done) { var a = Object.create(null); var b = Object.create(null); a.b = 'c'; b.b = 'c'; expect(Hoek.deepEqual(a, b)).to.be.true(); expect(Hoek.deepEqual(a, { b: 'c' })).to.be.false(); done(); }); it('compares an object with an empty object', function (done) { var a = { a: 1, b: 2 }; expect(Hoek.deepEqual({}, a)).to.be.false(); expect(Hoek.deepEqual(a, {})).to.be.false(); done(); }); it('compares an object ignoring the prototype', function (done) { var a = Object.create(null); var b = {}; expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true(); done(); }); it('compares an object ignoring the prototype recursively', function (done) { var a = [Object.create(null)]; var b = [{}]; expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true(); done(); }); }); describe('unique()', function () { it('ensures uniqueness within array of objects based on subkey', function (done) { var a = Hoek.unique(dupsArray, 'x'); expect(a).to.deep.equal(reducedDupsArray); done(); }); it('removes duplicated without key', function (done) { expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]); done(); }); }); describe('mapToObject()', function () { it('returns null on null array', function (done) { var a = Hoek.mapToObject(null); expect(a).to.equal(null); done(); }); it('converts basic array to existential object', function (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('converts array of objects to existential object', 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(); }); }); describe('intersect()', function () { it('returns the common objects of two arrays', 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(); }); it('returns just the first common object of two arrays', 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(); }); it('returns null when no common and returning just the first common object of two arrays', 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(); }); it('returns an empty array if either input is null', function (done) { expect(Hoek.intersect([1], null).length).to.equal(0); expect(Hoek.intersect(null, [1]).length).to.equal(0); 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('contain()', function () { it('tests strings', function (done) { expect(Hoek.contain('abc', 'ab')).to.be.true(); expect(Hoek.contain('abc', 'abc', { only: true })).to.be.true(); expect(Hoek.contain('aaa', 'a', { only: true })).to.be.true(); expect(Hoek.contain('abc', 'b', { once: true })).to.be.true(); expect(Hoek.contain('abc', ['a', 'c'])).to.be.true(); expect(Hoek.contain('abc', ['a', 'd'], { part: true })).to.be.true(); expect(Hoek.contain('abc', 'ac')).to.be.false(); expect(Hoek.contain('abcd', 'abc', { only: true })).to.be.false(); expect(Hoek.contain('aab', 'a', { only: true })).to.be.false(); expect(Hoek.contain('abb', 'b', { once: true })).to.be.false(); expect(Hoek.contain('abc', ['a', 'd'])).to.be.false(); expect(Hoek.contain('abc', ['ab', 'bc'])).to.be.false(); // Overlapping values not supported done(); }); it('tests arrays', function (done) { expect(Hoek.contain([1, 2, 3], 1)).to.be.true(); expect(Hoek.contain([{ a: 1 }], { a: 1 }, { deep: true })).to.be.true(); expect(Hoek.contain([1, 2, 3], [1, 2])).to.be.true(); expect(Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true })).to.be.true(); expect(Hoek.contain([1, 1, 2], [1, 2], { only: true })).to.be.true(); expect(Hoek.contain([1, 2], [1, 2], { once: true })).to.be.true(); expect(Hoek.contain([1, 2, 3], [1, 4], { part: true })).to.be.true(); expect(Hoek.contain([[1], [2]], [[1]], { deep: true })).to.be.true(); expect(Hoek.contain([1, 2, 3], 4)).to.be.false(); expect(Hoek.contain([{ a: 1 }], { a: 2 }, { deep: true })).to.be.false(); expect(Hoek.contain([{ a: 1 }], { a: 1 })).to.be.false(); expect(Hoek.contain([1, 2, 3], [4, 5])).to.be.false(); expect(Hoek.contain([[3], [2]], [[1]])).to.be.false(); expect(Hoek.contain([[1], [2]], [[1]])).to.be.false(); expect(Hoek.contain([{ a: 1 }], [{ a: 2 }], { deep: true })).to.be.false(); expect(Hoek.contain([1, 3, 2], [1, 2], { only: true })).to.be.false(); expect(Hoek.contain([1, 2, 2], [1, 2], { once: true })).to.be.false(); expect(Hoek.contain([0, 2, 3], [1, 4], { part: true })).to.be.false(); done(); }); it('tests objects', function (done) { expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'a')).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'c'])).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'b', 'c'], { only: true })).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1 })).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 })).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true })).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, { only: true })).to.be.true(); expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] }, { deep: true })).to.be.true(); expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true })).to.be.true(); expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: true })).to.be.true(); expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: false })).to.be.false(); expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: true })).to.be.false(); expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: false })).to.be.true(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'd')).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'd'])).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'b', 'c'], { only: true })).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2 })).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2, b: 2 }, { part: true })).to.be.false(); // part does not ignore bad value expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 3 })).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 })).to.be.false(); expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 }, { only: true })).to.be.false(); expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] })).to.be.false(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } })).to.be.false(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true })).to.be.true(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: true })).to.be.false(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: false })).to.be.true(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: true })).to.be.true(); expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: false })).to.be.false(); // Getter check var Foo = function (bar) { this.bar = bar; }; Object.defineProperty(Foo.prototype, 'baz', { enumerable: true, get: function () { return this.bar; } }); expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true })).to.be.true(); expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true, part: true })).to.be.true(); expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true })).to.be.true(); expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, only: true })).to.be.false(); expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: false })).to.be.false(); expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: true })).to.be.true(); done(); }); }); describe('flatten()', function () { it('returns a flat array', function (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(); }); }); describe('reach()', function () { var obj = { a: { b: { c: { d: 1, e: 2 }, f: 'hello' }, g: { h: 3 } }, i: function () { }, j: null, k: [4, 8, 9, 1] }; obj.i.x = 5; it('returns object itself', function (done) { expect(Hoek.reach(obj, null)).to.equal(obj); expect(Hoek.reach(obj, false)).to.equal(obj); expect(Hoek.reach(obj)).to.equal(obj); done(); }); it('returns first value of array', function (done) { expect(Hoek.reach(obj, 'k.0')).to.equal(4); done(); }); it('returns last value of array using negative index', function (done) { expect(Hoek.reach(obj, 'k.-2')).to.equal(9); done(); }); it('returns a valid member', function (done) { expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1); done(); }); it('returns a valid member with separator override', function (done) { expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1); done(); }); it('returns undefined on null object', function (done) { expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined); done(); }); it('returns undefined on missing object member', function (done) { expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined); done(); }); it('returns undefined on missing function member', function (done) { expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined); done(); }); it('throws on missing member in strict mode', function (done) { expect(function () { Hoek.reach(obj, 'a.b.c.o.x', { strict: true }); }).to.throw('Missing segment o in reach path a.b.c.o.x'); done(); }); it('returns undefined on invalid member', function (done) { expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined); done(); }); it('returns function member', function (done) { expect(typeof Hoek.reach(obj, 'i')).to.equal('function'); done(); }); it('returns function property', function (done) { expect(Hoek.reach(obj, 'i.x')).to.equal(5); done(); }); it('returns null', function (done) { expect(Hoek.reach(obj, 'j')).to.equal(null); done(); }); it('throws on function property when functions not allowed', function (done) { expect(function () { Hoek.reach(obj, 'i.x', { functions: false }); }).to.throw('Invalid segment x in reach path i.x'); done(); }); it('will return a default value if property is not found', function (done) { expect(Hoek.reach(obj, 'a.b.q', { default: 'defaultValue' })).to.equal('defaultValue'); done(); }); it('will return a default value if path is not found', function (done) { expect(Hoek.reach(obj, 'q', { default: 'defaultValue' })).to.equal('defaultValue'); done(); }); it('allows a falsey value to be used as the default value', function (done) { expect(Hoek.reach(obj, 'q', { default: '' })).to.equal(''); done(); }); }); describe('reachTemplate()', function () { it('applies object to template', function (done) { var obj = { a: { b: { c: { d: 1 } } }, j: null, k: [4, 8, 9, 1] }; var template = '{k.0}:{k.-2}:{a.b.c.d}:{x.y}:{j}'; expect(Hoek.reachTemplate(obj, template)).to.equal('4:9:1::'); done(); }); it('applies object to template (options)', function (done) { var obj = { a: { b: { c: { d: 1 } } }, j: null, k: [4, 8, 9, 1] }; var template = '{k/0}:{k/-2}:{a/b/c/d}:{x/y}:{j}'; expect(Hoek.reachTemplate(obj, template, '/')).to.equal('4:9:1::'); done(); }); }); describe('callStack()', function () { it('returns the full call stack', function (done) { var stack = Hoek.callStack(); expect(stack[0][0]).to.contain('index.js'); expect(stack[0][2]).to.equal(26); done(); }); }); describe('displayStack ()', function () { it('returns the full call stack for display', function (done) { var stack = Hoek.displayStack(); expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':'); done(); }); it('includes constructor functions correctly', function (done) { var Something = function (next) { next(); }; var something = new Something(function () { var stack = Hoek.displayStack(); expect(stack[1]).to.contain('new Something'); done(); }); }); }); describe('abort()', function () { it('exits 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'); }); it('throws when not in test mode and abortThrow is true', function (done) { var env = process.env.NODE_ENV; process.env.NODE_ENV = 'nottatest'; Hoek.abortThrow = true; var fn = function () { Hoek.abort('my error message'); }; expect(fn).to.throw('my error message'); Hoek.abortThrow = false; process.env.NODE_ENV = env; done(); }); it('respects hideStack argument', function (done) { var env = process.env.NODE_ENV; var write = process.stdout.write; var exit = process.exit; var output = ''; process.exit = function () { }; process.env.NODE_ENV = ''; process.stdout.write = function (message) { output = message; }; Hoek.abort('my error message', true); process.env.NODE_ENV = env; process.stdout.write = write; process.exit = exit; expect(output).to.equal('ABORT: my error message\n\t\n'); done(); }); it('throws in test mode', function (done) { var env = process.env.NODE_ENV; process.env.NODE_ENV = 'test'; expect(function () { Hoek.abort('my error message', true); }).to.throw('my error message'); process.env.NODE_ENV = env; done(); }); it('throws in test mode with default message', function (done) { var env = process.env.NODE_ENV; process.env.NODE_ENV = 'test'; expect(function () { Hoek.abort('', true); }).to.throw('Unknown error'); process.env.NODE_ENV = env; done(); }); it('defaults to showing stack', function (done) { var env = process.env.NODE_ENV; var write = process.stdout.write; var exit = process.exit; var output = ''; process.exit = function () { }; process.env.NODE_ENV = ''; process.stdout.write = function (message) { output = message; }; Hoek.abort('my error message'); process.env.NODE_ENV = env; process.stdout.write = write; process.exit = exit; expect(output).to.contain('index.js'); done(); }); }); describe('assert()', function () { it('throws an Error when using assert in a test', function (done) { var fn = function () { Hoek.assert(false, 'my error message'); }; expect(fn).to.throw('my error message'); done(); }); it('throws an Error when using assert in a test with no message', function (done) { var fn = function () { Hoek.assert(false); }; expect(fn).to.throw('Unknown error'); done(); }); it('throws an Error when using assert in a test with multipart message', function (done) { var fn = function () { Hoek.assert(false, 'This', 'is', 'my message'); }; expect(fn).to.throw('This is my message'); done(); }); it('throws an Error when using assert in a test with multipart message (empty)', function (done) { var fn = function () { Hoek.assert(false, 'This', 'is', '', 'my message'); }; expect(fn).to.throw('This is my message'); done(); }); it('throws an Error when using assert in a test with object message', function (done) { var fn = function () { Hoek.assert(false, 'This', 'is', { spinal: 'tap' }); }; expect(fn).to.throw('This is {"spinal":"tap"}'); done(); }); it('throws an Error when using assert in a test with multipart string and error messages', function (done) { var fn = function () { Hoek.assert(false, 'This', 'is', new Error('spinal'), new Error('tap')); }; expect(fn).to.throw('This is spinal tap'); done(); }); it('throws an Error when using assert in a test with error object message', function (done) { var fn = function () { Hoek.assert(false, new Error('This is spinal tap')); }; expect(fn).to.throw('This is spinal tap'); done(); }); it('throws the same Error that is passed to it if there is only one error passed', function (done) { var error = new Error('ruh roh'); var error2 = new Error('ruh roh'); var fn = function () { Hoek.assert(false, error); }; try { fn(); } catch (err) { expect(error).to.equal(error); // should be the same reference expect(error).to.not.equal(error2); // error with the same message should not match } done(); }); }); describe('Timer', function () { it('returns time elapsed', function (done) { var timer = new Hoek.Timer(); setTimeout(fun