mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +00:00
I've commented out a test for `_.merge` and will re-look it as I gradually cleanup the codebase.
351 lines
10 KiB
JavaScript
351 lines
10 KiB
JavaScript
import assert from 'assert';
|
|
import lodashStable from 'lodash';
|
|
import { args, typedArrays, stubTrue, defineProperty, document, root } from './utils.js';
|
|
import merge from '../merge.js';
|
|
import isArguments from '../isArguments.js';
|
|
|
|
describe('merge', function() {
|
|
it('should merge `source` into `object`', function() {
|
|
var names = {
|
|
'characters': [
|
|
{ 'name': 'barney' },
|
|
{ 'name': 'fred' }
|
|
]
|
|
};
|
|
|
|
var ages = {
|
|
'characters': [
|
|
{ 'age': 36 },
|
|
{ 'age': 40 }
|
|
]
|
|
};
|
|
|
|
var heights = {
|
|
'characters': [
|
|
{ 'height': '5\'4"' },
|
|
{ 'height': '5\'5"' }
|
|
]
|
|
};
|
|
|
|
var expected = {
|
|
'characters': [
|
|
{ 'name': 'barney', 'age': 36, 'height': '5\'4"' },
|
|
{ 'name': 'fred', 'age': 40, 'height': '5\'5"' }
|
|
]
|
|
};
|
|
|
|
assert.deepStrictEqual(merge(names, ages, heights), expected);
|
|
});
|
|
|
|
it('should merge sources containing circular references', function() {
|
|
var object = {
|
|
'foo': { 'a': 1 },
|
|
'bar': { 'a': 2 }
|
|
};
|
|
|
|
var source = {
|
|
'foo': { 'b': { 'c': { 'd': {} } } },
|
|
'bar': {}
|
|
};
|
|
|
|
source.foo.b.c.d = source;
|
|
source.bar.b = source.foo.b;
|
|
|
|
var actual = merge(object, source);
|
|
|
|
assert.notStrictEqual(actual.bar.b, actual.foo.b);
|
|
assert.strictEqual(actual.foo.b.c.d, actual.foo.b.c.d.foo.b.c.d);
|
|
});
|
|
|
|
it('should work with four arguments', function() {
|
|
var expected = { 'a': 4 },
|
|
actual = merge({ 'a': 1 }, { 'a': 2 }, { 'a': 3 }, expected);
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should merge onto function `object` values', function() {
|
|
function Foo() {}
|
|
|
|
var source = { 'a': 1 },
|
|
actual = merge(Foo, source);
|
|
|
|
assert.strictEqual(actual, Foo);
|
|
assert.strictEqual(Foo.a, 1);
|
|
});
|
|
|
|
it('should merge first source object properties to function', function() {
|
|
var fn = function() {},
|
|
object = { 'prop': {} },
|
|
actual = merge({ 'prop': fn }, object);
|
|
|
|
assert.deepStrictEqual(actual, object);
|
|
});
|
|
|
|
it('should merge first and second source object properties to function', function() {
|
|
var fn = function() {},
|
|
object = { 'prop': {} },
|
|
actual = merge({ 'prop': fn }, { 'prop': fn }, object);
|
|
|
|
assert.deepStrictEqual(actual, object);
|
|
});
|
|
|
|
it('should not merge onto function values of sources', function() {
|
|
var source1 = { 'a': function() {} },
|
|
source2 = { 'a': { 'b': 2 } },
|
|
expected = { 'a': { 'b': 2 } },
|
|
actual = merge({}, source1, source2);
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
assert.ok(!('b' in source1.a));
|
|
|
|
actual = merge(source1, source2);
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should merge onto non-plain `object` values', function() {
|
|
function Foo() {}
|
|
|
|
var object = new Foo,
|
|
actual = merge(object, { 'a': 1 });
|
|
|
|
assert.strictEqual(actual, object);
|
|
assert.strictEqual(object.a, 1);
|
|
});
|
|
|
|
// TODO: revisit.
|
|
it.skip('should treat sparse array sources as dense', function() {
|
|
var array = [1];
|
|
array[2] = 3;
|
|
|
|
var actual = merge([], array),
|
|
expected = array.slice();
|
|
|
|
expected[1] = undefined;
|
|
|
|
assert.ok('1' in actual);
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should merge `arguments` objects', function() {
|
|
var object1 = { 'value': args },
|
|
object2 = { 'value': { '3': 4 } },
|
|
expected = { '0': 1, '1': 2, '2': 3, '3': 4 },
|
|
actual = merge(object1, object2);
|
|
|
|
assert.ok(!('3' in args));
|
|
assert.ok(!isArguments(actual.value));
|
|
assert.deepStrictEqual(actual.value, expected);
|
|
object1.value = args;
|
|
|
|
actual = merge(object2, object1);
|
|
assert.ok(!isArguments(actual.value));
|
|
assert.deepStrictEqual(actual.value, expected);
|
|
|
|
expected = { '0': 1, '1': 2, '2': 3 };
|
|
|
|
actual = merge({}, object1);
|
|
assert.ok(!isArguments(actual.value));
|
|
assert.deepStrictEqual(actual.value, expected);
|
|
});
|
|
|
|
it('should merge typed arrays', function() {
|
|
var array1 = [0],
|
|
array2 = [0, 0],
|
|
array3 = [0, 0, 0, 0],
|
|
array4 = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
var arrays = [array2, array1, array4, array3, array2, array4, array4, array3, array2],
|
|
buffer = ArrayBuffer && new ArrayBuffer(8);
|
|
|
|
var expected = lodashStable.map(typedArrays, function(type, index) {
|
|
var array = arrays[index].slice();
|
|
array[0] = 1;
|
|
return root[type] ? { 'value': array } : false;
|
|
});
|
|
|
|
var actual = lodashStable.map(typedArrays, function(type) {
|
|
var Ctor = root[type];
|
|
return Ctor ? merge({ 'value': new Ctor(buffer) }, { 'value': [1] }) : false;
|
|
});
|
|
|
|
assert.ok(lodashStable.isArray(actual));
|
|
assert.deepStrictEqual(actual, expected);
|
|
|
|
expected = lodashStable.map(typedArrays, function(type, index) {
|
|
var array = arrays[index].slice();
|
|
array.push(1);
|
|
return root[type] ? { 'value': array } : false;
|
|
});
|
|
|
|
actual = lodashStable.map(typedArrays, function(type, index) {
|
|
var Ctor = root[type],
|
|
array = lodashStable.range(arrays[index].length);
|
|
|
|
array.push(1);
|
|
return Ctor ? merge({ 'value': array }, { 'value': new Ctor(buffer) }) : false;
|
|
});
|
|
|
|
assert.ok(lodashStable.isArray(actual));
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should assign `null` values', function() {
|
|
var actual = merge({ 'a': 1 }, { 'a': null });
|
|
assert.strictEqual(actual.a, null);
|
|
});
|
|
|
|
it('should assign non array/buffer/typed-array/plain-object source values directly', function() {
|
|
function Foo() {}
|
|
|
|
var values = [new Foo, new Boolean, new Date, Foo, new Number, new String, new RegExp],
|
|
expected = lodashStable.map(values, stubTrue);
|
|
|
|
var actual = lodashStable.map(values, function(value) {
|
|
var object = merge({}, { 'a': value, 'b': { 'c': value } });
|
|
return object.a === value && object.b.c === value;
|
|
});
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should clone buffer source values', function() {
|
|
if (Buffer) {
|
|
var buffer = new Buffer([1]),
|
|
actual = merge({}, { 'value': buffer }).value;
|
|
|
|
assert.ok(lodashStable.isBuffer(actual));
|
|
assert.strictEqual(actual[0], buffer[0]);
|
|
assert.notStrictEqual(actual, buffer);
|
|
}
|
|
});
|
|
|
|
it('should deep clone array/typed-array/plain-object source values', function() {
|
|
var typedArray = Uint8Array
|
|
? new Uint8Array([1])
|
|
: { 'buffer': [1] };
|
|
|
|
var props = ['0', 'buffer', 'a'],
|
|
values = [[{ 'a': 1 }], typedArray, { 'a': [1] }],
|
|
expected = lodashStable.map(values, stubTrue);
|
|
|
|
var actual = lodashStable.map(values, function(value, index) {
|
|
var key = props[index],
|
|
object = merge({}, { 'value': value }),
|
|
subValue = value[key],
|
|
newValue = object.value,
|
|
newSubValue = newValue[key];
|
|
|
|
return (
|
|
newValue !== value &&
|
|
newSubValue !== subValue &&
|
|
lodashStable.isEqual(newValue, value)
|
|
);
|
|
});
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should not augment source objects', function() {
|
|
var source1 = { 'a': [{ 'a': 1 }] },
|
|
source2 = { 'a': [{ 'b': 2 }] },
|
|
actual = merge({}, source1, source2);
|
|
|
|
assert.deepStrictEqual(source1.a, [{ 'a': 1 }]);
|
|
assert.deepStrictEqual(source2.a, [{ 'b': 2 }]);
|
|
assert.deepStrictEqual(actual.a, [{ 'a': 1, 'b': 2 }]);
|
|
|
|
var source1 = { 'a': [[1, 2, 3]] },
|
|
source2 = { 'a': [[3, 4]] },
|
|
actual = merge({}, source1, source2);
|
|
|
|
assert.deepStrictEqual(source1.a, [[1, 2, 3]]);
|
|
assert.deepStrictEqual(source2.a, [[3, 4]]);
|
|
assert.deepStrictEqual(actual.a, [[3, 4, 3]]);
|
|
});
|
|
|
|
it('should merge plain objects onto non-plain objects', function() {
|
|
function Foo(object) {
|
|
lodashStable.assign(this, object);
|
|
}
|
|
|
|
var object = { 'a': 1 },
|
|
actual = merge(new Foo, object);
|
|
|
|
assert.ok(actual instanceof Foo);
|
|
assert.deepStrictEqual(actual, new Foo(object));
|
|
|
|
actual = merge([new Foo], [object]);
|
|
assert.ok(actual[0] instanceof Foo);
|
|
assert.deepStrictEqual(actual, [new Foo(object)]);
|
|
});
|
|
|
|
it('should not overwrite existing values with `undefined` values of object sources', function() {
|
|
var actual = merge({ 'a': 1 }, { 'a': undefined, 'b': undefined });
|
|
assert.deepStrictEqual(actual, { 'a': 1, 'b': undefined });
|
|
});
|
|
|
|
it('should not overwrite existing values with `undefined` values of array sources', function() {
|
|
var array = [1];
|
|
array[2] = 3;
|
|
|
|
var actual = merge([4, 5, 6], array),
|
|
expected = [1, 5, 3];
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
|
|
array = [1, , 3];
|
|
array[1] = undefined;
|
|
|
|
actual = merge([4, 5, 6], array);
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
|
|
it('should skip merging when `object` and `source` are the same value', function() {
|
|
var object = {},
|
|
pass = true;
|
|
|
|
defineProperty(object, 'a', {
|
|
'configurable': true,
|
|
'enumerable': true,
|
|
'get': function() { pass = false; },
|
|
'set': function() { pass = false; }
|
|
});
|
|
|
|
merge(object, object);
|
|
assert.ok(pass);
|
|
});
|
|
|
|
it('should convert values to arrays when merging arrays of `source`', function() {
|
|
var object = { 'a': { '1': 'y', 'b': 'z', 'length': 2 } },
|
|
actual = merge(object, { 'a': ['x'] });
|
|
|
|
assert.deepStrictEqual(actual, { 'a': ['x', 'y'] });
|
|
|
|
actual = merge({ 'a': {} }, { 'a': [] });
|
|
assert.deepStrictEqual(actual, { 'a': [] });
|
|
});
|
|
|
|
it('should convert strings to arrays when merging arrays of `source`', function() {
|
|
var object = { 'a': 'abcde' },
|
|
actual = merge(object, { 'a': ['x', 'y', 'z'] });
|
|
|
|
assert.deepStrictEqual(actual, { 'a': ['x', 'y', 'z'] });
|
|
});
|
|
|
|
it('should not error on DOM elements', function() {
|
|
var object1 = { 'el': document && document.createElement('div') },
|
|
object2 = { 'el': document && document.createElement('div') },
|
|
pairs = [[{}, object1], [object1, object2]],
|
|
expected = lodashStable.map(pairs, stubTrue);
|
|
|
|
var actual = lodashStable.map(pairs, function(pair) {
|
|
try {
|
|
return merge(pair[0], pair[1]).el === pair[1].el;
|
|
} catch (e) {}
|
|
});
|
|
|
|
assert.deepStrictEqual(actual, expected);
|
|
});
|
|
});
|