From 971ce3a7f7c84d3cd046b3ad7a35f149594f8b85 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Wed, 8 Nov 2017 14:43:49 +0100
Subject: [PATCH 01/11] :sparkles: First naive implementation of slice walking
---
src/array/convertArrayMethod.js | 6 ++++-
src/array/push.spec.js | 24 ++++++++++++++++++
src/core/apply.js | 37 ++++++++++++++++++++++-----
src/core/path.utils.js | 10 +++++---
src/core/path.utils.spec.js | 26 -------------------
src/core/set.spec.js | 26 +++++++++++++++++++
src/util/lang.js | 24 ++++++++++++++++++
src/util/lang.spec.js | 44 +++++++++++++++++++++++++++++++++
8 files changed, 160 insertions(+), 37 deletions(-)
diff --git a/src/array/convertArrayMethod.js b/src/array/convertArrayMethod.js
index 43883faf..cdcc4e5f 100644
--- a/src/array/convertArrayMethod.js
+++ b/src/array/convertArrayMethod.js
@@ -1,7 +1,11 @@
import { convert } from 'core/convert'
+import {
+ isNil,
+} from 'util/lang'
+
const copyArray = array => {
- if (array === undefined || array === null) return []
+ if (isNil(array)) return []
if (Array.isArray(array)) return [...array]
return [array]
}
diff --git a/src/array/push.spec.js b/src/array/push.spec.js
index ee898eed..39fb7371 100644
--- a/src/array/push.spec.js
+++ b/src/array/push.spec.js
@@ -47,4 +47,28 @@ describe('Push', () => {
return output
}, { nested: { prop: 1 } }, 'nested.prop')
})
+
+ it('should push in several arrays', () => {
+ immutaTest(input => {
+ const output = push(input, 'nested.prop[:].arr', 2)
+ expect(output).toEqual({
+ nested: {
+ prop: [
+ { arr: [1, 2] },
+ { arr: [1, 2] },
+ { arr: [2] },
+ ],
+ },
+ })
+ return output
+ }, {
+ nested: {
+ prop: [
+ { arr: [1] },
+ { arr: 1 },
+ {},
+ ],
+ },
+ }, 'nested.prop')
+ })
})
diff --git a/src/core/apply.js b/src/core/apply.js
index 05711754..95078eea 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -1,8 +1,14 @@
import {
- isArrayProp,
+ isIndex,
+ isSlice,
} from './path.utils'
import { unsafeToPath } from './toPath'
+import {
+ isNaturalInteger,
+ isNil,
+} from 'util/lang'
+
/**
* Makes a copy of value.
* @function
@@ -14,17 +20,25 @@ import { unsafeToPath } from './toPath'
* @since 0.4.0
*/
const copy = (value, asArray) => {
- if (value === undefined || value === null) {
- if (asArray)
- return []
+ if (isNil(value)) {
+ if (asArray) return []
return {}
}
if (Array.isArray(value)) return [...value]
return { ...value }
}
+// FIXME move ?
+const getLength = value => {
+ if (isNil(value) || !isNaturalInteger(value.length)) return 0
+ return value.length
+}
+
+// FIXME move ?
+const min = (a, b) => a < b ? a : b
+
const callback = (obj, prop) => {
- if (obj === undefined || obj === null) return undefined
+ if (isNil(obj)) return undefined
return obj[prop]
}
@@ -55,9 +69,20 @@ const apply = (obj, path, operation) => {
const walkPath = (curObj, curPath) => {
const [prop, ...pathRest] = curPath
+ if (isSlice(prop)) {
+ const length = getLength(curObj)
+ const startIndex = prop[0] === undefined ? 0 : min(prop[0], length)
+ const endIndex = prop[1] === undefined ? length : min(prop[1], length)
+
+ let curSliceObj = curObj
+ for (let i = startIndex; i < endIndex; i++)
+ curSliceObj = walkPath(curSliceObj, [i, ...pathRest])
+ return curSliceObj
+ }
+
const value = callback(curObj, prop)
- const newObj = copy(curObj, isArrayProp(prop))
+ const newObj = copy(curObj, isIndex(prop))
if (curPath.length === 1) {
operation(newObj, prop, value)
diff --git a/src/core/path.utils.js b/src/core/path.utils.js
index ab577a12..718716c5 100644
--- a/src/core/path.utils.js
+++ b/src/core/path.utils.js
@@ -1,13 +1,15 @@
+import {
+ isNaturalInteger,
+} from 'util/lang'
+
/**
- * Tests whether arg
is a valid index, that is a positive integer.
+ * This is an alias for {@link util/isNaturalInteger}.
* @function
- * @param {*} arg The value to test
- * @return {boolean} True if arg
is a valid index, false otherwise
* @memberof core
* @private
* @since 0.4.0
*/
-const isIndex = arg => Number.isSafeInteger(arg) && arg >= 0
+const isIndex = isNaturalInteger
/**
* Tests whether arg
is a valid slice index, that is an integer or undefined
.
diff --git a/src/core/path.utils.spec.js b/src/core/path.utils.spec.js
index 36a49ec4..edc4930c 100644
--- a/src/core/path.utils.spec.js
+++ b/src/core/path.utils.spec.js
@@ -1,36 +1,10 @@
/* eslint-env jest */
import {
- isIndex,
isSlice,
isSliceIndex,
} from './path.utils'
describe('Path Utils', () => {
- describe('IsIndex', () => {
- it('should return true for any non negative integer', () => {
- expect(isIndex(0)).toBe(true)
- expect(isIndex(1)).toBe(true)
- expect(isIndex(6)).toBe(true)
- expect(isIndex(100000000000)).toBe(true)
- })
-
- it('should return false for any negative integer', () => {
- expect(isIndex(-1)).toBe(false)
- expect(isIndex(-6)).toBe(false)
- expect(isIndex(-100000000000)).toBe(false)
- })
-
- it('should return false for any non integer', () => {
- expect(isIndex(undefined)).toBe(false)
- expect(isIndex(null)).toBe(false)
- expect(isIndex(true)).toBe(false)
- expect(isIndex({})).toBe(false)
- expect(isIndex([])).toBe(false)
- expect(isIndex('')).toBe(false)
- expect(isIndex(.6)).toBe(false)
- })
- })
-
describe('IsSliceIndex', () => {
it('should return true for any integer or undefined', () => {
expect(isSliceIndex(0)).toBe(true)
diff --git a/src/core/set.spec.js b/src/core/set.spec.js
index 43bbca60..c1e73c52 100644
--- a/src/core/set.spec.js
+++ b/src/core/set.spec.js
@@ -31,6 +31,32 @@ describe('Set', () => {
}, 'nested.prop.0')
})
+ it('should set values in an array slice', () => {
+ immutaTest(input => {
+ const output = set(input, 'nested.prop[:]', 'final')
+ expect(output).toEqual({
+ nested: { prop: ['final', 'final', 'final'] },
+ other: {},
+ })
+ return output
+ }, {
+ nested: { prop: ['a', 'b', 'c'] },
+ other: {},
+ }, 'nested.prop')
+
+ immutaTest(input => {
+ const output = set(input, 'nested.prop[1:3].val', 'final')
+ expect(output).toEqual({
+ nested: { prop: [{ val: 'a' }, { val: 'final' }, { val: 'final' }, { val: 'd' }] },
+ other: {},
+ })
+ return output
+ }, {
+ nested: { prop: [{ val: 'a' }, { val: 'b' }, { val: 'c' }, { val: 'd' }] },
+ other: {},
+ }, 'nested.prop')
+ })
+
it('should set a deep undefined prop', () => {
immutaTest((input, path) => {
const output = set(input, path, 'final')
diff --git a/src/util/lang.js b/src/util/lang.js
index f24e4dff..d8e646de 100644
--- a/src/util/lang.js
+++ b/src/util/lang.js
@@ -1,3 +1,25 @@
+/**
+ * Tests whether arg
is a natural integer.
+ * @function
+ * @param {*} arg The value to test
+ * @return {boolean} True if arg
is a natural integer, false otherwise
+ * @memberof util
+ * @private
+ * @since 0.4.0
+ */
+const isNaturalInteger = arg => Number.isSafeInteger(arg) && arg >= 0
+
+/**
+ * Tests whether arg
is a undefined
or null
.
+ * @function
+ * @param {*} arg The value to test
+ * @return {boolean} True if arg
is undefined
or null
, false otherwise
+ * @memberof util
+ * @private
+ * @since 0.4.0
+ */
+const isNil = arg => arg === undefined || arg === null
+
/**
* Tests whether arg
is a Symbol.
* @param {*} arg The value to test
@@ -20,6 +42,8 @@ const isSymbol = arg => typeof arg === 'symbol'
const toString = arg => typeof arg === 'string' ? arg : `${arg}`
export {
+ isNaturalInteger,
+ isNil,
isSymbol,
toString,
}
diff --git a/src/util/lang.spec.js b/src/util/lang.spec.js
index c39f4fc6..39ce4ff8 100644
--- a/src/util/lang.spec.js
+++ b/src/util/lang.spec.js
@@ -1,10 +1,54 @@
/* eslint-env jest */
import {
+ isNaturalInteger,
+ isNil,
isSymbol,
toString,
} from 'util/lang'
describe('Lang utils', () => {
+ describe('IsNaturalInteger', () => {
+ it('should return true for any non negative integer', () => {
+ expect(isNaturalInteger(0)).toBe(true)
+ expect(isNaturalInteger(1)).toBe(true)
+ expect(isNaturalInteger(6)).toBe(true)
+ expect(isNaturalInteger(100000000000)).toBe(true)
+ })
+
+ it('should return false for any negative integer', () => {
+ expect(isNaturalInteger(-1)).toBe(false)
+ expect(isNaturalInteger(-6)).toBe(false)
+ expect(isNaturalInteger(-100000000000)).toBe(false)
+ })
+
+ it('should return false for any non integer', () => {
+ expect(isNaturalInteger(undefined)).toBe(false)
+ expect(isNaturalInteger(null)).toBe(false)
+ expect(isNaturalInteger(true)).toBe(false)
+ expect(isNaturalInteger({})).toBe(false)
+ expect(isNaturalInteger([])).toBe(false)
+ expect(isNaturalInteger('')).toBe(false)
+ expect(isNaturalInteger(.6)).toBe(false)
+ })
+ })
+
+ describe('IsNil', () => {
+ it('should return true for undefined and null', () => {
+ expect(isNil(undefined)).toBe(true)
+ expect(isNil(null)).toBe(true)
+ })
+
+ it('should return false for any other value than undefined and null', () => {
+ expect(isNil(true)).toBe(false)
+ expect(isNil({})).toBe(false)
+ expect(isNil([])).toBe(false)
+ expect(isNil('')).toBe(false)
+ expect(isNil(.6)).toBe(false)
+ expect(isNil('null')).toBe(false)
+ expect(isNil('undefined')).toBe(false)
+ })
+ })
+
describe('IsSymbol', () => {
it('should return true for symbols', () => {
expect(isSymbol(Symbol())).toBe(true)
From a7aaf27d8fb502fd6845890f193c8c4ef5231116 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Fri, 10 Nov 2017 12:28:09 +0100
Subject: [PATCH 02/11] :sparkles: Support negative slice indexes
---
src/core/apply.js | 20 +++++++++++++-------
src/core/set.spec.js | 12 ++++++++++++
2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index 95078eea..93c1bf0d 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -34,14 +34,22 @@ const getLength = value => {
return value.length
}
-// FIXME move ?
-const min = (a, b) => a < b ? a : b
-
const callback = (obj, prop) => {
if (isNil(obj)) return undefined
return obj[prop]
}
+const getSliceBound = (value, defaultValue, length) => {
+ if (value === undefined) return defaultValue
+ if (value < 0) return Math.max(length + value, 0)
+ return Math.min(value, length)
+}
+
+const getSliceBounds = ([start, end], length) => ([
+ getSliceBound(start, 0, length),
+ getSliceBound(end, length, length),
+])
+
/**
* Operation to apply on a nested property of an object, to be called by {@link core.apply|apply}.
* @memberof core
@@ -70,12 +78,10 @@ const apply = (obj, path, operation) => {
const [prop, ...pathRest] = curPath
if (isSlice(prop)) {
- const length = getLength(curObj)
- const startIndex = prop[0] === undefined ? 0 : min(prop[0], length)
- const endIndex = prop[1] === undefined ? length : min(prop[1], length)
+ const [start, end] = getSliceBounds(prop, getLength(curObj))
let curSliceObj = curObj
- for (let i = startIndex; i < endIndex; i++)
+ for (let i = start; i < end; i++)
curSliceObj = walkPath(curSliceObj, [i, ...pathRest])
return curSliceObj
}
diff --git a/src/core/set.spec.js b/src/core/set.spec.js
index c1e73c52..d84945c5 100644
--- a/src/core/set.spec.js
+++ b/src/core/set.spec.js
@@ -55,6 +55,18 @@ describe('Set', () => {
nested: { prop: [{ val: 'a' }, { val: 'b' }, { val: 'c' }, { val: 'd' }] },
other: {},
}, 'nested.prop')
+
+ immutaTest(input => {
+ const output = set(input, 'nested.prop[-3:-1].val', 'final')
+ expect(output).toEqual({
+ nested: { prop: [{ val: 'a' }, { val: 'final' }, { val: 'final' }, { val: 'd' }] },
+ other: {},
+ })
+ return output
+ }, {
+ nested: { prop: [{ val: 'a' }, { val: 'b' }, { val: 'c' }, { val: 'd' }] },
+ other: {},
+ }, 'nested.prop')
})
it('should set a deep undefined prop', () => {
From a821b1ede1d0633384f4942a0bb57cd900a51836 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 19:07:25 +0100
Subject: [PATCH 03/11] :zap: Avoid multiple array reinstanciations
---
src/core/apply.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index 93c1bf0d..21658c31 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -74,21 +74,22 @@ const getSliceBounds = ([start, end], length) => ([
* @since 0.4.0
*/
const apply = (obj, path, operation) => {
- const walkPath = (curObj, curPath) => {
+ const walkPath = (curObj, curPath, doCopy = true) => {
const [prop, ...pathRest] = curPath
if (isSlice(prop)) {
const [start, end] = getSliceBounds(prop, getLength(curObj))
- let curSliceObj = curObj
+ const newArr = copy(curObj, true)
for (let i = start; i < end; i++)
- curSliceObj = walkPath(curSliceObj, [i, ...pathRest])
- return curSliceObj
+ walkPath(newArr, [i, ...pathRest], false)
+ return newArr
}
const value = callback(curObj, prop)
- const newObj = copy(curObj, isIndex(prop))
+ let newObj = curObj
+ if (doCopy) newObj = copy(curObj, isIndex(prop))
if (curPath.length === 1) {
operation(newObj, prop, value)
From 124e1ef3dfb2dc5fe9cd4ba9536e59e949882cb7 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 21:45:20 +0100
Subject: [PATCH 04/11] :bulb: fix operation callback jsdoc
---
src/core/apply.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index 21658c31..ae17405f 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -55,7 +55,7 @@ const getSliceBounds = ([start, end], length) => ([
* @memberof core
* @callback operation
* @param {*} obj The last nested object
- * @param {string|number|Array} prop The prop of the last nested object
+ * @param {string|number} prop The prop of the last nested object
* @param {*} value The value of the prop
* @returns {*} Result of the operation
* @private
From 8ebda31bb5f0a4e6d15449553836b909d4c7c910 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 21:47:02 +0100
Subject: [PATCH 05/11] :hammer: Do not cap slice bounds at length
---
src/core/apply.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index ae17405f..95e3801a 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -42,7 +42,7 @@ const callback = (obj, prop) => {
const getSliceBound = (value, defaultValue, length) => {
if (value === undefined) return defaultValue
if (value < 0) return Math.max(length + value, 0)
- return Math.min(value, length)
+ return value
}
const getSliceBounds = ([start, end], length) => ([
From a481a959b29eeb886b22649d56a0e9d9424b029f Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 22:49:56 +0100
Subject: [PATCH 06/11] :truck::fire: Move around some utils code, remove
unused isArrayProp, inline apply callback for now
---
src/core/apply.js | 31 ++++++-------------------------
src/core/path.utils.js | 32 ++++++++++++++++++++------------
src/core/path.utils.spec.js | 13 +++++++++++++
src/util/lang.js | 13 +++++++++++++
src/util/lang.spec.js | 16 ++++++++++++++++
5 files changed, 68 insertions(+), 37 deletions(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index 95e3801a..98755df5 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -1,12 +1,13 @@
import {
+ getSliceBounds,
isIndex,
isSlice,
} from './path.utils'
import { unsafeToPath } from './toPath'
import {
- isNaturalInteger,
isNil,
+ length,
} from 'util/lang'
/**
@@ -28,28 +29,6 @@ const copy = (value, asArray) => {
return { ...value }
}
-// FIXME move ?
-const getLength = value => {
- if (isNil(value) || !isNaturalInteger(value.length)) return 0
- return value.length
-}
-
-const callback = (obj, prop) => {
- if (isNil(obj)) return undefined
- return obj[prop]
-}
-
-const getSliceBound = (value, defaultValue, length) => {
- if (value === undefined) return defaultValue
- if (value < 0) return Math.max(length + value, 0)
- return value
-}
-
-const getSliceBounds = ([start, end], length) => ([
- getSliceBound(start, 0, length),
- getSliceBound(end, length, length),
-])
-
/**
* Operation to apply on a nested property of an object, to be called by {@link core.apply|apply}.
* @memberof core
@@ -78,15 +57,17 @@ const apply = (obj, path, operation) => {
const [prop, ...pathRest] = curPath
if (isSlice(prop)) {
- const [start, end] = getSliceBounds(prop, getLength(curObj))
+ const [start, end] = getSliceBounds(prop, length(curObj))
const newArr = copy(curObj, true)
+
for (let i = start; i < end; i++)
walkPath(newArr, [i, ...pathRest], false)
+
return newArr
}
- const value = callback(curObj, prop)
+ const value = isNil(curObj) ? undefined : curObj[prop]
let newObj = curObj
if (doCopy) newObj = copy(curObj, isIndex(prop))
diff --git a/src/core/path.utils.js b/src/core/path.utils.js
index 718716c5..041138cb 100644
--- a/src/core/path.utils.js
+++ b/src/core/path.utils.js
@@ -2,6 +2,25 @@ import {
isNaturalInteger,
} from 'util/lang'
+const getSliceBound = (value, defaultValue, length) => {
+ if (value === undefined) return defaultValue
+ if (value < 0) return Math.max(length + value, 0)
+ return value
+}
+
+/**
+ * Get the actual bounds of a slice.
+ * @param {Array} bounds The bounds of the slice
+ * @param {number} length The length of the actual array
+ * @returns {Array} The actual bounds of the slice
+ * @private
+ * @since 0.4.0
+ */
+const getSliceBounds = ([start, end], length) => ([
+ getSliceBound(start, 0, length),
+ getSliceBound(end, length, length),
+])
+
/**
* This is an alias for {@link util/isNaturalInteger}.
* @function
@@ -37,19 +56,8 @@ const isSlice = arg => {
return isSliceIndex(arg[0]) && isSliceIndex(arg[1])
}
-/**
- * Tests whether arg
is either an index or a slice.
- * @function
- * @param {*} arg The value to test
- * @return {boolean} True if arg
is either an index or a slice, false otherwise
- * @memberof core
- * @private
- * @since 0.4.0
- */
-const isArrayProp = arg => isIndex(arg) || isSlice(arg)
-
export {
- isArrayProp,
+ getSliceBounds,
isIndex,
isSlice,
isSliceIndex,
diff --git a/src/core/path.utils.spec.js b/src/core/path.utils.spec.js
index edc4930c..3e9bafd4 100644
--- a/src/core/path.utils.spec.js
+++ b/src/core/path.utils.spec.js
@@ -1,10 +1,23 @@
/* eslint-env jest */
import {
+ getSliceBounds,
isSlice,
isSliceIndex,
} from './path.utils'
describe('Path Utils', () => {
+ describe('GetSliceBounds', () => {
+ it('should return actual slice bounds', () => {
+ expect(getSliceBounds([undefined, undefined], 0)).toEqual([0, 0])
+ expect(getSliceBounds([-2, -1], 0)).toEqual([0, 0])
+ expect(getSliceBounds([1, 2], 0)).toEqual([1, 2])
+
+ expect(getSliceBounds([undefined, undefined], 6)).toEqual([0, 6])
+ expect(getSliceBounds([1, -1], 6)).toEqual([1, 5])
+ expect(getSliceBounds([7, 8], 6)).toEqual([7, 8])
+ })
+ })
+
describe('IsSliceIndex', () => {
it('should return true for any integer or undefined', () => {
expect(isSliceIndex(0)).toBe(true)
diff --git a/src/util/lang.js b/src/util/lang.js
index d8e646de..7babf74c 100644
--- a/src/util/lang.js
+++ b/src/util/lang.js
@@ -31,6 +31,18 @@ const isNil = arg => arg === undefined || arg === null
*/
const isSymbol = arg => typeof arg === 'symbol'
+/**
+ * Returns the length of arg
.
+ * @param {*} arg The value of which length must be returned
+ * @returns {number} The length of arg
+ * @private
+ * @since 0.4.0
+ */
+const length = arg => {
+ if (isNil(arg) || !isNaturalInteger(arg.length)) return 0
+ return arg.length
+}
+
/**
* Converts arg
to a string using string interpolation.
* @param {*} arg The value to convert
@@ -45,5 +57,6 @@ export {
isNaturalInteger,
isNil,
isSymbol,
+ length,
toString,
}
diff --git a/src/util/lang.spec.js b/src/util/lang.spec.js
index 39ce4ff8..ceb8ae9b 100644
--- a/src/util/lang.spec.js
+++ b/src/util/lang.spec.js
@@ -3,6 +3,7 @@ import {
isNaturalInteger,
isNil,
isSymbol,
+ length,
toString,
} from 'util/lang'
@@ -63,6 +64,21 @@ describe('Lang utils', () => {
})
})
+ describe('Length', () => {
+ it('should return length of array', () => {
+ expect(length(Array(666))).toBe(666)
+ expect(length([])).toBe(0)
+ expect(length([1, 2, 3])).toBe(3)
+ })
+
+ it('should return zero if arg has no length', () => {
+ expect(length({})).toBe(0)
+ expect(length(666)).toBe(0)
+ expect(length()).toBe(0)
+ expect(length(null)).toBe(0)
+ })
+ })
+
describe('ToString', () => {
it('should return string representation', () => {
expect(toString()).toBe('undefined')
From 0313da6cf0eca4c2b9b28e398f3c733ab0f0c699 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 23:03:23 +0100
Subject: [PATCH 07/11] :rotating_light: fix lint after rebase
---
src/core/apply.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/apply.js b/src/core/apply.js
index 98755df5..579c0f98 100644
--- a/src/core/apply.js
+++ b/src/core/apply.js
@@ -3,13 +3,14 @@ import {
isIndex,
isSlice,
} from './path.utils'
-import { unsafeToPath } from './toPath'
import {
isNil,
length,
} from 'util/lang'
+import { unsafeToPath } from './toPath'
+
/**
* Makes a copy of value.
* @function
From f1116e9cdac1a44de47b00a0c46e94a05e046d9d Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 23:13:22 +0100
Subject: [PATCH 08/11] :truck: Make apply's own test file
---
src/array/push.spec.js | 24 ------------------------
src/core/apply.spec.js | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+), 24 deletions(-)
create mode 100644 src/core/apply.spec.js
diff --git a/src/array/push.spec.js b/src/array/push.spec.js
index 39fb7371..ee898eed 100644
--- a/src/array/push.spec.js
+++ b/src/array/push.spec.js
@@ -47,28 +47,4 @@ describe('Push', () => {
return output
}, { nested: { prop: 1 } }, 'nested.prop')
})
-
- it('should push in several arrays', () => {
- immutaTest(input => {
- const output = push(input, 'nested.prop[:].arr', 2)
- expect(output).toEqual({
- nested: {
- prop: [
- { arr: [1, 2] },
- { arr: [1, 2] },
- { arr: [2] },
- ],
- },
- })
- return output
- }, {
- nested: {
- prop: [
- { arr: [1] },
- { arr: 1 },
- {},
- ],
- },
- }, 'nested.prop')
- })
})
diff --git a/src/core/apply.spec.js b/src/core/apply.spec.js
new file mode 100644
index 00000000..5f05f4c9
--- /dev/null
+++ b/src/core/apply.spec.js
@@ -0,0 +1,40 @@
+/* eslint-env jest */
+import { apply } from './apply'
+import { immutaTest } from 'test.utils'
+
+describe('Apply', () => {
+
+ const _inc = (v, i = 1) => {
+ let r = Number(v)
+ if (Number.isNaN(r)) r = 0
+ return r + i
+ }
+
+ const inc = (obj, path, ...args) => apply(obj, path, (obj, prop) => { obj[prop] = _inc(obj[prop], ...args) })
+
+ it('should inc in all an array', () => {
+ immutaTest(input => {
+ const output = inc(input, 'nested.prop[:].val', 2)
+ expect(output).toEqual({
+ nested: {
+ prop: [
+ { val: 6 },
+ { val: -6 },
+ { val: 2 },
+ { val: 2 },
+ ],
+ },
+ })
+ return output
+ }, {
+ nested: {
+ prop: [
+ { val: 4 },
+ { val: -8 },
+ { val: 'a' },
+ {},
+ ],
+ },
+ }, 'nested.prop')
+ })
+})
From 23edb077c69eb1ecf55094b48b89e0fbd365cad4 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 23:19:16 +0100
Subject: [PATCH 09/11] :hammer: Finer mutations detection in tests
---
src/core/apply.spec.js | 49 ++++++++++++++++++++++++++----------------
1 file changed, 31 insertions(+), 18 deletions(-)
diff --git a/src/core/apply.spec.js b/src/core/apply.spec.js
index 5f05f4c9..dd43edc4 100644
--- a/src/core/apply.spec.js
+++ b/src/core/apply.spec.js
@@ -13,28 +13,41 @@ describe('Apply', () => {
const inc = (obj, path, ...args) => apply(obj, path, (obj, prop) => { obj[prop] = _inc(obj[prop], ...args) })
it('should inc in all an array', () => {
- immutaTest(input => {
- const output = inc(input, 'nested.prop[:].val', 2)
- expect(output).toEqual({
+ immutaTest(
+ input => {
+ const output = inc(input, 'nested.prop[:].val', 2)
+ expect(output).toEqual({
+ nested: {
+ prop: [
+ {
+ val: 6,
+ other: {},
+ },
+ { val: -6 },
+ { val: 2 },
+ { val: 2 },
+ ],
+ },
+ })
+ return output
+ },
+ {
nested: {
prop: [
- { val: 6 },
- { val: -6 },
- { val: 2 },
- { val: 2 },
+ {
+ val: 4,
+ other: {},
+ },
+ { val: -8 },
+ { val: 'a' },
+ {},
],
},
- })
- return output
- }, {
- nested: {
- prop: [
- { val: 4 },
- { val: -8 },
- { val: 'a' },
- {},
- ],
},
- }, 'nested.prop')
+ 'nested.prop.0.val',
+ 'nested.prop.1.val',
+ 'nested.prop.2.val',
+ 'nested.prop.3.val',
+ )
})
})
From 5aae42730bac6a38603d54608dd19b0353a55ca7 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 23:26:01 +0100
Subject: [PATCH 10/11] :truck: Move tests in apply's own test file
---
src/core/apply.spec.js | 57 +++++++++++++++++++++++++++++++++++++++++-
src/core/set.spec.js | 38 ----------------------------
2 files changed, 56 insertions(+), 39 deletions(-)
diff --git a/src/core/apply.spec.js b/src/core/apply.spec.js
index dd43edc4..0fffc567 100644
--- a/src/core/apply.spec.js
+++ b/src/core/apply.spec.js
@@ -12,7 +12,7 @@ describe('Apply', () => {
const inc = (obj, path, ...args) => apply(obj, path, (obj, prop) => { obj[prop] = _inc(obj[prop], ...args) })
- it('should inc in all an array', () => {
+ it('should inc in an array slice', () => {
immutaTest(
input => {
const output = inc(input, 'nested.prop[:].val', 2)
@@ -49,5 +49,60 @@ describe('Apply', () => {
'nested.prop.2.val',
'nested.prop.3.val',
)
+
+ immutaTest(
+ input => {
+ const output = inc(input, 'nested.prop[1:3].val')
+ expect(output).toEqual({
+ nested: {
+ prop: [{ val: 0 }, {
+ val: 2,
+ other: {},
+ }, { val: 3 }, { val: 3 }],
+ },
+ other: {},
+ })
+ return output
+ },
+ {
+ nested: {
+ prop: [{ val: 0 }, {
+ val: 1,
+ other: {},
+ }, { val: 2 }, { val: 3 }],
+ },
+ other: {},
+ },
+ 'nested.prop.1.val',
+ 'nested.prop.2.val',
+ )
+
+ immutaTest(
+ input => {
+ const output = inc(input, 'nested.prop[-3:-1].val')
+ expect(output).toEqual({
+ nested: {
+ prop: [{ val: 0 }, {
+ val: 2,
+ other: {},
+ }, { val: 3 }, { val: 3 }],
+ },
+ other: {},
+ })
+ return output
+ },
+ {
+ nested: {
+ prop: [{ val: 0 }, {
+ val: 1,
+ other: {},
+ }, { val: 2 }, { val: 3 }],
+ },
+ other: {},
+ },
+ 'nested.prop.1.val',
+ 'nested.prop.2.val',
+ )
})
+
})
diff --git a/src/core/set.spec.js b/src/core/set.spec.js
index d84945c5..43bbca60 100644
--- a/src/core/set.spec.js
+++ b/src/core/set.spec.js
@@ -31,44 +31,6 @@ describe('Set', () => {
}, 'nested.prop.0')
})
- it('should set values in an array slice', () => {
- immutaTest(input => {
- const output = set(input, 'nested.prop[:]', 'final')
- expect(output).toEqual({
- nested: { prop: ['final', 'final', 'final'] },
- other: {},
- })
- return output
- }, {
- nested: { prop: ['a', 'b', 'c'] },
- other: {},
- }, 'nested.prop')
-
- immutaTest(input => {
- const output = set(input, 'nested.prop[1:3].val', 'final')
- expect(output).toEqual({
- nested: { prop: [{ val: 'a' }, { val: 'final' }, { val: 'final' }, { val: 'd' }] },
- other: {},
- })
- return output
- }, {
- nested: { prop: [{ val: 'a' }, { val: 'b' }, { val: 'c' }, { val: 'd' }] },
- other: {},
- }, 'nested.prop')
-
- immutaTest(input => {
- const output = set(input, 'nested.prop[-3:-1].val', 'final')
- expect(output).toEqual({
- nested: { prop: [{ val: 'a' }, { val: 'final' }, { val: 'final' }, { val: 'd' }] },
- other: {},
- })
- return output
- }, {
- nested: { prop: [{ val: 'a' }, { val: 'b' }, { val: 'c' }, { val: 'd' }] },
- other: {},
- }, 'nested.prop')
- })
-
it('should set a deep undefined prop', () => {
immutaTest((input, path) => {
const output = set(input, path, 'final')
From ddf4c503a09d913eb04ba7c584e4529b0fa4eb73 Mon Sep 17 00:00:00 2001
From: nlepage <19571875+nlepage@users.noreply.github.com>
Date: Sun, 12 Nov 2017 23:57:30 +0100
Subject: [PATCH 11/11] :white_check_mark: Test out of bound slice
---
src/core/apply.spec.js | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/src/core/apply.spec.js b/src/core/apply.spec.js
index 0fffc567..5df139cc 100644
--- a/src/core/apply.spec.js
+++ b/src/core/apply.spec.js
@@ -105,4 +105,21 @@ describe('Apply', () => {
)
})
+ immutaTest(
+ input => {
+ const output = inc(input, 'nested.prop[3:5].val', 6)
+ expect(output).toEqual({
+ nested: { prop: [{ val: 0 }, { val: 1 }, undefined, { val: 6 }, { val: 6 }] },
+ other: {},
+ })
+ return output
+ },
+ {
+ nested: { prop: [{ val: 0 }, { val: 1 }] },
+ other: {},
+ },
+ 'nested.prop.2',
+ 'nested.prop.3.val',
+ 'nested.prop.4.val',
+ )
})