@inglorious/utils 3.7.0 → 3.7.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/README.md +86 -86
- package/package.json +8 -7
- package/src/data-structures/object.js +1 -1
- package/src/algorithms/decision-tree.test.js +0 -154
- package/src/algorithms/path-finding.test.js +0 -151
- package/src/data-structures/array.test.js +0 -206
- package/src/data-structures/board.test.js +0 -242
- package/src/data-structures/heap.test.js +0 -103
- package/src/data-structures/object.test.js +0 -393
- package/src/data-structures/objects.test.js +0 -196
- package/src/data-structures/set.test.js +0 -15
- package/src/data-structures/tree.test.js +0 -33
- package/src/functions/function.test.js +0 -15
- package/src/functions/functions.test.js +0 -51
- package/src/math/circle.test.js +0 -98
- package/src/math/hitmask.test.js +0 -156
- package/src/math/line.test.js +0 -50
- package/src/math/numbers.test.js +0 -198
- package/src/math/point.test.js +0 -94
- package/src/math/quaternion.test.js +0 -25
- package/src/math/quaternions.test.js +0 -29
- package/src/math/rectangle.test.js +0 -43
- package/src/math/rng.test.js +0 -55
- package/src/math/segment.test.js +0 -184
- package/src/math/statistics.test.js +0 -47
- package/src/math/triangle.test.js +0 -11
- package/src/math/trigonometry.test.js +0 -52
- package/src/math/vector.test.js +0 -345
- package/src/math/vectors.test.js +0 -77
- package/src/physics/friction.test.js +0 -43
- package/src/physics/gravity.test.js +0 -78
- package/src/v.test.js +0 -48
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { v } from "../v.js"
|
|
4
|
-
import {
|
|
5
|
-
clone,
|
|
6
|
-
deserialize,
|
|
7
|
-
filter,
|
|
8
|
-
find,
|
|
9
|
-
get,
|
|
10
|
-
isObject,
|
|
11
|
-
map,
|
|
12
|
-
produce,
|
|
13
|
-
serialize,
|
|
14
|
-
set,
|
|
15
|
-
toString,
|
|
16
|
-
} from "./object.js"
|
|
17
|
-
|
|
18
|
-
test("it should deep clone an object", () => {
|
|
19
|
-
const obj = {
|
|
20
|
-
primitive: 1,
|
|
21
|
-
array: [2, 3],
|
|
22
|
-
object: { a: 1, b: 2 },
|
|
23
|
-
}
|
|
24
|
-
const expectedResult = {
|
|
25
|
-
primitive: 1,
|
|
26
|
-
array: [2, 3],
|
|
27
|
-
object: { a: 1, b: 2 },
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const result = clone(obj)
|
|
31
|
-
|
|
32
|
-
expect(result).toStrictEqual(expectedResult)
|
|
33
|
-
expect(result).not.toBe(expectedResult)
|
|
34
|
-
expect(result.primitive).toBe(expectedResult.primitive)
|
|
35
|
-
expect(result.array).not.toBe(expectedResult.array)
|
|
36
|
-
expect(result.object).not.toBe(expectedResult.object)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test("it should behave like Array.prototype.filter, but on an object", () => {
|
|
40
|
-
const obj = {
|
|
41
|
-
key1: "value1",
|
|
42
|
-
key2: "value2",
|
|
43
|
-
key3: "value3",
|
|
44
|
-
}
|
|
45
|
-
const callback = (key) => ["key2", "key3"].includes(key)
|
|
46
|
-
const expectedResult = {
|
|
47
|
-
key2: "value2",
|
|
48
|
-
key3: "value3",
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
expect(filter(obj, callback)).toStrictEqual(expectedResult)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test("it should behave like Array.prototype.find, but on an object", () => {
|
|
55
|
-
const obj = {
|
|
56
|
-
key1: "value1",
|
|
57
|
-
key2: "value2",
|
|
58
|
-
key3: "value3",
|
|
59
|
-
}
|
|
60
|
-
const callback = (key) => ["key2", "key3"].includes(key)
|
|
61
|
-
const expectedResult = {
|
|
62
|
-
key2: "value2",
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
expect(find(obj, callback)).toStrictEqual(expectedResult)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
test("get should retrieve a value from a nested object", () => {
|
|
69
|
-
const obj = { a: { b: { c: 1 } }, d: [{ e: 2 }] }
|
|
70
|
-
|
|
71
|
-
expect(get(obj, "a.b.c")).toBe(1)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test("get should retrieve a value from an array within an object", () => {
|
|
75
|
-
const obj = { a: { b: { c: 1 } }, d: [{ e: 2 }] }
|
|
76
|
-
|
|
77
|
-
expect(get(obj, "d.0.e")).toBe(2)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
test("get should return a default value for a non-existent path", () => {
|
|
81
|
-
const obj = { a: { b: { c: 1 } } }
|
|
82
|
-
|
|
83
|
-
expect(get(obj, "a.x.y", "default")).toBe("default")
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
test("get should return undefined for a non-existent path without a default value", () => {
|
|
87
|
-
const obj = { a: { b: { c: 1 } } }
|
|
88
|
-
|
|
89
|
-
expect(get(obj, "a.x.y")).toBeUndefined()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test("get should handle null or undefined paths gracefully", () => {
|
|
93
|
-
const obj = { a: 1 }
|
|
94
|
-
|
|
95
|
-
expect(get(obj, null, "default")).toBe("default")
|
|
96
|
-
expect(get(obj, undefined, "default")).toBe("default")
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test("set should modify a value at a given path", () => {
|
|
100
|
-
const obj = { a: { b: 1 } }
|
|
101
|
-
set(obj, "a.b", 2)
|
|
102
|
-
expect(obj.a.b).toBe(2)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test("set should create nested objects if they do not exist", () => {
|
|
106
|
-
const obj = {}
|
|
107
|
-
set(obj, "a.b.c", 3)
|
|
108
|
-
expect(obj).toStrictEqual({ a: { b: { c: 3 } } })
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
test("set should create nested arrays for numeric keys", () => {
|
|
112
|
-
const obj = {}
|
|
113
|
-
set(obj, "a.0.b", "value")
|
|
114
|
-
expect(obj).toStrictEqual({ a: [{ b: "value" }] })
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test("set should return the mutated object", () => {
|
|
118
|
-
const obj = { a: 1 }
|
|
119
|
-
const result = set(obj, "b", 2)
|
|
120
|
-
expect(result).toBe(obj)
|
|
121
|
-
expect(result).toStrictEqual({ a: 1, b: 2 })
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
test("it correctly should check if a value is an object", () => {
|
|
125
|
-
expect(isObject(1)).toBe(false)
|
|
126
|
-
expect(isObject("a")).toBe(false)
|
|
127
|
-
expect(isObject([])).toBe(false)
|
|
128
|
-
expect(isObject(null)).toBe(false)
|
|
129
|
-
expect(isObject(new Date())).toBe(false)
|
|
130
|
-
expect(isObject({})).toBe(true)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test("it should behave like Array.prototype.map, but on an object", () => {
|
|
134
|
-
const obj = {
|
|
135
|
-
key1: "value1",
|
|
136
|
-
key2: "value2",
|
|
137
|
-
key3: "value3",
|
|
138
|
-
}
|
|
139
|
-
const callback = (key, value) => value.toUpperCase()
|
|
140
|
-
const expectedResult = {
|
|
141
|
-
key1: "VALUE1",
|
|
142
|
-
key2: "VALUE2",
|
|
143
|
-
key3: "VALUE3",
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
expect(map(obj, callback)).toStrictEqual(expectedResult)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
test("it should produce a new state without mutating the original", () => {
|
|
150
|
-
const baseState = {
|
|
151
|
-
a: 1,
|
|
152
|
-
b: {
|
|
153
|
-
c: [2, 3],
|
|
154
|
-
d: { e: 4 },
|
|
155
|
-
},
|
|
156
|
-
f: 5,
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const recipe = (draft) => {
|
|
160
|
-
draft.a = 10
|
|
161
|
-
draft.b.c.push(4)
|
|
162
|
-
draft.b.d.e = 40
|
|
163
|
-
draft.g = 6
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const expectedState = {
|
|
167
|
-
a: 10,
|
|
168
|
-
b: {
|
|
169
|
-
c: [2, 3, 4],
|
|
170
|
-
d: { e: 40 },
|
|
171
|
-
},
|
|
172
|
-
f: 5,
|
|
173
|
-
g: 6,
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const originalBaseState = deserialize(serialize(baseState))
|
|
177
|
-
|
|
178
|
-
const nextState = produce(baseState, recipe)
|
|
179
|
-
|
|
180
|
-
expect(nextState).toStrictEqual(expectedState)
|
|
181
|
-
expect(nextState).not.toBe(baseState)
|
|
182
|
-
expect(baseState).toStrictEqual(originalBaseState)
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
test("it should support currying to produce a new state", () => {
|
|
186
|
-
const baseState = {
|
|
187
|
-
a: 1,
|
|
188
|
-
b: {
|
|
189
|
-
c: [2, 3],
|
|
190
|
-
d: { e: 4 },
|
|
191
|
-
},
|
|
192
|
-
f: 5,
|
|
193
|
-
}
|
|
194
|
-
const originalStateCopy = clone(baseState)
|
|
195
|
-
|
|
196
|
-
const recipe = (draft) => {
|
|
197
|
-
draft.a = 10
|
|
198
|
-
draft.b.c.push(4)
|
|
199
|
-
draft.g = 6
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const expectedState = {
|
|
203
|
-
a: 10,
|
|
204
|
-
b: {
|
|
205
|
-
c: [2, 3, 4],
|
|
206
|
-
d: { e: 4 },
|
|
207
|
-
},
|
|
208
|
-
f: 5,
|
|
209
|
-
g: 6,
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const producer = produce(recipe)
|
|
213
|
-
const nextState = producer(baseState)
|
|
214
|
-
|
|
215
|
-
expect(nextState).toStrictEqual(expectedState)
|
|
216
|
-
expect(nextState).not.toBe(baseState)
|
|
217
|
-
expect(baseState).toStrictEqual(originalStateCopy)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
test("it should pass extra arguments to the recipe", () => {
|
|
221
|
-
const baseState = { value: 1 }
|
|
222
|
-
const recipe = (draft, increment, multiplier) => {
|
|
223
|
-
draft.value = (draft.value + increment) * multiplier
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Test non-curried version
|
|
227
|
-
const nextStateUncurried = produce(baseState, recipe, 2, 3) // (1 + 2) * 3 = 9
|
|
228
|
-
expect(nextStateUncurried.value).toBe(9)
|
|
229
|
-
|
|
230
|
-
// Test curried version
|
|
231
|
-
const producer = produce(recipe)
|
|
232
|
-
const nextStateCurried = producer(baseState, 4, 5) // (1 + 4) * 5 = 25
|
|
233
|
-
expect(nextStateCurried.value).toBe(25)
|
|
234
|
-
|
|
235
|
-
expect(baseState.value).toBe(1)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
test("it should return a string representation of a shallow object", () => {
|
|
239
|
-
const obj = {
|
|
240
|
-
key1: "value1",
|
|
241
|
-
key2: "value2",
|
|
242
|
-
key3: "value3",
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
expect(toString(obj)).toBe(`{
|
|
246
|
-
key1: "value1",
|
|
247
|
-
key2: "value2",
|
|
248
|
-
key3: "value3"
|
|
249
|
-
}`)
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
test("it should return a string representation of a nested object", () => {
|
|
253
|
-
const obj = {
|
|
254
|
-
a: 1,
|
|
255
|
-
b: [7, 3],
|
|
256
|
-
c: { d: 4, h: 8 },
|
|
257
|
-
e: [{ f: 5, i: 9 }],
|
|
258
|
-
g: 6,
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
expect(toString(obj)).toBe(`{
|
|
262
|
-
a: 1,
|
|
263
|
-
b: [
|
|
264
|
-
7,
|
|
265
|
-
3
|
|
266
|
-
],
|
|
267
|
-
c: {
|
|
268
|
-
d: 4,
|
|
269
|
-
h: 8
|
|
270
|
-
},
|
|
271
|
-
e: [
|
|
272
|
-
{
|
|
273
|
-
f: 5,
|
|
274
|
-
i: 9
|
|
275
|
-
}
|
|
276
|
-
],
|
|
277
|
-
g: 6
|
|
278
|
-
}`)
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
test("it should serialize a basic object with primitive values", () => {
|
|
282
|
-
const obj = { a: 1, b: "hello", c: true }
|
|
283
|
-
const expected = `{"a":1,"b":"hello","c":true}`
|
|
284
|
-
|
|
285
|
-
expect(serialize(obj)).toBe(expected)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
test("it should serialize an object with nested objects", () => {
|
|
289
|
-
const obj = { a: 1, b: { c: "nested", d: { e: false } } }
|
|
290
|
-
const expected = `{"a":1,"b":{"c":"nested","d":{"e":false}}}`
|
|
291
|
-
|
|
292
|
-
expect(serialize(obj)).toBe(expected)
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
test("it should serialize an object containing a vector-like object", () => {
|
|
296
|
-
const obj = { position: v(1, 2, 3) }
|
|
297
|
-
const expected = `{"position":{"_type":"vector","coords":[1,2,3]}}`
|
|
298
|
-
|
|
299
|
-
expect(serialize(obj)).toBe(expected)
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
test("it should serialize an object with nested vector-like objects", () => {
|
|
303
|
-
const obj = {
|
|
304
|
-
entity: {
|
|
305
|
-
id: "player",
|
|
306
|
-
pos: v(10, 20),
|
|
307
|
-
vel: v(1, 0),
|
|
308
|
-
stats: { health: 100 },
|
|
309
|
-
},
|
|
310
|
-
}
|
|
311
|
-
const expected = `{"entity":{"id":"player","pos":{"_type":"vector","coords":[10,20]},"vel":{"_type":"vector","coords":[1,0]},"stats":{"health":100}}}`
|
|
312
|
-
|
|
313
|
-
expect(serialize(obj)).toBe(expected)
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
test("it should serialize an empty object", () => {
|
|
317
|
-
const obj = {}
|
|
318
|
-
const expected = `{}`
|
|
319
|
-
|
|
320
|
-
expect(serialize(obj)).toBe(expected)
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
test("it should serialize an object with null and undefined values (undefined should be omitted)", () => {
|
|
324
|
-
const obj = { a: null, b: undefined, c: 1 }
|
|
325
|
-
const expected = `{"a":null,"c":1}` // JSON.stringify omits undefined properties
|
|
326
|
-
|
|
327
|
-
expect(serialize(obj)).toBe(expected)
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
test("it should deserialize a basic object with primitive values", () => {
|
|
331
|
-
const serialized = `{"a":1,"b":"hello","c":true}`
|
|
332
|
-
const expected = { a: 1, b: "hello", c: true }
|
|
333
|
-
|
|
334
|
-
expect(deserialize(serialized)).toStrictEqual(expected)
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
test("it should deserialize an object with nested objects", () => {
|
|
338
|
-
const serialized = `{"a":1,"b":{"c":"nested","d":{"e":false}}}`
|
|
339
|
-
const expected = { a: 1, b: { c: "nested", d: { e: false } } }
|
|
340
|
-
|
|
341
|
-
expect(deserialize(serialized)).toStrictEqual(expected)
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
test("it should deserialize an object containing a vector representation", () => {
|
|
345
|
-
const serialized = `{"position":{"_type":"vector","coords":[1,2,3]}}`
|
|
346
|
-
const expected = { position: v(1, 2, 3) }
|
|
347
|
-
|
|
348
|
-
const result = deserialize(serialized)
|
|
349
|
-
|
|
350
|
-
expect(result).toStrictEqual(expected)
|
|
351
|
-
expect(result.position.__isVector__).toBe(true)
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
test("it should deserialize an object with nested vector representations", () => {
|
|
355
|
-
const serialized = `{"entity":{"id":"player","pos":{"_type":"vector","coords":[10,20]},"vel":{"_type":"vector","coords":[1,0]},"stats":{"health":100}}}`
|
|
356
|
-
const expected = {
|
|
357
|
-
entity: {
|
|
358
|
-
id: "player",
|
|
359
|
-
pos: v(10, 20),
|
|
360
|
-
vel: v(1, 0),
|
|
361
|
-
stats: { health: 100 },
|
|
362
|
-
},
|
|
363
|
-
}
|
|
364
|
-
const result = deserialize(serialized)
|
|
365
|
-
|
|
366
|
-
expect(result).toStrictEqual(expected)
|
|
367
|
-
expect(result.entity.pos.__isVector__).toBe(true)
|
|
368
|
-
expect(result.entity.vel.__isVector__).toBe(true)
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
test("it should deserialize an empty object", () => {
|
|
372
|
-
const serialized = `{}`
|
|
373
|
-
const expected = {}
|
|
374
|
-
|
|
375
|
-
expect(deserialize(serialized)).toStrictEqual(expected)
|
|
376
|
-
})
|
|
377
|
-
|
|
378
|
-
test("it should correctly round-trip a complex object through serialize and deserialize", () => {
|
|
379
|
-
const original = {
|
|
380
|
-
id: "game1",
|
|
381
|
-
player: { name: "Alice", score: 100, position: v(10, 20) },
|
|
382
|
-
enemies: [{ type: "goblin", pos: v(5, 5) }],
|
|
383
|
-
settings: { volume: 0.8, mute: false },
|
|
384
|
-
}
|
|
385
|
-
const jsonString = serialize(original)
|
|
386
|
-
const deserialized = deserialize(jsonString)
|
|
387
|
-
|
|
388
|
-
expect(deserialized).toStrictEqual(original)
|
|
389
|
-
expect(deserialized.player.position.__isVector__).toBe(true)
|
|
390
|
-
expect(deserialized.enemies[0].pos.__isVector__).toBe(true)
|
|
391
|
-
// Ensure that non-vector objects are still distinct references if they were originally
|
|
392
|
-
expect(deserialized.player).not.toBe(original.player)
|
|
393
|
-
})
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { isArray } from "./array.js"
|
|
4
|
-
import { extend, extendWith, merge, mergeWith } from "./objects.js"
|
|
5
|
-
|
|
6
|
-
test("it should extend an object with another, producing a new object as a result", () => {
|
|
7
|
-
const obj1 = {
|
|
8
|
-
primitiveKept: 1,
|
|
9
|
-
primitiveMerged: 2,
|
|
10
|
-
primitiveArrayKept: [1, 2],
|
|
11
|
-
primitiveArrayMerged: [3, 4],
|
|
12
|
-
primitiveObjectKept: { a: 1 },
|
|
13
|
-
primitiveObjectMerged: { b: 2, c: 3 },
|
|
14
|
-
nestedArrayKept: [{ a: 1 }],
|
|
15
|
-
nestedArrayMerged: [{ b: 2, c: 3 }],
|
|
16
|
-
nestedObjectKept: { a: { b: 2 } },
|
|
17
|
-
nestedObjectMerged: { c: { d: 4 } },
|
|
18
|
-
}
|
|
19
|
-
const obj2 = {
|
|
20
|
-
primitiveMerged: 3,
|
|
21
|
-
primitiveAdded: 4,
|
|
22
|
-
primitiveArrayMerged: [5],
|
|
23
|
-
primitiveArrayAdded: [6, 7],
|
|
24
|
-
primitiveObjectMerged: { c: 4 },
|
|
25
|
-
primitiveObjectAdded: { d: 4 },
|
|
26
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
27
|
-
nestedArrayAdded: [{ e: 5 }],
|
|
28
|
-
nestedObjectMerged: { c: { e: 5 } },
|
|
29
|
-
nestedObjectAdded: { f: { g: 7 } },
|
|
30
|
-
}
|
|
31
|
-
const expectedResult = {
|
|
32
|
-
primitiveKept: 1,
|
|
33
|
-
primitiveMerged: 3,
|
|
34
|
-
primitiveAdded: 4,
|
|
35
|
-
primitiveArrayKept: [1, 2],
|
|
36
|
-
primitiveArrayMerged: [5],
|
|
37
|
-
primitiveArrayAdded: [6, 7],
|
|
38
|
-
primitiveObjectKept: { a: 1 },
|
|
39
|
-
primitiveObjectMerged: { b: 2, c: 4 },
|
|
40
|
-
primitiveObjectAdded: { d: 4 },
|
|
41
|
-
nestedArrayKept: [{ a: 1 }],
|
|
42
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
43
|
-
nestedArrayAdded: [{ e: 5 }],
|
|
44
|
-
nestedObjectKept: { a: { b: 2 } },
|
|
45
|
-
nestedObjectMerged: { c: { d: 4, e: 5 } },
|
|
46
|
-
nestedObjectAdded: { f: { g: 7 } },
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const result = extend(obj1, obj2)
|
|
50
|
-
expect(result).toStrictEqual(expectedResult)
|
|
51
|
-
expect(result).not.toBe(obj1)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test("it should deep merge an two objects, changing the first object in place", () => {
|
|
55
|
-
const obj1 = {
|
|
56
|
-
primitiveKept: 1,
|
|
57
|
-
primitiveMerged: 2,
|
|
58
|
-
primitiveArrayKept: [1, 2],
|
|
59
|
-
primitiveArrayMerged: [3, 4],
|
|
60
|
-
primitiveObjectKept: { a: 1 },
|
|
61
|
-
primitiveObjectMerged: { b: 2, c: 3 },
|
|
62
|
-
nestedArrayKept: [{ a: 1 }],
|
|
63
|
-
nestedArrayMerged: [{ b: 2, c: 3 }],
|
|
64
|
-
nestedObjectKept: { a: { b: 2 } },
|
|
65
|
-
nestedObjectMerged: { c: { d: 4 } },
|
|
66
|
-
}
|
|
67
|
-
const obj2 = {
|
|
68
|
-
primitiveMerged: 3,
|
|
69
|
-
primitiveAdded: 4,
|
|
70
|
-
primitiveArrayMerged: [5],
|
|
71
|
-
primitiveArrayAdded: [6, 7],
|
|
72
|
-
primitiveObjectMerged: { c: 4 },
|
|
73
|
-
primitiveObjectAdded: { d: 4 },
|
|
74
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
75
|
-
nestedArrayAdded: [{ e: 5 }],
|
|
76
|
-
nestedObjectMerged: { c: { e: 5 } },
|
|
77
|
-
nestedObjectAdded: { f: { g: 7 } },
|
|
78
|
-
}
|
|
79
|
-
const expectedResult = {
|
|
80
|
-
primitiveKept: 1,
|
|
81
|
-
primitiveMerged: 3,
|
|
82
|
-
primitiveAdded: 4,
|
|
83
|
-
primitiveArrayKept: [1, 2],
|
|
84
|
-
primitiveArrayMerged: [5],
|
|
85
|
-
primitiveArrayAdded: [6, 7],
|
|
86
|
-
primitiveObjectKept: { a: 1 },
|
|
87
|
-
primitiveObjectMerged: { b: 2, c: 4 },
|
|
88
|
-
primitiveObjectAdded: { d: 4 },
|
|
89
|
-
nestedArrayKept: [{ a: 1 }],
|
|
90
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
91
|
-
nestedArrayAdded: [{ e: 5 }],
|
|
92
|
-
nestedObjectKept: { a: { b: 2 } },
|
|
93
|
-
nestedObjectMerged: { c: { d: 4, e: 5 } },
|
|
94
|
-
nestedObjectAdded: { f: { g: 7 } },
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const result = merge(obj1, obj2)
|
|
98
|
-
expect(result).toStrictEqual(expectedResult)
|
|
99
|
-
expect(result).toBe(obj1)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test("it should extend an object with a custom merger, producing a new object", () => {
|
|
103
|
-
const obj1 = {
|
|
104
|
-
primitiveArrayMerged: [3, 4],
|
|
105
|
-
nestedArrayMerged: [{ b: 2, c: 3 }],
|
|
106
|
-
nestedObjectMerged: { c: { d: 4 } },
|
|
107
|
-
}
|
|
108
|
-
const obj2 = {
|
|
109
|
-
primitiveArrayMerged: [5],
|
|
110
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
111
|
-
nestedObjectMerged: { c: { e: 5 } },
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const merger = (targetValue, sourceValue) => {
|
|
115
|
-
if (isArray(targetValue) && isArray(sourceValue)) {
|
|
116
|
-
return targetValue.concat(sourceValue)
|
|
117
|
-
}
|
|
118
|
-
return undefined // Use default logic for non-arrays
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const expectedResult = {
|
|
122
|
-
primitiveArrayMerged: [3, 4, 5], // Concatenated
|
|
123
|
-
nestedArrayMerged: [{ b: 2, c: 3 }, { d: 4 }], // Concatenated
|
|
124
|
-
nestedObjectMerged: { c: { d: 4, e: 5 } }, // Default deep merge
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const result = extendWith(merger, obj1, obj2)
|
|
128
|
-
expect(result).toStrictEqual(expectedResult)
|
|
129
|
-
expect(result).not.toBe(obj1)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
test("it should merge an object with a custom merger, changing the first object in place", () => {
|
|
133
|
-
const obj1 = {
|
|
134
|
-
primitiveArrayMerged: [3, 4],
|
|
135
|
-
nestedArrayMerged: [{ b: 2, c: 3 }],
|
|
136
|
-
nestedObjectMerged: { c: { d: 4 } },
|
|
137
|
-
}
|
|
138
|
-
const obj2 = {
|
|
139
|
-
primitiveArrayMerged: [5],
|
|
140
|
-
nestedArrayMerged: [{ d: 4 }],
|
|
141
|
-
nestedObjectMerged: { c: { e: 5 } },
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const merger = (targetValue, sourceValue) => {
|
|
145
|
-
if (isArray(targetValue) && isArray(sourceValue)) {
|
|
146
|
-
return targetValue.concat(sourceValue)
|
|
147
|
-
}
|
|
148
|
-
return undefined // Use default logic for non-arrays
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const expectedResult = {
|
|
152
|
-
primitiveArrayMerged: [3, 4, 5], // Concatenated
|
|
153
|
-
nestedArrayMerged: [{ b: 2, c: 3 }, { d: 4 }], // Concatenated
|
|
154
|
-
nestedObjectMerged: { c: { d: 4, e: 5 } }, // Default deep merge
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const result = mergeWith(merger, obj1, obj2)
|
|
158
|
-
expect(result).toStrictEqual(expectedResult)
|
|
159
|
-
expect(result).toBe(obj1)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
test("extendWith without a merger should behave like extend", () => {
|
|
163
|
-
const obj1 = { a: 1, b: { c: 2 }, d: [3] }
|
|
164
|
-
const obj2 = { a: 10, b: { d: 20 }, d: [30] }
|
|
165
|
-
|
|
166
|
-
const expected = {
|
|
167
|
-
a: 10,
|
|
168
|
-
b: { c: 2, d: 20 },
|
|
169
|
-
d: [30],
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const result = extendWith(undefined, obj1, obj2)
|
|
173
|
-
expect(result).toStrictEqual(expected)
|
|
174
|
-
expect(result).not.toBe(obj1)
|
|
175
|
-
|
|
176
|
-
const extendResult = extend(obj1, obj2)
|
|
177
|
-
expect(result).toStrictEqual(extendResult)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
test("mergeWith without a merger should behave like merge", () => {
|
|
181
|
-
const obj1 = { a: 1, b: { c: 2 }, d: [3] }
|
|
182
|
-
const obj2 = { a: 10, b: { d: 20 }, d: [30] }
|
|
183
|
-
|
|
184
|
-
const expected = {
|
|
185
|
-
a: 10,
|
|
186
|
-
b: { c: 2, d: 20 },
|
|
187
|
-
d: [30],
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const result = mergeWith(undefined, obj1, obj2)
|
|
191
|
-
expect(result).toStrictEqual(expected)
|
|
192
|
-
expect(result).toBe(obj1)
|
|
193
|
-
|
|
194
|
-
const mergeResult = merge({ a: 1, b: { c: 2 }, d: [3] }, obj2)
|
|
195
|
-
expect(result).toStrictEqual(mergeResult)
|
|
196
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { ensureSet } from "./set.js"
|
|
4
|
-
|
|
5
|
-
test("it should return the same array if the value is already an array", () => {
|
|
6
|
-
const set = new Set([1, "two", { three: 3 }])
|
|
7
|
-
|
|
8
|
-
expect(ensureSet(set)).toBe(set)
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
test("it should wrap the value in an array if it is not an array", () => {
|
|
12
|
-
const str = "hello"
|
|
13
|
-
|
|
14
|
-
expect(ensureSet(str)).toStrictEqual(new Set(["h", "e", "l", "o"]))
|
|
15
|
-
})
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { bfs, dfs } from "./tree.js"
|
|
4
|
-
|
|
5
|
-
test("it should perform Breadth-First Search on a tree", () => {
|
|
6
|
-
const tree = {
|
|
7
|
-
value: 1,
|
|
8
|
-
children: [
|
|
9
|
-
{ value: 2, children: [{ value: 4 }, { value: 5 }] },
|
|
10
|
-
{ value: 3, children: [{ value: 6 }, { value: 7 }] },
|
|
11
|
-
],
|
|
12
|
-
}
|
|
13
|
-
const expectedResult = [1, 2, 3, 4, 5, 6, 7]
|
|
14
|
-
|
|
15
|
-
const result = bfs(tree)
|
|
16
|
-
|
|
17
|
-
expect(result).toStrictEqual(expectedResult)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test("it should perform Depth-First Search on a tree", () => {
|
|
21
|
-
const tree = {
|
|
22
|
-
value: 1,
|
|
23
|
-
children: [
|
|
24
|
-
{ value: 2, children: [{ value: 3 }, { value: 4 }] },
|
|
25
|
-
{ value: 5, children: [{ value: 6 }, { value: 7 }] },
|
|
26
|
-
],
|
|
27
|
-
}
|
|
28
|
-
const expectedResult = [1, 2, 3, 4, 5, 6, 7]
|
|
29
|
-
|
|
30
|
-
const result = dfs(tree)
|
|
31
|
-
|
|
32
|
-
expect(result).toStrictEqual(expectedResult)
|
|
33
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { isFunction } from "./function.js"
|
|
4
|
-
|
|
5
|
-
test("it should correctly identify a function", () => {
|
|
6
|
-
const func = () => {}
|
|
7
|
-
|
|
8
|
-
expect(isFunction(func)).toBe(true)
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
test("it should correctly identify a non-function", () => {
|
|
12
|
-
const str = "hello"
|
|
13
|
-
|
|
14
|
-
expect(isFunction(str)).toBe(false)
|
|
15
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest"
|
|
2
|
-
|
|
3
|
-
import { compose, pipe } from "./functions.js"
|
|
4
|
-
|
|
5
|
-
test("it should compose unary functions", () => {
|
|
6
|
-
const shout = (x) => x.toUpperCase()
|
|
7
|
-
const punctuate = (mark) => (x) => `${x}${mark}`
|
|
8
|
-
const html = (tag) => (x) => `<${tag}>${x}</${tag}>`
|
|
9
|
-
|
|
10
|
-
const fn = compose(html("p"), punctuate("!"), shout)
|
|
11
|
-
expect(fn("Hello world")).toBe("<p>HELLO WORLD!</p>")
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test("it should compose functions with multiple initial arguments", () => {
|
|
15
|
-
const add = (a, b) => a + b
|
|
16
|
-
const square = (x) => x * x
|
|
17
|
-
const half = (x) => x / 2
|
|
18
|
-
|
|
19
|
-
const fn = compose(half, square, add) // half(square(add(2, 3)))
|
|
20
|
-
expect(fn(2, 3)).toBe(12.5)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test("it should return an identity-like function if compose is called with no arguments", () => {
|
|
24
|
-
const fn = compose()
|
|
25
|
-
expect(fn(42)).toBe(42)
|
|
26
|
-
expect(fn({ a: 1 })).toStrictEqual({ a: 1 })
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
test("it should pipe unary functions", () => {
|
|
30
|
-
const shout = (x) => x.toUpperCase()
|
|
31
|
-
const punctuate = (mark) => (x) => `${x}${mark}`
|
|
32
|
-
const html = (tag) => (x) => `<${tag}>${x}</${tag}>`
|
|
33
|
-
|
|
34
|
-
const fn = pipe(shout, punctuate("!"), html("p"))
|
|
35
|
-
expect(fn("Hello world")).toBe("<p>HELLO WORLD!</p>")
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test("it should pipe functions with multiple initial arguments", () => {
|
|
39
|
-
const add = (a, b) => a + b
|
|
40
|
-
const square = (x) => x * x
|
|
41
|
-
const half = (x) => x / 2
|
|
42
|
-
|
|
43
|
-
const fn = pipe(add, square, half) // half(square(add(2, 3)))
|
|
44
|
-
expect(fn(2, 3)).toBe(12.5)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test("it should return an identity-like function if pipe is called with no arguments", () => {
|
|
48
|
-
const fn = pipe()
|
|
49
|
-
expect(fn(42)).toBe(42)
|
|
50
|
-
expect(fn({ a: 1 })).toStrictEqual({ a: 1 })
|
|
51
|
-
})
|