ts-prime
Version:
A utility library for JavaScript and Typescript.
175 lines (174 loc) • 6.13 kB
JavaScript
import * as assert from 'assert';
import { clone } from './clone';
var eq = function (a, b) {
expect(a).toEqual(b);
};
describe('deep clone integers, strings and booleans', function () {
it('clones integers', function () {
eq(clone(-4), -4);
eq(clone(9007199254740991), 9007199254740991);
});
it('clones floats', function () {
eq(clone(-4.5), -4.5);
eq(clone(0.0), 0.0);
});
it('clones strings', function () {
eq(clone('foo'), 'foo');
});
it('clones booleans', function () {
eq(clone(true), true);
});
});
describe('deep clone objects', function () {
it('clones shallow object', function () {
var obj = { a: 1, b: 'foo', c: true, d: new Date(2013, 11, 25) };
var cloned = clone(obj);
obj.c = false;
obj.d.setDate(31);
assert.notStrictEqual(cloned, { a: 1, b: 'foo', c: true, d: new Date(2013, 11, 25) });
});
it('clones deep object', function () {
var obj = { a: { b: { c: 'foo' } } };
var cloned = clone(obj);
obj.a.b.c = 'bar';
eq(cloned, { a: { b: { c: 'foo' } } });
});
it('clones objects with circular references', function () {
var x = { c: null };
var y = { a: x };
var z = { b: y };
x.c = z;
var cloned = clone(x);
assert.notStrictEqual(x, cloned);
assert.notStrictEqual(x.c, cloned.c);
assert.notStrictEqual(x.c.b, cloned.c.b);
assert.notStrictEqual(x.c.b.a, cloned.c.b.a);
assert.notStrictEqual(x.c.b.a.c, cloned.c.b.a.c);
eq(Object.keys(cloned), Object.keys(x));
eq(Object.keys(cloned.c), Object.keys(x.c));
eq(Object.keys(cloned.c.b), Object.keys(x.c.b));
eq(Object.keys(cloned.c.b.a), Object.keys(x.c.b.a));
eq(Object.keys(cloned.c.b.a.c), Object.keys(x.c.b.a.c));
x.c.b = 1;
assert.notDeepEqual(cloned.c.b, x.c.b);
});
// it('clone instances', () => {
// const Obj = function(x) {
// this.x = x;
// };
// Obj.prototype.get = () => {
// return this.x;
// };
// Obj.prototype.set = function(x) {
// this.x = x;
// };
// const obj = new Obj(10);
// eq(obj.get(), 10);
// const cloned = clone(obj);
// eq(cloned.get(), 10);
// assert.notStrictEqual(obj, cloned);
// obj.set(11);
// eq(obj.get(), 11);
// eq(cloned.get(), 10);
// });
});
describe('deep clone arrays', function () {
it('clones shallow arrays', function () {
var list = [1, 2, 3];
var cloned = clone(list);
list.pop();
eq(cloned, [1, 2, 3]);
});
it('clones deep arrays', function () {
var list = [1, [1, 2, 3], [[[5]]]];
var cloned = clone(list);
assert.notStrictEqual(list, cloned);
assert.notStrictEqual(list[2], cloned[2]);
assert.notStrictEqual(list[2][0], cloned[2][0]);
eq(cloned, [1, [1, 2, 3], [[[5]]]]);
});
});
describe('deep clone functions', function () {
it('keep reference to function', function () {
var fn = function (x) {
return x + x;
};
var list = [{ a: fn }];
var cloned = clone(list);
eq(cloned[0].a(10), 20);
eq(list[0].a, cloned[0].a);
});
});
describe('built-in types', function () {
it('clones Date object', function () {
var date = new Date(2014, 10, 14, 23, 59, 59, 999);
var cloned = clone(date);
eq(date, cloned);
eq(cloned, new Date(2014, 10, 14, 23, 59, 59, 999));
eq(cloned.getDay(), 5); // friday
});
it('clones RegExp object', function () {
[/x/, /x/g, /x/i, /x/m, /x/gi, /x/gm, /x/im, /x/gim].forEach(function (pattern) {
var cloned = clone(pattern);
assert.notStrictEqual(cloned, pattern);
eq(cloned.constructor, RegExp);
eq(cloned.source, pattern.source);
eq(cloned.global, pattern.global);
eq(cloned.ignoreCase, pattern.ignoreCase);
eq(cloned.multiline, pattern.multiline);
});
});
});
describe('deep clone deep nested mixed objects', function () {
it('clones array with objects', function () {
var list = [{ a: { b: 1 } }, [{ c: { d: 1 } }]];
var cloned = clone(list);
list[1][0] = null;
eq(cloned, [{ a: { b: 1 } }, [{ c: { d: 1 } }]]);
});
it('clones array with arrays', function () {
var list = [[1], [[3]]];
var cloned = clone(list);
list[1][0] = null;
eq(cloned, [[1], [[3]]]);
});
it('clones array with mutual ref object', function () {
var obj = { a: 1 };
var list = [{ b: obj }, { b: obj }];
var cloned = clone(list);
assert.strictEqual(list[0].b, list[1].b);
assert.strictEqual(cloned[0].b, cloned[1].b);
assert.notStrictEqual(cloned[0].b, list[0].b);
assert.notStrictEqual(cloned[1].b, list[1].b);
eq(cloned[0].b, { a: 1 });
eq(cloned[1].b, { a: 1 });
obj.a = 2;
eq(cloned[0].b, { a: 1 });
eq(cloned[1].b, { a: 1 });
});
});
describe('deep clone edge cases', function () {
it('nulls, undefineds and empty objects and arrays', function () {
eq(clone(null), null);
eq(clone(undefined), undefined);
assert.notStrictEqual(clone(undefined), null);
var obj = {};
assert.notStrictEqual(clone(obj), obj);
var list = [];
assert.notStrictEqual(clone(list), list);
});
});
// describe('Let `clone` use an arbitrary user defined `clone` method', () => {
// it('dispatches to `clone` method if present', () => {
// function ArbitraryClone(x: any) {
// this.value = x;
// }
// ArbitraryClone.prototype.clone = () => {
// return new ArbitraryClone(this.value);
// };
// const obj = new ArbitraryClone(42);
// const arbitraryClonedObj = clone(obj);
// eq(arbitraryClonedObj, new ArbitraryClone(42));
// eq(arbitraryClonedObj instanceof ArbitraryClone, true);
// });
// });