diff --git a/package-lock.json b/package-lock.json index 2cf63cf8d..1947f04ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5635,7 +5635,8 @@ }, "node_modules/lodash": { "version": "4.17.21", - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", diff --git a/src/__tests__/unit/util/helpers.test.ts b/src/__tests__/unit/util/helpers.test.ts index 9feffc5f3..5bda8b752 100644 --- a/src/__tests__/unit/util/helpers.test.ts +++ b/src/__tests__/unit/util/helpers.test.ts @@ -7,8 +7,7 @@ jest.mock('../../../util/logger', () => ({ error: mockError, }, })); - -import {andHandle} from '../../../util/helpers'; +import {andHandle, mergeObjects} from '../../../util/helpers'; import {ERRORS} from '../../../util/errors'; const {WriteFileError} = ERRORS; @@ -38,3 +37,142 @@ describe('util/helpers: ', () => { }); }); }); + +describe('util/helpers: ', () => { + describe('mergeObjects(): ', () => { + it('does not override input.', () => { + expect.assertions(1); + + const input = { + a: 1, + b: false, + c: 'testInput', + }; + + const defaults = { + c: 'testDefault', + }; + const result = mergeObjects(defaults, input); + + expect(result).toEqual(input); + }); + + it('overrides null/undefined inputs.', () => { + expect.assertions(1); + + const input = { + a: 1, + b: false, + c: 'testInput', + d: null, + e: undefined, + }; + + const defaults = { + c: 'testDefault', + d: 'testDefault', + e: 'testDefault', + }; + const result = mergeObjects(defaults, input); + const expectedResult = { + a: 1, + b: false, + c: 'testInput', + d: 'testDefault', + e: 'testDefault', + }; + + expect(result).toEqual(expectedResult); + }); + + it('adds only properties missing in input.', () => { + expect.assertions(1); + + const input = { + a: 1, + b: false, + c: 'testInput', + }; + + const defaults = { + b: true, + c: 'testDefault', + d: 25, + }; + + const result = mergeObjects(defaults, input); + const expectedResult = { + a: 1, + b: false, + c: 'testInput', + d: 25, + }; + + expect(result).toEqual(expectedResult); + }); + + it('keeps values from input in case of nested objects.', () => { + expect.assertions(1); + + const input = { + a: 1, + b: false, + c: 'testInput', + d: { + e: 1, + }, + }; + + const defaults = { + b: true, + c: 'testDefault1', + d: { + e: 25, + f: 'testDefault2', + }, + }; + + const result = mergeObjects(defaults, input); + const expectedResult = { + a: 1, + b: false, + c: 'testInput', + d: { + e: 1, + }, + }; + + expect(result).toEqual(expectedResult); + }); + + it('keeps value from input in case of arrays.', () => { + expect.assertions(1); + + const input = { + a: 1, + b: false, + c: 'testInput1', + d: [1, 2, 3, 4], + e: 'testInput2', + }; + + const defaults = { + b: true, + c: 'testDefault1', + d: [5, 6, 7, 8, 9], + e: [1, 2, 3], + }; + + const result = mergeObjects(defaults, input); + const expectedResult = { + a: 1, + b: false, + c: 'testInput1', + d: [1, 2, 3, 4], + e: 'testInput2', + }; + + expect(result).toEqual(expectedResult); + }); + }); +}); diff --git a/src/lib/compute.ts b/src/lib/compute.ts index 343bfcc97..b86ff02dc 100644 --- a/src/lib/compute.ts +++ b/src/lib/compute.ts @@ -22,7 +22,7 @@ const mergeDefaults = ( ) => { if (inputs) { const response = defaults - ? inputs.map(input => mergeObjects(input, defaults)) + ? inputs.map(input => mergeObjects(defaults, input)) : inputs; return response; diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 3ef81c037..88050c268 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -1,6 +1,5 @@ -import {ERRORS} from './errors'; - import {STRINGS} from '../config'; +import {ERRORS} from './errors'; import {logger} from './logger'; const {ISSUE_TEMPLATE} = STRINGS; @@ -19,22 +18,20 @@ export const andHandle = (error: Error) => { }; /** - * Mergers two objects, omitting null values. + * Append entries from defaults which are missing from inputs. */ -export const mergeObjects = (object1: any, object2: any) => { - const merged: Record = {}; - - const keys1 = Object.keys(object1); - keys1.forEach(key1 => { - merged[key1] = object1[key1] || object2[key1]; - }); - - const keys2 = Object.keys(object2); - keys2.forEach(key2 => { - if (!keys1.includes(key2)) { - merged[key2] = object2[key2]; +export const mergeObjects = (defaults: any, input: any) => { + const merged: Record = structuredClone(input); + + for (const key in defaults) { + if (!(key in input)) { + merged[key] = defaults[key]; } - }); + + if (merged[key] === undefined || merged[key] === null) { + merged[key] = defaults[key]; + } + } return merged; };