diff options
author | Paul Slaughter <pslaughter@gitlab.com> | 2018-09-13 16:00:56 -0500 |
---|---|---|
committer | Paul Slaughter <pslaughter@gitlab.com> | 2018-09-13 17:46:45 -0500 |
commit | 578efd5fe7320e9a160236d40d1cdd4ca0aec0da (patch) | |
tree | 7cf9d511f78cc85a6c792f6a2ea09cf187868f9c | |
parent | 32b96bfd81ff254142dbd9c73e1a494308213cb3 (diff) | |
download | gitlab-ce-stash-deep-merge-util.tar.gz |
Create deep_merge javascript utilstash-deep-merge-util
- This will help simplify `replaceState()` calls in tests
- We could've used a third-party package for this, but this
is a pretty small utility and might not be worth the headache
of a whole other dependency.
Update deep_merge
-rw-r--r-- | app/assets/javascripts/lib/utils/deep_merge.js | 51 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/deep_merge_spec.js | 78 |
2 files changed, 129 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/deep_merge.js b/app/assets/javascripts/lib/utils/deep_merge.js new file mode 100644 index 00000000000..82595ddd6b4 --- /dev/null +++ b/app/assets/javascripts/lib/utils/deep_merge.js @@ -0,0 +1,51 @@ +import _ from 'underscore'; + +function isMergeable(obj) { + return obj && _.isObject(obj); +} + +function deepMergeInto(dest, source) { + return Object.keys(source) + .reduce((acc, key) => Object.assign(acc, { + [key]: isMergeable(acc[key]) && isMergeable(source[key]) + ? deepMergeInto({ ...acc[key] }, source[key]) + : source[key], + }), dest); +} + +/** + * Return a deeply merge of the given objects + * + * Rules: + * - Objects are merged from left to right. + * - Values are merged only if they are mergeable, + * otherwise the left value is replaced with the right value. + * - Immutable. + * + * Example: + ```javascript + const a = { + foo: { val: 1 }, + bar: 'abc', + zoo: {}, + }; + const b = { + foo: { reason: 'math' }, + bar: 'abc/abc', + zoo: null, + }; + const result = deepMerge(a, b); + + // result is + // { + // foo: { val: 1, reason: 'math' }, + // bar: 'abc/abc', + // zoo: null, + // } + ``` + * + * @param {...any} args + */ +export default function deepMerge(...objs) { + return objs.reduce(deepMergeInto, {}); +} diff --git a/spec/javascripts/lib/utils/deep_merge_spec.js b/spec/javascripts/lib/utils/deep_merge_spec.js new file mode 100644 index 00000000000..f1ccad20082 --- /dev/null +++ b/spec/javascripts/lib/utils/deep_merge_spec.js @@ -0,0 +1,78 @@ +import deepMerge from '~/lib/utils/deep_merge'; + +/** + * This array of objects is used to test the following cases: + * - [x] merging more than 2 objects + * - [x] merging shallow properties + * - [x] merging deep properties + * - [x] overwriting when the source property is not mergeable + * - [x] overwriting when the target property is not mergeable + */ +const getTestArgs = () => [ + { + foo: { + author: 'Franz', + chapter: { + page: 3, + }, + }, + bar: null, + }, + { + foo: { + author: 'Franz Kafka', + title: 'The Trial', + }, + bar: { + zoo: 'ny', + animal: 'monkey', + }, + }, + { + foo: { + chapter: { + page: 3, + title: 'The First Chapter', + }, + }, + bar: { + zoo: 'la', + }, + car: 'fast', + }, +]; + +const TEST_RESULT = { + foo: { + author: 'Franz Kafka', + chapter: { + page: 3, + title: 'The First Chapter', + }, + title: 'The Trial', + }, + bar: { + zoo: 'la', + animal: 'monkey', + }, + car: 'fast', +}; + +describe('deepMerge', () => { + it('merges objects deeply', () => { + const args = getTestArgs(); + + const result = deepMerge(...args); + + expect(result).toEqual(TEST_RESULT); + }); + + it('does not mutate objects', () => { + const args = getTestArgs(); + const origJSON = JSON.stringify(args); + + deepMerge(...args); + + expect(JSON.stringify(args)).toEqual(origJSON); + }); +}); |