diff --git a/__tests__/src/reducers/utils.test.js b/__tests__/src/reducers/utils.test.js new file mode 100644 index 0000000000000000000000000000000000000000..1ee47ebfb331115674a7b43d6f546d309e884b5b --- /dev/null +++ b/__tests__/src/reducers/utils.test.js @@ -0,0 +1,71 @@ +import { set, update, unset } from '../../../src/state/reducers/utils'; + +describe('set', () => { + it('does not change input object', () => { + const object = { foo: { a: 1 } }; + set(object, 'foo', { b: 2 }); + expect(object).toEqual({ foo: { a: 1 } }); + }); + + it('sets a new item with the provided props', () => { + const object = { foo: { a: 1 } }; + const result = set(object, 'bar', { b: 2 }); + expect(result).toEqual({ + bar: { b: 2 }, + foo: { a: 1 }, + }); + }); + + it('overrides an existing item', () => { + const object = { foo: { a: 1 } }; + const result = set(object, 'foo', { b: 2 }); + expect(result).toEqual({ foo: { b: 2 } }); + }); +}); + +describe('updateItem', () => { + it('does not change input object', () => { + const object = { foo: { a: 1 } }; + update(object, 'foo', { b: 2 }); + expect(object).toEqual({ foo: { a: 1 } }); + }); + + it('updates an item with the provided props', () => { + const object = { bar: { b: 2, c: 3 }, foo: { a: 1 } }; + const result = update(object, 'bar', { c: 4, d: 5 }); + expect(result).toEqual({ + bar: { b: 2, c: 4, d: 5 }, + foo: { a: 1 }, + }); + }); + + it('updates an item based on the function passed', () => { + const object = { bar: { b: 2, c: 3 }, foo: { a: 1 } }; + /** */ const fn = props => ({ ...props, c: props.c + 1 }); + const result = update(object, 'bar', fn); + expect(result).toEqual({ + bar: { b: 2, c: 4 }, + foo: { a: 1 }, + }); + }); +}); + +describe('removeItem', () => { + it('does not change input object', () => { + const object = { foo: { a: 1 } }; + unset(object, 'foo'); + expect(object).toEqual({ foo: { a: 1 } }); + }); + + it('removes item from object', () => { + const object = { bar: { a: 1 }, foo: { b: 2 } }; + const result = unset(object, 'bar'); + expect(result).toEqual({ foo: { b: 2 } }); + }); + + it('returns the same object shape if item does not exist', () => { + const object = { bar: { a: 1 }, foo: { b: 2 } }; + const result = unset(object, 'bubu'); + expect(result).toEqual({ bar: { a: 1 }, foo: { b: 2 } }); + }); +}); diff --git a/src/state/reducers/utils.js b/src/state/reducers/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..4caf4a4b2a2060819a7a795691ae2dea9628b916 --- /dev/null +++ b/src/state/reducers/utils.js @@ -0,0 +1,45 @@ +import _set from 'lodash/fp/set'; +import _update from 'lodash/fp/update'; +import _unset from 'lodash/fp/unset'; + +/** +* Sets the value at path of object. +* If a portion of `path` doesn't exist, it's created. +* +* @param {Object} object +* @param {String|String[]} path +* @param {any} value +* @return {Object} +*/ +export function set(object, path, value) { + return _set(path, value, object); +} + +/** +* Updates the value at path of object. +* If a portion of `path` doesn't exist, it's created. +* If `value` is a function it should have this signature: (currentValue) => newValue. +* If `value` is an object it is assumed that the current value is also an object +* and the new value will crated by: { ...currentValue, ...value }. +* +* @param {Object} object +* @param {String|String[]} path +* @param {Object|Function} value +* @return {Object} +*/ +export function update(object, path, value) { + return (typeof value) === 'function' + ? _update(path, value, object) + : _update(path, current => ({ ...current, ...value }), object); +} + +/** +* Removes the property at path of object. +* +* @param {Object} object +* @param {String|String[]} path +* @param {Object} +*/ +export function unset(object, path) { + return _unset(path, object); +}