@splendidlabz/utils 1.7.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/dom/font-size.cjs +1 -1
- package/dist/cjs/dom/index.cjs +91 -1
- package/dist/cjs/dom/observers/index.cjs +92 -2
- package/dist/cjs/dom/observers/scroll-observer.cjs +170 -0
- package/dist/cjs/lib/date/index.cjs +3 -1
- package/dist/cjs/lib/date/time.cjs +3 -1
- package/dist/cjs/lib/functions/functional.cjs +20 -0
- package/dist/cjs/lib/functions/index.cjs +20 -0
- package/dist/cjs/lib/index.cjs +27 -2
- package/dist/cjs/lib/numbers/index.cjs +1 -1
- package/dist/cjs/lib/objects/extend.cjs +2 -1
- package/dist/cjs/lib/objects/index.cjs +4 -1
- package/dist/cjs/lib/objects/mix/mix.cjs +4 -1
- package/dist/cjs/node/file-cache.cjs +3 -3
- package/dist/cjs/node/index.cjs +3 -3
- package/dist/esm/dom/font-size.js +1 -1
- package/dist/esm/dom/index.js +90 -1
- package/dist/esm/dom/observers/index.js +90 -1
- package/dist/esm/dom/observers/scroll-observer.js +144 -0
- package/dist/esm/lib/date/index.js +3 -1
- package/dist/esm/lib/date/time.js +3 -1
- package/dist/esm/lib/functions/functional.js +18 -0
- package/dist/esm/lib/functions/index.js +18 -0
- package/dist/esm/lib/index.js +24 -2
- package/dist/esm/lib/numbers/index.js +1 -1
- package/dist/esm/lib/objects/extend.js +2 -1
- package/dist/esm/lib/objects/index.js +3 -1
- package/dist/esm/lib/objects/mix/mix.js +3 -1
- package/dist/esm/node/file-cache.js +1 -1
- package/dist/esm/node/index.js +1 -1
- package/dist/types/dom/index.d.cts +1 -0
- package/dist/types/dom/observers/index.d.cts +1 -0
- package/dist/types/dom/observers/scroll-observer.d.cts +27 -0
- package/dist/types/lib/functions/functional.d.cts +71 -5
- package/dist/types/lib/functions/index.d.cts +1 -1
- package/dist/types/lib/index.d.cts +2 -2
- package/dist/types/lib/objects/extend.d.cts +1 -1
- package/dist/types/lib/objects/index.d.cts +1 -1
- package/dist/types/lib/objects/mix/mix.d.cts +43 -3
- package/package.json +2 -3
- package/src/dom/observers/index.js +1 -0
- package/src/dom/observers/resize-observer.js +5 -5
- package/src/dom/observers/scroll-observer.js +132 -0
- package/src/lib/date/time.js +2 -0
- package/src/lib/functions/functional.js +82 -0
- package/src/lib/functions/functional.test.js +196 -0
- package/src/lib/numbers/index.js +1 -1
- package/src/lib/numbers/split-unit.test.js +45 -0
- package/src/lib/objects/mix/mix.js +74 -4
- package/src/lib/objects/mix/mix.test.js +51 -2
- package/src/node/file-cache.js +1 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
compose,
|
|
4
|
+
composeAsync,
|
|
5
|
+
curry,
|
|
6
|
+
pipe,
|
|
7
|
+
pipeAsync,
|
|
8
|
+
times,
|
|
9
|
+
} from './functional.js'
|
|
10
|
+
|
|
11
|
+
describe('functional utilities', () => {
|
|
12
|
+
describe('curry', () => {
|
|
13
|
+
it('should curry a function with multiple arguments', () => {
|
|
14
|
+
const add = (a, b, c) => a + b + c
|
|
15
|
+
const curriedAdd = curry(add)
|
|
16
|
+
|
|
17
|
+
expect(curriedAdd(1)(2)(3)).toBe(6)
|
|
18
|
+
expect(curriedAdd(1, 2)(3)).toBe(6)
|
|
19
|
+
expect(curriedAdd(1)(2, 3)).toBe(6)
|
|
20
|
+
expect(curriedAdd(1, 2, 3)).toBe(6)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('compose (sync)', () => {
|
|
25
|
+
it('should compose functions from right to left', () => {
|
|
26
|
+
const add1 = x => x + 1
|
|
27
|
+
const multiply2 = x => x * 2
|
|
28
|
+
const composed = compose(add1, multiply2)
|
|
29
|
+
|
|
30
|
+
expect(composed(3)).toBe(7) // (3 * 2) + 1
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should work with single function', () => {
|
|
34
|
+
const add1 = x => x + 1
|
|
35
|
+
const composed = compose(add1)
|
|
36
|
+
|
|
37
|
+
expect(composed(5)).toBe(6)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should work with multiple functions', () => {
|
|
41
|
+
const add1 = x => x + 1
|
|
42
|
+
const multiply2 = x => x * 2
|
|
43
|
+
const subtract3 = x => x - 3
|
|
44
|
+
const composed = compose(subtract3, add1, multiply2)
|
|
45
|
+
|
|
46
|
+
expect(composed(5)).toBe(8) // ((5 * 2) + 1) - 3 = 8
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('composeAsync', () => {
|
|
51
|
+
it('should compose async functions from right to left', async () => {
|
|
52
|
+
const add1 = x => x + 1
|
|
53
|
+
const multiplyAsync = async x => x * 2
|
|
54
|
+
|
|
55
|
+
const result = await composeAsync(add1, multiplyAsync)(3)
|
|
56
|
+
expect(result).toBe(7) // (3 * 2) + 1
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should work with all sync functions', async () => {
|
|
60
|
+
const add1 = x => x + 1
|
|
61
|
+
const multiply2 = x => x * 2
|
|
62
|
+
|
|
63
|
+
const result = await composeAsync(add1, multiply2)(3)
|
|
64
|
+
expect(result).toBe(7) // (3 * 2) + 1
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should work with all async functions', async () => {
|
|
68
|
+
const add1Async = async x => x + 1
|
|
69
|
+
const multiply2Async = async x => x * 2
|
|
70
|
+
|
|
71
|
+
const result = await composeAsync(add1Async, multiply2Async)(3)
|
|
72
|
+
expect(result).toBe(7) // (3 * 2) + 1
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should work with mixed sync and async functions', async () => {
|
|
76
|
+
const add1 = x => x + 1
|
|
77
|
+
const multiply2Async = async x => x * 2
|
|
78
|
+
const subtract3 = x => x - 3
|
|
79
|
+
|
|
80
|
+
const result = await composeAsync(subtract3, add1, multiply2Async)(5)
|
|
81
|
+
expect(result).toBe(8) // ((5 * 2) + 1) - 3 = 8
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('pipe (sync)', () => {
|
|
86
|
+
it('should pipe functions from left to right', () => {
|
|
87
|
+
const add1 = x => x + 1
|
|
88
|
+
const multiply2 = x => x * 2
|
|
89
|
+
const piped = pipe(add1, multiply2)
|
|
90
|
+
|
|
91
|
+
expect(piped(3)).toBe(8) // (3 + 1) * 2
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should work with single function', () => {
|
|
95
|
+
const add1 = x => x + 1
|
|
96
|
+
const piped = pipe(add1)
|
|
97
|
+
|
|
98
|
+
expect(piped(5)).toBe(6)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should work with multiple functions', () => {
|
|
102
|
+
const add1 = x => x + 1
|
|
103
|
+
const multiply2 = x => x * 2
|
|
104
|
+
const subtract3 = x => x - 3
|
|
105
|
+
const piped = pipe(add1, multiply2, subtract3)
|
|
106
|
+
|
|
107
|
+
expect(piped(5)).toBe(9) // ((5 + 1) * 2) - 3 = 9
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('pipeAsync', () => {
|
|
112
|
+
it('should pipe async functions from left to right', async () => {
|
|
113
|
+
const add1 = x => x + 1
|
|
114
|
+
const multiplyAsync = async x => x * 2
|
|
115
|
+
|
|
116
|
+
const result = await pipeAsync(add1, multiplyAsync)(3)
|
|
117
|
+
expect(result).toBe(8) // (3 + 1) * 2
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should work with all sync functions', async () => {
|
|
121
|
+
const add1 = x => x + 1
|
|
122
|
+
const multiply2 = x => x * 2
|
|
123
|
+
|
|
124
|
+
const result = await pipeAsync(add1, multiply2)(3)
|
|
125
|
+
expect(result).toBe(8) // (3 + 1) * 2
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should work with all async functions', async () => {
|
|
129
|
+
const add1Async = async x => x + 1
|
|
130
|
+
const multiply2Async = async x => x * 2
|
|
131
|
+
|
|
132
|
+
const result = await pipeAsync(add1Async, multiply2Async)(3)
|
|
133
|
+
expect(result).toBe(8) // (3 + 1) * 2
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should work with mixed sync and async functions', async () => {
|
|
137
|
+
const add1 = x => x + 1
|
|
138
|
+
const multiply2Async = async x => x * 2
|
|
139
|
+
const subtract3 = x => x - 3
|
|
140
|
+
|
|
141
|
+
const result = await pipeAsync(add1, multiply2Async, subtract3)(5)
|
|
142
|
+
expect(result).toBe(9) // ((5 + 1) * 2) - 3 = 9
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should handle promises in the pipeline', async () => {
|
|
146
|
+
const asyncAdd = async x => {
|
|
147
|
+
await new Promise(resolve => setTimeout(resolve, 1))
|
|
148
|
+
return x + 1
|
|
149
|
+
}
|
|
150
|
+
const multiply2 = x => x * 2
|
|
151
|
+
|
|
152
|
+
const result = await pipeAsync(asyncAdd, multiply2)(3)
|
|
153
|
+
expect(result).toBe(8) // (3 + 1) * 2
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('times', () => {
|
|
158
|
+
it('should call function n times with index', () => {
|
|
159
|
+
const result = times(i => i * 2, 3)
|
|
160
|
+
expect(result).toEqual([0, 2, 4])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('should work with zero iterations', () => {
|
|
164
|
+
const result = times(i => i, 0)
|
|
165
|
+
expect(result).toEqual([])
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should work with functions that ignore index', () => {
|
|
169
|
+
const result = times(() => 'hello', 2)
|
|
170
|
+
expect(result).toEqual(['hello', 'hello'])
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Edge cases and error handling
|
|
175
|
+
describe('edge cases', () => {
|
|
176
|
+
it('should handle empty function arrays in compose', () => {
|
|
177
|
+
const composed = compose()
|
|
178
|
+
expect(composed(5)).toBe(5)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should handle empty function arrays in pipe', () => {
|
|
182
|
+
const piped = pipe()
|
|
183
|
+
expect(piped(5)).toBe(5)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('should handle empty function arrays in composeAsync', async () => {
|
|
187
|
+
const result = await composeAsync()(5)
|
|
188
|
+
expect(result).toBe(5)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('should handle empty function arrays in pipeAsync', async () => {
|
|
192
|
+
const result = await pipeAsync()(5)
|
|
193
|
+
expect(result).toBe(5)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
package/src/lib/numbers/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export function splitUnit(string) {
|
|
|
4
4
|
// If there are no units, return the number straight.
|
|
5
5
|
if (typeof string === 'number') return [string, null]
|
|
6
6
|
|
|
7
|
-
const regex = /(-?\d*\.?\d+)([a-z]+)/i
|
|
7
|
+
const regex = /(-?\d*\.?\d+)\s*([a-z]+)/i
|
|
8
8
|
const match = string.match(regex)
|
|
9
9
|
|
|
10
10
|
if (match) return [parseFloat(match[1]), match[2]]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { splitUnit } from './index.js'
|
|
3
|
+
|
|
4
|
+
describe('splitUnit', () => {
|
|
5
|
+
test('handles number input', () => {
|
|
6
|
+
const result = splitUnit(42)
|
|
7
|
+
expect(result).toEqual([42, null])
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('handles unitless string numbers', () => {
|
|
11
|
+
const result = splitUnit('100')
|
|
12
|
+
expect(result).toEqual([100, null])
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('handles "3hours" without space', () => {
|
|
16
|
+
const result = splitUnit('3hours')
|
|
17
|
+
expect(result).toEqual([3, 'hours'])
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('handles "2rem" CSS unit', () => {
|
|
21
|
+
const result = splitUnit('2rem')
|
|
22
|
+
expect(result).toEqual([2, 'rem'])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('handles decimal values', () => {
|
|
26
|
+
const result = splitUnit('1.5em')
|
|
27
|
+
expect(result).toEqual([1.5, 'em'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('handles negative values', () => {
|
|
31
|
+
const result = splitUnit('-10px')
|
|
32
|
+
expect(result).toEqual([-10, 'px'])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Tests with spaces
|
|
36
|
+
test('handles values with spaces', () => {
|
|
37
|
+
const result = splitUnit('30 days')
|
|
38
|
+
expect(result).toEqual([30, 'days'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('handles values with many spaces between number and unit', () => {
|
|
42
|
+
const result = splitUnit('10 em')
|
|
43
|
+
expect(result).toEqual([10, 'em'])
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -1,7 +1,45 @@
|
|
|
1
|
-
const DEFAULT_OPTIONS = { array: '
|
|
2
|
-
|
|
1
|
+
const DEFAULT_OPTIONS = { array: 'replace' } // Can be concat or replace
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deep merges multiple objects into a new object with intelligent handling of arrays and nested objects.
|
|
5
|
+
*
|
|
6
|
+
* @param {...Object} sources - Objects to merge. Null and undefined values are skipped.
|
|
7
|
+
* @return {Object} A new object containing the merged properties from all sources.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const obj1 = { a: 1, b: { x: 10 } }
|
|
11
|
+
* const obj2 = { b: { y: 20 }, c: 3 }
|
|
12
|
+
* const result = mix(obj1, obj2)
|
|
13
|
+
* // Returns: { a: 1, b: { x: 10, y: 20 }, c: 3 }
|
|
14
|
+
*/
|
|
3
15
|
export const mix = createMix()
|
|
4
|
-
|
|
16
|
+
export const concatMix = createMix({ array: 'concat' })
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a customized mix function with specific options for array handling.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} [userOptions={}] - Configuration options for the mix function.
|
|
22
|
+
* @param {('replace'|'concat')} [userOptions.array='replace'] - How to handle array merging:
|
|
23
|
+
* - 'replace': Arrays from later sources completely replace arrays from earlier sources
|
|
24
|
+
* - 'concat': Arrays are concatenated together
|
|
25
|
+
* @return {Function} A mix function configured with the provided options.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Create mix function that concatenates arrays
|
|
29
|
+
* const concatMix = createMix({ array: 'concat' })
|
|
30
|
+
* const obj1 = { items: [1, 2] }
|
|
31
|
+
* const obj2 = { items: [3, 4] }
|
|
32
|
+
* const result = concatMix(obj1, obj2)
|
|
33
|
+
* // Returns: { items: [1, 2, 3, 4] }
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Create mix function that replaces arrays (default behavior)
|
|
37
|
+
* const replaceMix = createMix({ array: 'replace' })
|
|
38
|
+
* const obj1 = { items: [1, 2] }
|
|
39
|
+
* const obj2 = { items: [3, 4] }
|
|
40
|
+
* const result = replaceMix(obj1, obj2)
|
|
41
|
+
* // Returns: { items: [3, 4] }
|
|
42
|
+
*/
|
|
5
43
|
export function createMix(userOptions = {}) {
|
|
6
44
|
const options = { ...DEFAULT_OPTIONS, ...userOptions }
|
|
7
45
|
|
|
@@ -18,6 +56,15 @@ export function createMix(userOptions = {}) {
|
|
|
18
56
|
return _mix
|
|
19
57
|
}
|
|
20
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Recursively merges properties from input object into output object.
|
|
61
|
+
* Handles property descriptors, prevents prototype pollution, and deep clones values.
|
|
62
|
+
*
|
|
63
|
+
* @param {Object} output - The target object to merge properties into.
|
|
64
|
+
* @param {Object} input - The source object to merge properties from.
|
|
65
|
+
* @param {Object} options - Merge options including array handling strategy.
|
|
66
|
+
* @property {('replace'|'concat')} options.array - How to handle array merging.
|
|
67
|
+
*/
|
|
21
68
|
function merge(output, input, options) {
|
|
22
69
|
const props = Object.keys(input)
|
|
23
70
|
|
|
@@ -63,7 +110,18 @@ function merge(output, input, options) {
|
|
|
63
110
|
}
|
|
64
111
|
}
|
|
65
112
|
|
|
66
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Creates a deep clone of a property descriptor value.
|
|
115
|
+
* Handles arrays, objects, dates, maps, sets, and other types appropriately.
|
|
116
|
+
*
|
|
117
|
+
* @param {*} value - The value to clone.
|
|
118
|
+
* @return {*} A deep clone of the input value.
|
|
119
|
+
* @property {Array} value - Cloned recursively with all elements deep cloned.
|
|
120
|
+
* @property {Object} value - Cloned with all properties and descriptors preserved.
|
|
121
|
+
* @property {Date} value - Cloned as new Date with same time value.
|
|
122
|
+
* @property {Map} value - Cloned with all entries deep cloned.
|
|
123
|
+
* @property {Set} value - Cloned with all entries deep cloned.
|
|
124
|
+
*/
|
|
67
125
|
function cloneDescriptorValue(value) {
|
|
68
126
|
// Arrays
|
|
69
127
|
if (objectType(value) === '[object Array]') {
|
|
@@ -116,6 +174,18 @@ function cloneDescriptorValue(value) {
|
|
|
116
174
|
return value
|
|
117
175
|
}
|
|
118
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Returns the precise object type of a value using Object.prototype.toString.
|
|
179
|
+
*
|
|
180
|
+
* @param {*} value - The value to get the type of.
|
|
181
|
+
* @return {string} The object type string (e.g., '[object Array]', '[object Object]', '[object Date]').
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* objectType([]) // '[object Array]'
|
|
185
|
+
* objectType({}) // '[object Object]'
|
|
186
|
+
* objectType(new Date()) // '[object Date]'
|
|
187
|
+
* objectType(null) // '[object Null]'
|
|
188
|
+
*/
|
|
119
189
|
function objectType(value) {
|
|
120
190
|
return Object.prototype.toString.call(value)
|
|
121
191
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import { createMix, mix } from './mix.js'
|
|
3
|
+
import { concatMix, createMix, mix } from './mix.js'
|
|
4
4
|
|
|
5
5
|
it('Merge objects', () => {
|
|
6
6
|
const one = { one: 'one', overwrite: 'nope' }
|
|
@@ -176,7 +176,8 @@ it('Double objects with nested same array', () => {
|
|
|
176
176
|
|
|
177
177
|
const three = mix(one, two)
|
|
178
178
|
expect(typeof three.array.push).toBe('function')
|
|
179
|
-
expect(three.array).toHaveLength(
|
|
179
|
+
expect(three.array).toHaveLength(3)
|
|
180
|
+
expect(three.array).toEqual([4, 5, 6])
|
|
180
181
|
})
|
|
181
182
|
|
|
182
183
|
it('Objects in Arrays', () => {
|
|
@@ -217,6 +218,54 @@ it('Deep nested arrays', () => {
|
|
|
217
218
|
expect(two.array[1]).not.toBe(three.array[1])
|
|
218
219
|
})
|
|
219
220
|
|
|
221
|
+
it('concatMix: concatenates arrays', () => {
|
|
222
|
+
const one = {
|
|
223
|
+
array: [1, 2, 3],
|
|
224
|
+
}
|
|
225
|
+
const two = {
|
|
226
|
+
array: [4, 5, 6],
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const three = concatMix(one, two)
|
|
230
|
+
expect(typeof three.array.push).toBe('function')
|
|
231
|
+
expect(three.array).toHaveLength(6)
|
|
232
|
+
expect(three.array).toEqual([1, 2, 3, 4, 5, 6])
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('concatMix: empty array concat', () => {
|
|
236
|
+
const one = {}
|
|
237
|
+
const two = { array: [1, 2, 3] }
|
|
238
|
+
const three = concatMix(one, two)
|
|
239
|
+
|
|
240
|
+
expect(three).toEqual(two)
|
|
241
|
+
expect(typeof three.array.push).toBe('function')
|
|
242
|
+
expect(three.array).toHaveLength(3)
|
|
243
|
+
|
|
244
|
+
// Prevent mutation
|
|
245
|
+
three.array.push(4)
|
|
246
|
+
expect(two.array).toHaveLength(3)
|
|
247
|
+
expect(three.array).toHaveLength(4)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('concatMix: nested objects with concatenated arrays', () => {
|
|
251
|
+
const one = {
|
|
252
|
+
nested: {
|
|
253
|
+
items: [1, 2],
|
|
254
|
+
other: 'value1',
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
const two = {
|
|
258
|
+
nested: {
|
|
259
|
+
items: [3, 4],
|
|
260
|
+
other: 'value2',
|
|
261
|
+
},
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const three = concatMix(one, two)
|
|
265
|
+
expect(three.nested.items).toEqual([1, 2, 3, 4])
|
|
266
|
+
expect(three.nested.other).toBe('value2')
|
|
267
|
+
})
|
|
268
|
+
|
|
220
269
|
it('Functions (cannot be cloned)', () => {
|
|
221
270
|
const one = {}
|
|
222
271
|
const two = {
|
package/src/node/file-cache.js
CHANGED