UNPKG

@qntm-code/utils

Version:

A collection of useful utility functions with associated TypeScript types. All functions have been unit tested.

269 lines (268 loc) 10.3 kB
import { merge } from './merge'; import { mergeTests } from './mergeTestDefinitions.spec'; describe(`merge`, () => { for (const { description, target, source, expected, notEqual } of mergeTests) { it(description, () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const merged = merge(target, source); if (expected) { expect(merged).toEqual(expected); } if (notEqual) { expect(merged[notEqual.keyA]).not.toBe(target[notEqual.keyA]); expect(merged[notEqual.keyB]).not.toBe(source[notEqual.keyB]); } }); } it(`should clone arrays element if it is object`, () => { const a = { key: 'yup' }; const target = []; const source = [a]; const output = merge(target, source); expect(output[0]).not.toBe(a); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access expect(output[0].key).toEqual('yup'); }); it('should clone an array property when there is no target array', () => { const someObject = {}; const target = {}; const source = { ary: [someObject] }; const output = merge(target, source); expect(output).toEqual({ ary: [{}] }); expect(output.ary[0]).not.toBe(someObject); }); it('should overwrite values when property is initialised but undefined', () => { const target1 = { value: [] }; const target2 = { value: null }; const target3 = { value: 2 }; const src = { value: undefined }; function hasUndefinedProperty(o) { expect(typeof o.value).toEqual('undefined'); } hasUndefinedProperty(merge(target1, src)); hasUndefinedProperty(merge(target2, src)); hasUndefinedProperty(merge(target3, src)); }); it('dates should copy correctly in an array', () => { const monday = new Date('2016-09-27T01:08:12.761Z'); const tuesday = new Date('2016-09-28T01:18:12.761Z'); const target = [monday, 'dude']; const source = [tuesday, 'lol']; const expected = [monday, 'dude', tuesday, 'lol']; const actual = merge(target, source); expect(actual).toEqual(expected); }); it('should handle custom merge functions', () => { const target = { letters: ['a', 'b'], people: { first: 'Alex', second: 'Bert', }, }; const source = { letters: ['c'], people: { first: 'Smith', second: 'Bertson', third: 'Car', }, }; const mergePeople = (target, source) => { const keys = new Set(Object.keys(target).concat(Object.keys(source))); const destination = {}; keys.forEach(key => { if (key in target && key in source) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions destination[key] = `${target[key]}-${source[key]}`; } else if (key in target) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment destination[key] = target[key]; } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment destination[key] = source[key]; } }); return destination; }; const options = { customMerge: key => { if (key === 'people') { return mergePeople; } return merge; }, }; const expected = { letters: ['a', 'b', 'c'], people: { first: 'Alex-Smith', second: 'Bert-Bertson', third: 'Car', }, }; const actual = merge(target, source, options); expect(actual).toEqual(expected); }); it('should handle custom merge functions', () => { const target = { letters: ['a', 'b'], people: { first: 'Alex', second: 'Bert', }, }; const source = { letters: ['c'], people: { first: 'Smith', second: 'Bertson', third: 'Car', }, }; const mergeLetters = () => { return 'merged letters'; }; const options = { customMerge: key => { if (key === 'letters') { return mergeLetters; } }, }; const expected = { letters: 'merged letters', people: { first: 'Smith', second: 'Bertson', third: 'Car', }, }; const actual = merge(target, source, options); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument expect(actual).toEqual(expected); }); it('should merge correctly if custom merge is not a valid function', () => { const target = { letters: ['a', 'b'], people: { first: 'Alex', second: 'Bert', }, }; const source = { letters: ['c'], people: { first: 'Smith', second: 'Bertson', third: 'Car', }, }; const expected = { letters: ['a', 'b', 'c'], people: { first: 'Smith', second: 'Bertson', third: 'Car', }, }; const actual = merge(target, source); expect(actual).toEqual(expected); }); it('copy symbol keys in target that do not exist on the target', () => { const mySymbol = Symbol(); const src = { [mySymbol]: 'value1' }; const target = {}; const res = merge(target, src); expect(res[mySymbol]).toEqual('value1'); expect(Object.getOwnPropertySymbols(res)).toEqual(Object.getOwnPropertySymbols(src)); }); it('copy symbol keys in target that do exist on the target', () => { const mySymbol = Symbol(); const src = { [mySymbol]: 'value1' }; const target = { [mySymbol]: 'wat' }; const res = merge(target, src); expect(res[mySymbol]).toEqual('value1'); }); it('Falsey properties should be mergeable', () => { const uniqueValue = {}; const target = { wat: false, }; const source = { wat: false, }; let customMergeWasCalled = false; const result = merge(target, source, { isMergeableObject: function () { return true; }, customMerge: function () { return function () { customMergeWasCalled = true; return uniqueValue; }; }, }); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument expect(result.wat).toEqual(uniqueValue); expect(customMergeWasCalled).toBe(true); }); describe(`all`, () => { it('throw error if first argument is not an array', () => { expect(merge.all.bind(null, { example: true }, { another: '2' })).toThrowError(); }); it('return an empty object if first argument is an array with no elements', () => { expect(merge.all([])).toEqual({}); }); it('Work just fine if first argument is an array with least than two elements', () => { const actual = merge.all([{ example: true }]); const expected = { example: true }; expect(actual).toEqual(expected); }); it('execute correctly if options object were not passed', () => { const arrayToMerge = [{ example: true }, { another: '123' }]; expect(merge.all.bind(null, arrayToMerge)).not.toThrowError(); }); it('execute correctly if options object were passed', () => { const arrayToMerge = [{ example: true }, { another: '123' }]; expect(merge.all.bind(null, arrayToMerge)).not.toThrowError(); }); it('invoke merge on every item in array should result with all props', () => { const firstObject = { first: true }; const secondObject = { second: false }; const thirdObject = { third: 123 }; const fourthObject = { fourth: 'some string' }; const mergedObject = merge.all([ firstObject, secondObject, thirdObject, fourthObject, ]); expect(mergedObject.first).toBe(true); expect(mergedObject.second).toBe(false); expect(mergedObject.third).toBe(123); expect(mergedObject.fourth).toBe('some string'); }); it('invoke merge on every item in array with clone should clone all elements', () => { const firstObject = { a: { d: 123 } }; const secondObject = { b: { e: true } }; const thirdObject = { c: { f: 'string' } }; const mergedWithClone = merge.all([firstObject, secondObject, thirdObject]); expect(mergedWithClone.a).not.toBe(firstObject.a); expect(mergedWithClone.b).not.toBe(secondObject.b); expect(mergedWithClone.c).not.toBe(thirdObject.c); }); it('invoke merge on every item in array without clone should clone all elements', () => { const firstObject = { a: { d: 123 } }; const secondObject = { b: { e: true } }; const thirdObject = { c: { f: 'string' } }; const mergedWithoutClone = merge.all([firstObject, secondObject, thirdObject]); expect(mergedWithoutClone.a).not.toBe(firstObject.a); expect(mergedWithoutClone.b).not.toBe(secondObject.b); expect(mergedWithoutClone.c).not.toBe(thirdObject.c); }); }); });