-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
212 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,231 @@ | ||
// @ts-check | ||
import { test } from 'uvu'; | ||
import { suite } from 'uvu'; | ||
import * as assert from 'uvu/assert'; | ||
import dset from '../dist/dset'; | ||
|
||
test('dset', () => { | ||
assert.type(dset, 'function', 'exports a function'); | ||
const API = suite('API'); | ||
|
||
let foo = { a:1, b:2 }; | ||
let out = dset(foo, 'c', 3); // add c | ||
assert.is(out, undefined, 'does not return output'); | ||
assert.equal(foo, { a:1, b:2, c:3 }, 'mutates; adds simple key:val'); | ||
API('should export a function', () => { | ||
assert.type(dset, 'function'); | ||
}); | ||
|
||
API.run(); | ||
|
||
// --- | ||
|
||
const usage = suite('usage'); | ||
|
||
usage('should not give return value', () => { | ||
let output = dset({}, 'c', 3); // add c | ||
assert.is(output, undefined); | ||
}); | ||
|
||
usage('should mutate original object', () => { | ||
let item = { foo: 1 }; | ||
dset(item, 'bar', 123); | ||
assert.ok(item === item); | ||
assert.equal(item, { | ||
foo: 1, | ||
bar: 123 | ||
}); | ||
}); | ||
|
||
usage.run(); | ||
|
||
// --- | ||
|
||
const keys = suite('keys'); | ||
|
||
keys('should add value to key path :: shallow :: string', () => { | ||
let input = {}; | ||
dset(input, 'abc', 123); | ||
assert.equal(input, { abc: 123 }); | ||
}); | ||
|
||
keys('should add value to key path :: shallow :: array', () => { | ||
let input = {}; | ||
dset(input, ['abc'], 123); | ||
assert.equal(input, { abc: 123 }); | ||
}); | ||
|
||
keys('should add value to key path :: nested :: string', () => { | ||
let input = {}; | ||
dset(input, 'a.b.c', 123); | ||
assert.equal(input, { | ||
a: { | ||
b: { | ||
c: 123 | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
keys('should add value to key path :: nested :: array', () => { | ||
let input = {}; | ||
dset(input, ['a', 'b', 'c'], 123); | ||
assert.equal(input, { | ||
a: { | ||
b: { | ||
c: 123 | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
keys.run(); | ||
|
||
foo = {}; | ||
dset(foo, 'a.b.c', 999); // add deep | ||
assert.equal(foo, { a:{ b:{ c:999 } } }, 'mutates; adds deeply nested key:val'); | ||
// --- | ||
|
||
foo = {}; | ||
dset(foo, ['a', 'b', 'c'], 123); // change via array | ||
assert.equal(foo, { a:{ b:{ c:123 } } }, 'mutates; changes the value via array-type keys'); | ||
const arrays = suite('arrays'); | ||
|
||
arrays('should create array instead of object via numeric key :: simple', () => { | ||
let input = { a: 1 }; | ||
dset(input, 'e.0', 2); | ||
assert.instance(input.e, Array); | ||
assert.is(input.e[0], 2); | ||
assert.equal(input, { | ||
a: 1, | ||
e: [2] | ||
}); | ||
}); | ||
|
||
arrays('should create array instead of object via numeric key :: nested', () => { | ||
let input = { a: 1 }; | ||
dset(input, 'e.0.0', 123); | ||
assert.instance(input.e, Array); | ||
assert.is(input.e[0][0], 123); | ||
assert.equal(input, { | ||
a: 1, | ||
e: [ [123] ] | ||
}); | ||
}); | ||
|
||
foo = { a:1 }; | ||
dset(foo, 'e.0.0', 2); // create arrays instead of objects | ||
assert.is(foo.e[0][0], 2, 'mutates; can create arrays when key is numeric'); | ||
assert.equal(foo, { a: 1, e:[[2]] }); | ||
assert.instance(foo.e, Array); | ||
arrays('should be able to create object inside of array', () => { | ||
let input = {}; | ||
dset(input, ['x', '0', 'z'], 123); | ||
assert.instance(input.x, Array); | ||
assert.equal(input, { | ||
x: [{ z:123 }] | ||
}); | ||
}); | ||
|
||
foo = { a:{ b:{ c:123 } } }; | ||
dset(foo, 'a.b.x.y', 456); // preserve existing structure | ||
assert.equal(foo, { a:{ b:{ c:123, x:{ y:456 } }} }, 'mutates; writes into/preserves existing object'); | ||
arrays('should create arrays with hole(s) if needed', () => { | ||
let input = {}; | ||
dset(input, ['x', '1', 'z'], 123); | ||
assert.instance(input.x, Array); | ||
assert.equal(input, { | ||
x: [, { z:123 }] | ||
}); | ||
}); | ||
|
||
foo = { a: { b:123 } }; | ||
dset(foo, 'a.b.c', 'hello'); // preserve non-object value, won't alter | ||
assert.is(foo.a.b, 123, 'refuses to convert existing non-object value into object'); | ||
assert.equal(foo, { a: { b:123 }}); | ||
arrays('should create object from decimal-like key :: array :: zero', () => { | ||
let input = {}; | ||
dset(input, ['x', '10.0', 'z'], 123); | ||
assert.not.instance(input.x, Array); | ||
assert.equal(input, { | ||
x: { | ||
'10.0': { | ||
z: 123 | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
foo = { a:{ b:{ c:123, d:{ e:5 } } } }; | ||
dset(foo, 'a.b.d.z', [1,2,3,4]); // preserve object tree, with array value | ||
assert.equal(foo.a.b.d, { e:5, z:[1,2,3,4] }, 'mutates; writes into existing object w/ array value'); | ||
arrays('should create object from decimal-like key :: array :: nonzero', () => { | ||
let input = {}; | ||
dset(input, ['x', '10.2', 'z'], 123); | ||
assert.not.instance(input.x, Array); | ||
assert.equal(input, { | ||
x: { | ||
'10.2': { | ||
z: 123 | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
foo = { b:123 }; | ||
assert.not.throws(_ => dset(foo, 'b.c.d.e', 123), 'silently preserves existing non-null value'); | ||
assert.is(foo.b, 123, 'preserves existing value'); | ||
arrays.run(); | ||
|
||
// --- | ||
|
||
const preserves = suite('preserves'); | ||
|
||
preserves('should preserve existing object structure', () => { | ||
let input = { | ||
a: { | ||
b: { | ||
c: 123 | ||
} | ||
} | ||
}; | ||
|
||
dset(input, 'a.b.x.y', 456); | ||
|
||
assert.equal(input, { | ||
a: { | ||
b: { | ||
c: 123, | ||
x: { | ||
y:456 | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
foo = { b:0 }; | ||
assert.not.throws(_ => dset(foo, 'b.a.s.d', 123), 'silently preserves `0` as existing non-null value'); | ||
assert.equal(foo, { b:0 }, 'preserves existing object values'); | ||
preserves('should not convert existing non-object values into object', () => { | ||
let input = { | ||
a: { | ||
b: 123 | ||
} | ||
}; | ||
|
||
foo = {}; | ||
dset(foo, ['x', 'y', 'z'], 123); | ||
assert.equal(foo, { x:{ y:{ z:123 } } }); | ||
let before = JSON.stringify(input); | ||
dset(input, 'a.b.c', 'hello'); | ||
|
||
foo = {}; | ||
dset(foo, ['x', '0', 'z'], 123); | ||
assert.equal(foo, { x:[{ z:123 }] }); | ||
assert.instance(foo.x, Array); | ||
assert.is( | ||
JSON.stringify(input), | ||
before | ||
); | ||
}); | ||
|
||
foo = {}; | ||
dset(foo, ['x', '1', 'z'], 123); | ||
assert.equal(foo, { x:[,{ z:123 }] }); | ||
assert.instance(foo.x, Array); | ||
preserves('should preserve existing object tree w/ array value', () => { | ||
let input = { | ||
a: { | ||
b: { | ||
c: 123, | ||
d: { | ||
e: 5 | ||
} | ||
} | ||
} | ||
}; | ||
|
||
dset(input, 'a.b.d.z', [1,2,3,4]); | ||
|
||
assert.equal(input.a.b.d, { | ||
e: 5, | ||
z: [1,2,3,4] | ||
}); | ||
}); | ||
|
||
foo = {}; | ||
dset(foo, ['x', '10.0', 'z'], 123); | ||
assert.equal(foo, { x:{ '10.0':{ z:123 } } }); | ||
assert.not.instance(foo.x, Array); | ||
preserves('should not throw when refusing to convert non-object into object', () => { | ||
try { | ||
let input = { b:123 }; | ||
dset(input, 'b.c.d.e', 123); | ||
assert.is(input.b, 123); | ||
} catch (err) { | ||
assert.unreachable('should not have thrown'); | ||
} | ||
}); | ||
|
||
foo = {}; | ||
dset(foo, ['x', '10.2', 'z'], 123); | ||
assert.equal(foo, { x:{ '10.2':{ z:123 } } }); | ||
assert.not.instance(foo.x, Array); | ||
preserves('should not throw when refusing to convert `0` into object', () => { | ||
try { | ||
let input = { b:0 }; | ||
dset(input, 'b.a.s.d', 123); | ||
assert.equal(input, { b: 0 }); | ||
} catch (err) { | ||
assert.unreachable('should not have thrown'); | ||
} | ||
}); | ||
|
||
test.run(); | ||
preserves.run(); |