objtools
Version:
Various utility functions for working with object, including object merging, inheritance, deep copying, etc.
725 lines (700 loc) • 24 kB
text/typescript
// Copyright 2016 Zipscene, LLC
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
import { expect } from 'chai';
import _ from 'lodash';
import * as objtools from '../lib/index.js';
class TestClass {
testMethod() {
return true;
}
}
describe('Base Functions', function() {
describe('isScalar()', function() {
it('should return true for scalar values', function() {
expect(objtools.isScalar(true)).to.be.true;
expect(objtools.isScalar(false)).to.be.true;
expect(objtools.isScalar(new Date())).to.be.true;
expect(objtools.isScalar(123)).to.be.true;
expect(objtools.isScalar(null)).to.be.true;
expect(objtools.isScalar(undefined)).to.be.true;
expect(objtools.isScalar(function() {})).to.be.true;
});
it('should return false for non-scalar values', function() {
expect(objtools.isScalar({})).to.be.false;
expect(objtools.isScalar([])).to.be.false;
expect(objtools.isScalar(new Error())).to.be.false;
});
});
describe('scalarEquals', function() {
const date1 = new Date('2014-01-01T00:00:00Z');
const date2 = new Date('2014-01-01T00:00:00Z');
const date3 = new Date('2014-01-01T00:00:01Z');
const func = function() {};
const obj = {};
it('should handle dates', function() {
expect(objtools.scalarEquals(date1, date2)).to.be.true;
expect(objtools.scalarEquals(date2, date3)).to.be.false;
});
it('should handle other types', function() {
expect(objtools.scalarEquals(2, 2)).to.be.true;
expect(objtools.scalarEquals(true, true)).to.be.true;
expect(objtools.scalarEquals(null, null)).to.be.true;
expect(objtools.scalarEquals(undefined, undefined)).to.be.true;
expect(objtools.scalarEquals(0, null)).to.be.false;
expect(objtools.scalarEquals(obj, obj)).to.be.true;
expect(objtools.scalarEquals(func, func)).to.be.true;
expect(objtools.scalarEquals(obj, func)).to.be.false;
expect(objtools.scalarEquals({}, {})).to.be.false;
});
});
describe('deepEquals()', function() {
const date1 = new Date('2014-01-01T00:00:00Z');
const date2 = new Date('2014-01-01T00:00:00Z');
const date3 = new Date('2014-01-01T00:00:01Z');
const obj1 = { foo: { bar: 'baz', biz: [ 1, 2 ] } };
const obj2 = { foo: { bar: 'baz', biz: [ 1, 2 ] } };
const obj3 = { foo: { bar: 'biz' }, biz: [ 1, 2 ] };
let obj4 = { foo: { bar: 'biz' }, biz: [ 1 ] };
it('should handle dates correctly', function() {
expect(objtools.deepEquals(date1, date2)).to.be.true;
expect(objtools.deepEquals(date2, date3)).to.be.false;
expect(objtools.deepEquals({ d: date1 }, { d: date2 })).to.be.true;
});
it('should handle objects correctly', function() {
expect(objtools.deepEquals(obj1, obj2)).to.be.true;
expect(objtools.deepEquals(obj2, obj3)).to.be.false;
expect(objtools.deepEquals(obj3, obj4)).to.be.false;
});
it('should not coerce types', function() {
expect(objtools.deepEquals({ a: null }, { a: null })).to.be.true;
expect(objtools.deepEquals({ a: undefined }, { a: undefined })).to.be.true;
expect(objtools.deepEquals({ a: null }, { a: undefined })).to.be.false;
expect(objtools.deepEquals({ a: 0 }, { a: null })).to.be.false;
});
});
describe('deepCopy()', function() {
const obj1 = {
foo: 'bar',
fuzz: 123,
biz: { dat: new Date('2014-01-01T00:00:00Z'), n: null, u: undefined },
arr: [ 1, 2 ]
};
const obj2 = {
foo: new TestClass()
};
it('should correctly copy objects', function() {
const copy = objtools.deepCopy(obj1);
expect(copy).to.deep.equal(obj1);
expect(null).to.not.deep.equal(undefined); // make sure chai does what we want it to
});
it('should not maintain references to objects', function() {
const copy = objtools.deepCopy(obj1);
expect(copy).to.deep.equal(obj1);
copy.biz.dat = 123;
expect(copy).to.not.deep.equal(obj1);
});
it('should copy non-primitive objects', function() {
const copy = objtools.deepCopy(obj2);
expect(copy.foo).to.equal(obj2.foo);
});
});
describe('collapseToDotted()', function() {
const obj1 = {
foo: 123,
bar: { biz: 12, baz: { buz: 1 } },
arr: [ 1, 2, { foo: 3 } ]
};
it('should correctly collapse objects to dotted form', function() {
const dotted = objtools.collapseToDotted(obj1);
expect(dotted).to.deep.equal({
'foo': 123,
'bar.biz': 12,
'bar.baz.buz': 1,
'arr.0': 1,
'arr.1': 2,
'arr.2.foo': 3
});
});
it('should obey includeRedundantLevels', function() {
const dotted = objtools.collapseToDotted(obj1, true);
expect(dotted).to.deep.equal({
'foo': 123,
'bar': obj1.bar,
'bar.biz': 12,
'bar.baz': obj1.bar.baz,
'bar.baz.buz': 1,
'arr': obj1.arr,
'arr.0': 1,
'arr.1': 2,
'arr.2.foo': 3,
'arr.2': obj1.arr[2]
});
// check for referential equality
expect(dotted.bar).to.equal(obj1.bar);
expect(dotted.bar.baz).to.equal(obj1.bar.baz);
});
it('should obey stopAtArrays', function() {
const dotted = objtools.collapseToDotted(obj1, false, true);
expect(dotted).to.deep.equal({
'foo': 123,
'bar.biz': 12,
'bar.baz.buz': 1,
'arr': obj1.arr
});
expect(dotted.arr).to.equal(obj1.arr);
});
});
describe('matchObject()', function() {
// it's not like an ObjectMask
const obj = { foo: 'foo', bar: { biz: 12 }, zip: [ 4, 5 ] };
it('returns true if it matches', function() {
expect(objtools.matchObject(obj, { foo: 'foo' })).to.be.true;
expect(objtools.matchObject(obj, { bar: { biz: 12 } })).to.be.true;
expect(objtools.matchObject(obj, { zip: [ 4 ] })).to.be.true;
});
it('returns false if it doesnt match', function() {
expect(objtools.matchObject(obj, { foo: true })).to.be.false;
expect(objtools.matchObject(obj, { bar: { _: 12 } })).to.be.false;
expect(objtools.matchObject(obj, { zip: [ 4, 5, 6 ] })).to.be.false;
});
});
describe('matchDottedObject()', function() {
const obj = { foo: 'foo', bar: { biz: 12 }, zip: [ 4, 5 ] };
const dotted1 = { 'foo': 'foo', 'bar': { biz: 12 }, 'zip': [ 4, 5 ] };
const dotted2 = { 'foo': 'foo', 'bar': { biz: 12 }, 'zip': [ 4, 2 ] };
it('returns true if it matches', function() {
expect(objtools.matchDottedObject(obj, dotted1)).to.be.true;
});
it('returns false if it doesnt match', function() {
expect(objtools.matchDottedObject(obj, dotted2)).to.be.false;
});
});
describe('syncObject()', function() {
const fromObj = {
foo: 'bar',
baz: { qux: [
{ zip: 'zap', bam: new Date('2014-01-01T00:00:00Z') },
{ bip: 'boop' }
] },
foop: { flap: 'flip' },
qax: new Date('2014-01-02T00:00:00Z')
};
it('should copy an object to the destination', function() {
let toObj = {
foo: 'bip',
zap: 'zip',
qux: { boom: 123 },
foop: { flap: 'flop' },
qax: new Date('2014-01-01T00:00:00Z')
};
const origFoop = toObj.foop;
const expected = objtools.deepCopy(fromObj);
objtools.syncObject(toObj, fromObj);
expect(toObj).to.deep.equal(fromObj);
// make sure it didn't modify fromObj
expect(fromObj).to.deep.equal(expected);
// make sure it didn't change the internal object reference
expect(toObj.foop).to.equal(origFoop);
});
it('should skip fields when the onField hook returns false', function() {
let toObj = {
foo: 'bip',
zap: 'zip',
baz: { qux: 123 },
qux: { boom: 123 },
foop: { flap: 'flop' }
};
objtools.syncObject(toObj, fromObj, { onField: (field) => field !== 'baz.qux' });
expect(toObj).to.deep.equal({
foo: 'bar',
baz: { qux: 123 },
foop: { flap: 'flip' },
qax: new Date('2014-01-02T00:00:00Z')
});
});
it('should call onChange for changed fields', function() {
let toObj = {
foo: 'bip',
zoop: 'zip',
baz: { qux: 123 },
qux: { boom: 123 },
foop: { flap: 'flop' },
zap: 4
};
let changed = [];
objtools.syncObject(toObj, fromObj, { onChange: (field) => changed.push(field) });
const expected = [ 'foo', 'zoop', 'baz.qux', 'qax', 'qux', 'foop.flap', 'zap' ];
expect(toObj).to.deep.equal(fromObj);
expect(changed.sort()).to.deep.equal(expected.sort());
});
it('should work with arrays and nested arrays', function() {
let fromArrObj = {
a: [ [ 1, 2, 3 ], [ 44 ] ],
b: [ 1, 2, 3, 4, 5 ],
c: [ 10 ],
d: [ { stuff: 'good', bar: 'foo' } ]
};
let toArrObj = {
a: [ [ 11, 2, 3, 4 ], [ 44 ], [ 55 ] ],
b: [ 1, 2, 3 ],
c: [ 10, 11 ],
d: [ { stuff: 'just ok', foo: 'bar' } ]
};
objtools.syncObject(toArrObj, fromArrObj);
expect(toArrObj).to.deep.equal({
a: [ [ 1, 2, 3 ], [ 44 ] ],
b: [ 1, 2, 3, 4, 5 ],
c: [ 10 ],
d: [ { stuff: 'good', bar: 'foo' } ]
});
});
});
describe('path functions', function() {
let obj1 = {
foo: 'bar',
baz: {
biz: 'buz',
arr: [ 1, 2, { zip: 3 } ],
arr2: [ { zip: 4 } ]
}
};
it('getPath should fetch basic object paths', function() {
expect(objtools.getPath(obj1, 'foo')).equals(obj1.foo);
expect(objtools.getPath(obj1, 'baz')).equals(obj1.baz);
expect(objtools.getPath(obj1, 'baz.biz')).equals(obj1.baz.biz);
expect(objtools.getPath(obj1, 'baz.arr2.zip')).equals(undefined);
expect(objtools.getPath(obj1, 'baz.arr.1')).equals(2);
expect(objtools.getPath(obj1, 'baz.arr.2.zip')).equals(3);
});
it('getPath should obey allowSkipArrays', function() {
expect(objtools.getPath(obj1, 'foo', true)).equals(obj1.foo);
expect(objtools.getPath(obj1, 'baz', true)).equals(obj1.baz);
expect(objtools.getPath(obj1, 'baz.biz', true)).equals(obj1.baz.biz);
expect(objtools.getPath(obj1, 'baz.arr2.zip', true)).equals(4);
expect(objtools.getPath(obj1, 'baz.arr.1', true)).equals(2);
expect(objtools.getPath(obj1, 'baz.arr.2.zip', true)).equals(3);
});
it('getPath should handle root path', function() {
expect(objtools.getPath(obj1, null)).to.deep.equal(obj1);
});
it('setPath should set various paths', function() {
objtools.setPath(obj1, 'foo', 'biz');
expect(obj1.foo).to.equal('biz');
objtools.setPath(obj1, 'baz.arr.1', 8);
expect(obj1.baz.arr[1]).to.equal(8);
});
it('setPath should create parent objects as necessary', function() {
objtools.setPath(obj1, 'bar.biz.baz.buz', 10);
// @ts-ignore
expect(obj1.bar).to.deep.equal({ biz: { baz: { buz: 10 } } });
});
it('setPath should overwrite parent object on conflicting type', function() {
objtools.setPath(obj1, 'baz.arr.1.buz', 11);
expect(obj1.baz.arr[1]).to.deep.equal({ buz: 11 });
});
it('deletePath should delete paths', function() {
objtools.deletePath(obj1, 'baz.arr');
expect(obj1.baz.arr).to.equal(undefined);
});
});
describe('merge()', function() {
const falsey = [ '', 0, false, NaN, null, undefined ];
it('should pass thru falsey `object` values', function() {
const actual = _.map(falsey, value => objtools.merge(value));
expect(actual).to.deep.equal(falsey);
});
it('should not error when `object` is nullish and source objects are provided', function() {
expect(objtools.merge(null, { a: 1 })).to.deep.equal({ a: 1 });
expect(objtools.merge(undefined, { a: 1 })).to.deep.equal({ a: 1 });
});
it('should work as an iteratee for methods like `_.reduce`', function() {
let array = [ { 'a': 1 }, { 'b': 2 }, { 'c': 3 } ];
let expected = { 'a': 1, 'b': 2, 'c': 3 };
let actual = _.reduce(array, objtools.merge, { 'a': 0 });
expect(actual).to.deep.equal(expected);
});
it('should provide the correct `customizer` arguments', function() {
let object: any = { 'a': 1 };
let source: any = { 'a': 2 };
let args, expected = [ 1, 2, 'a', _.cloneDeep(object), _.cloneDeep(source) ];
objtools.merge(_.cloneDeep(object), _.cloneDeep(source), function() {
args = _.toArray(_.cloneDeep(arguments));
});
expect(args).to.deep.equal(expected, 'primitive property values');
args = null;
object = { 'a': 1 };
source = { 'b': 2 };
expected = [ undefined, 2, 'b', object, source ];
objtools.merge(_.cloneDeep(object), _.cloneDeep(source), function() {
args = _.toArray(_.cloneDeep(arguments));
});
expect(args).to.deep.equal(expected, 'missing destination property');
args = [];
let objectValue = [ 1, 2 ];
let sourceValue = { 'b': 2 };
object = { 'a': objectValue };
source = { 'a': sourceValue };
expected = [
[ objectValue, sourceValue, 'a', object, source ],
// note: this differs from the lodash test bc that test is wrong
[ undefined, 2, 'b', objectValue, sourceValue ]
];
objtools.merge(_.cloneDeep(object), _.cloneDeep(source), function() {
args.push(_.toArray(_.cloneDeep(arguments)));
});
expect(args).to.deep.equal(expected, 'non-primitive property values');
});
it('should not assign the `customizer` result if it is the same as the destination value', function() {
_.each([ 'a', [ 'a' ], { 'a': 1 }, NaN ], function(value) {
let object = {};
let pass = true;
Object.defineProperty(object, 'a', {
'get': _.constant(value),
'set': function() { pass = false; }
});
objtools.merge(object, { 'a': value }, _.identity);
expect(pass).to.be.true;
});
});
it('should merge `source` into the destination object', function() {
const names = { 'characters': [ { 'name': 'barney' }, { 'name': 'fred' } ] };
const ages = { 'characters': [ { 'age': 36 }, { 'age': 40 } ] };
const heights = { 'characters': [ { 'height': '5\'4"' }, { 'height': '5\'5"' } ] };
const expected = { 'characters': [
{ 'name': 'barney', 'age': 36, 'height': '5\'4"' },
{ 'name': 'fred', 'age': 40, 'height': '5\'5"' }
] };
expect(objtools.merge(names, ages, heights)).to.deep.equal(expected);
});
it('should work with four arguments', function() {
const expected = { 'a': 4 };
const actual = objtools.merge({ 'a': 1 }, { 'a': 2 }, { 'a': 3 }, expected);
expect(actual).to.deep.equal(expected);
});
it('should assign `null` values', function() {
const actual = objtools.merge({ 'a': 1 }, { 'a': null });
expect(actual.a).to.equal(null);
});
it('should not assign `undefined` values', function() {
const actual = objtools.merge({ 'a': 1 }, { 'a': undefined, 'b': undefined });
expect(actual).to.deep.equal({ 'a': 1 });
});
it('should work with a function `object` value', function() {
function Foo() {}
const source = { 'a': 1 };
expect(objtools.merge(Foo, source)=== Foo);
// @ts-ignore
expect(Foo.a === 1);
});
it('should override primitive `object` values', function() {
const values = [ true, 1, '1' ];
const actual = _.map(values, value => objtools.merge(value, { 'a': 1 }));
expect(actual).to.deep.equal([ { a: 1 }, { a: 1 }, { a: 1 } ]);
});
it('should handle merging if `customizer` returns `undefined`', function() {
const actual = objtools.merge({ 'a': { 'b': [ 1, 1 ] } }, { 'a': { 'b': [ 0 ] } }, _.noop);
expect(actual).to.deep.equal({ 'a': { 'b': [ 0, 1 ] } });
expect(objtools.merge([], [ undefined ], _.identity)).to.deep.equal([ undefined ]);
});
it('should defer to `customizer` when it returns a value other than `undefined`', function() {
const customizer = (a, b) => (_.isArray(a) ? a.concat(b) : undefined);
const actual = objtools.merge({ 'a': { 'b': [ 0, 1 ] } }, { 'a': { 'b': [ 2 ] } }, customizer);
expect(actual).to.deep.equal({ 'a': { 'b': [ 0, 1, 2 ] } });
});
it('handles deep heterogeneous types', function() {
let obj = { a: {
b: [ 'c' ],
d: 'e',
f: { g: 'h' },
i: 'jk',
l: [ 'o', 'l' ]
} };
const source = { a: {
b: 'c',
d: [ 'e' ],
f: 'gh',
i: { j: 'k' },
l: { o: 'l' }
} };
const expected = { a: {
b: 'c',
d: [ 'e' ],
f: 'gh',
i: { j: 'k' },
l: _.extend([ 'o', 'l' ], { o: 'l' })
} };
expect(objtools.merge(obj, source)).to.deep.equal(expected);
});
it('copies constructors', function() {
let obj = {
foo: String
};
let result = objtools.merge({}, obj);
expect(result.foo).to.equal(String);
});
it('copies non-plain objects', function() {
function TestClass() {}
let obj = {
foo: new TestClass()
};
let result = objtools.merge({}, obj);
expect(result.foo).to.equal(obj.foo);
});
});
describe('getDuplicates()', function() {
const arr = [ 'a', 'b', 'a', 'c', 'c' ];
it('gets the duplicates in an array of strings', function() {
const result = objtools.getDuplicates(arr);
const expected = [ 'a', 'c' ];
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
});
describe('diffObjects()', function() {
const a = {
a: 'b', // value the same in all objects
c: 'd', // value exists in all objects with different values
e: 'f', // value only exists in some objects
g: 'h', // value is a scalar in some objects and non-scalar in others
i: { j: 'k' }, // value is a collection with non-overlapping fields across objects
l: { m: 'n', o: { p: 'q' } } // value is a collection with some overlapping fields across objects
};
const b = {
a: 'b',
c: 1,
e: 'f',
g: { h: true },
i: { k: 'j' },
l: { m: 'nop' }
};
const c = {
a: 'b',
c: false,
i: { jk: true },
l: { m: 'no', p: 'q' }
};
const aScalar = 'scalar';
it('diffs two objects', function() {
const result = objtools.diffObjects(a, b);
const expected = {
c: [ 'd', 1 ],
g: [ 'h', { h: true } ],
i: [ { j: 'k' }, { k: 'j' } ],
l: {
m: [ 'n', 'nop' ],
o: [ { p: 'q' }, null ]
}
};
expect(result).to.deep.equal(expected);
});
it('diffs n objects', function() {
const result = objtools.diffObjects(a, b, c);
const expected = {
c: [ 'd', 1, false ],
e: [ 'f', 'f', null ],
g: [ 'h', { h: true }, null ],
i: [ { j: 'k' }, { k: 'j' }, { jk: true } ],
l: {
m: [ 'n', 'nop', 'no' ],
o: [ { p: 'q' }, null, null ],
p: [ null, null, 'q' ]
}
};
expect(result).to.deep.equal(expected);
});
it('handles scalars', function() {
const result = objtools.diffObjects(a, b, aScalar);
const expected = _.extend([ null, null, aScalar ], {
a: [ 'b', 'b', null ],
c: [ 'd', 1, null ],
e: [ 'f', 'f', null ],
g: [ 'h', { h: true }, null ],
i: [ { j: 'k' }, { k: 'j' }, null ],
l: {
m: [ 'n', 'nop', null ],
o: [ { p: 'q' }, null, null ]
}
});
expect(result).to.deep.equal(expected);
});
});
describe('dottedDiff()', function() {
const obj1 = {
a: { b: 'c', d: { e: 'f' } },
d: 'efg'
};
const obj2 = {
a: { b: 'c', d: true },
d: new Date('2015-01-01'),
f: 'g'
};
const aScalar = 'scalar';
const anotherScalar = new Date('2015-01-01');
const arr1 = [ obj1.a, obj2.a, aScalar ];
const arr2 = [ obj1.d, obj2.d, aScalar ];
it('diffs two objects', function() {
const result = objtools.dottedDiff(obj1, obj2);
const expected = [ 'a.d', 'd', 'f' ];
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
it('diffs two arrays', function() {
const result = objtools.dottedDiff(arr1, arr2);
const expected = [ '0', '1' ];
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
it('diffs an object and an array', function() {
const result = objtools.dottedDiff(obj1, arr1);
const expected = _.union(_.keys(obj1), _.keys(arr1));
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
it('diffs an object and a scalar', function() {
const result = objtools.dottedDiff(obj1, aScalar);
const expected = [ '' ];
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
it('diffs a scalar and an object', function() {
const result = objtools.dottedDiff(aScalar, obj1);
const expected = [ '' ];
expect(result).to.contain.members(expected);
expect(result.length).to.equal(expected.length);
});
it('handles dates', function() {
const diffDates = objtools.dottedDiff({ foo: new Date() }, { foo: new Date(0) });
const sameDates = objtools.dottedDiff({ foo: new Date(0) }, { foo: new Date(0) });
expect(diffDates).to.deep.equal([ 'foo' ]);
expect(sameDates).to.deep.equal([]);
});
it('diffs two scalars', function() {
expect(objtools.dottedDiff(aScalar, anotherScalar)).to.deep.equal(['']);
});
it('handles deep equal values', function() {
expect(objtools.dottedDiff(obj1, _.cloneDeep(obj1))).to.deep.equal([]);
expect(objtools.dottedDiff(aScalar, aScalar)).to.deep.equal([]);
});
});
describe('objectHash()', function() {
const obj1a = {
a: 1,
b: '2',
c: [ 'xyz', null, null ],
d: {
asdf: 'jkl;'
}
};
const obj1b = {
b: '2',
c: [ 'xyz', null, null ],
d: {
asdf: 'jkl;'
},
a: 1
};
const obj2a = {
b: '2',
c: [ null, 'xyz', null ],
d: {
asdf: 'jkl;'
},
a: 1
};
const prim1 = 12345;
const prim2 = false;
it('returns a hash', function() {
let hash = objtools.objectHash(obj1a);
expect(hash).to.be.a('string');
});
it('hashes primitives', function() {
let hash1 = objtools.objectHash(prim1);
expect(hash1).to.be.a('string');
let hash2 = objtools.objectHash(prim2);
expect(hash2).to.be.a('string');
});
it('hashes are consistent', function() {
let hash1 = objtools.objectHash(obj1a);
let hash2 = objtools.objectHash(obj1b);
expect(hash1).to.equal(hash2);
});
it('hashes for different objects do not conflict', function() {
let hash1 = objtools.objectHash(obj1a);
let hash2 = objtools.objectHash(obj1b);
let hash3 = objtools.objectHash(obj2a);
expect(hash1).to.not.equal(hash3);
expect(hash2).to.not.equal(hash3);
});
});
describe('sanitizeDate()', function() {
it('should convert a number of miliseconds to a Date instance', () => {
let date = Date.now();
let sanitized = objtools.sanitizeDate(date);
expect(sanitized).to.be.an.instanceof(Date);
expect(sanitized.getTime()).to.equal(date);
});
it('should concert a date string to a Date instance', () => {
let date = new Date();
let sanitized = objtools.sanitizeDate(date.toISOString());
expect(sanitized).to.be.an.instanceof(Date);
expect(sanitized.getTime()).to.equal(date.getTime());
});
it('should return the same object if a date instance is passed in', () => {
let date = new Date();
let sanitized = objtools.sanitizeDate(date);
expect(sanitized).to.be.an.instanceof(Date);
expect(sanitized).to.deep.equal(date);
});
it('should flatten object with a field `date`', () => {
let date = { date: new Date() };
let sanitized = objtools.sanitizeDate(date);
expect(sanitized).to.be.an.instanceof(Date);
expect(sanitized).to.deep.equal(date.date);
});
});
describe('isPlainObject()', function() {
function TestConstructor() {}
const values = {
emptyObject: {},
'null': null,
plainObject: { foo: 'bar' },
'function': function() {},
nativeFunction: String,
date: new Date(),
'undefined': undefined,
'false': false,
string: 'foo',
'true': true,
classObject: new TestConstructor(),
objectCreate: Object.create(null),
array: [],
jsonDecoded: JSON.parse('{"foo":"bar"}')
};
_.forEach(values, function(value, key) {
it('should handle ' + key, function() {
expect(objtools.isPlainObject(value)).to.equal(_.isPlainObject(value));
});
});
});
describe('isEmpty()', function() {
const values = {
emptyObject: {},
'null': null,
plainObject: { foo: 'bar' },
'function': function() {},
date: new Date(),
'undefined': undefined,
string: 'foo',
emptyString: '',
array: [ 'foo' ],
emptyArray: [],
jsonDecoded: JSON.parse('{"foo":"bar"}')
};
_.forEach(values, function(value, key) {
it('should handle ' + key, function() {
expect(objtools.isEmpty(value)).to.equal(_.isEmpty(value));
});
});
});
});