@irrelon/path
Version:
A powerful JSON path processor. Allows you to drill into and manipulate JSON objects with a simple dot-delimited path format e.g. "obj.name".
1,862 lines (1,632 loc) • 104 kB
text/typescript
import assert from "node:assert";
import {
countLeafNodes,
diff,
diffValues,
down,
escape,
findOnePath,
findPath,
flatten,
flattenValues,
furthest,
get,
getMany,
isNotEqual,
join,
joinEscaped,
leafNodes,
match,
merge,
mergeImmutable,
PathData,
pop,
pullVal,
pushVal,
query,
set,
setImmutable,
shift,
splicePath,
split,
traverse,
type,
unSet,
unSetImmutable,
up,
update,
updateImmutable,
values
} from "../src/path";
describe("Path", () => {
describe("join()", () => {
it("Will join multiple paths together", () => {
const result = join("test1", "test2");
assert.strictEqual(result, "test1.test2", "Path is correct");
});
});
describe("joinEscape()", () => {
it("Will join multiple paths together escaped", () => {
const result = joinEscaped("test1.com", "test2.com");
assert.strictEqual(result, "test1\\.com.test2\\.com", "Path is correct");
});
});
describe("values()", () => {
it("Will traverse the tree and find all values for each part of the path", () => {
const obj = {
"obj": [{
"other": {}
}]
};
const result = values(obj, "obj.0.other.val.another");
assert.strictEqual(result.obj instanceof Array, true, "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0"], "object", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other"], "object", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other.val"], "undefined", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other.val.another"], "undefined", "The value was retrieved correctly");
});
it("Will handle infinite recursive structures", () => {
const obj: any = {
"obj": [{
"other": {}
}]
};
// Create an infinite recursion
obj.obj[0].other.obj = obj;
const result = values(obj, "obj.0.other.obj.0.other");
assert.strictEqual(result.obj instanceof Array, true, "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0"], "object", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other"], "object", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other.val"], "undefined", "The value was retrieved correctly");
assert.strictEqual(typeof result["obj.0.other.val.another"], "undefined", "The value was retrieved correctly");
});
});
describe("countLeafNodes()", () => {
it("Will count leaf nodes of an object", () => {
const obj = {
"obj": [{
"other": {
moo: "foo"
}
}]
};
const result = countLeafNodes(obj);
assert.strictEqual(result, 1, "The test value is correct");
});
it("Will count leaf nodes of an object with infinite recursive structure", () => {
const obj: any = {
"obj": [{
"other": {
moo: "foo"
}
}]
};
// Create an infinite recursion
obj.obj[0].other.obj = obj;
const result = countLeafNodes(obj);
assert.strictEqual(result, 2, "The test value is correct");
});
it("Will count null value leaf nodes of an object", () => {
const obj = {
"obj": [{
"other": {
"moo": "foo",
"bar": null
}
}]
};
const result = countLeafNodes(obj);
assert.strictEqual(result, 2, "The test value is correct");
});
});
describe("flatten()", () => {
it("Will flatten an object structure to array of keys", () => {
const obj = {
"obj": [{
"other": {
moo: "foo"
}
}]
};
const result = flatten(obj);
assert.ok(result instanceof Array, "The test type is correct");
assert.strictEqual(result.length, 4, "The array length is correct");
assert.ok(result.indexOf("obj") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0.other") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0.other.moo") > -1, "The test type is correct");
});
it("Will handle an infinite recursive structure", () => {
const obj: any = {
"obj": [{
"other": {
"moo": "foo"
}
}]
};
// Create an infinite recursion
obj.obj[0].other.obj = obj;
const result = flatten(obj);
assert.ok(result instanceof Array, "The test type is correct");
assert.strictEqual(result.length, 5, "The array length is correct");
assert.ok(result.indexOf("obj") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0.other") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0.other.moo") > -1, "The test type is correct");
assert.ok(result.indexOf("obj.0.other.obj") > -1, "The test type is correct");
});
});
describe("flattenValues()", () => {
it("Will flatten an object structure to keys and values", () => {
const obj = {
"obj": [{
"other": {
"moo": "foo"
}
}]
};
const result = flattenValues(obj);
assert.strictEqual(typeof result, "object", "The test type is correct");
assert.strictEqual(result["obj"] instanceof Array, true, "The test type is correct");
assert.strictEqual(typeof result["obj.0.other"], "object", "The test type is correct");
assert.strictEqual(typeof result["obj.0.other.moo"], "string", "The test type is correct");
assert.strictEqual(result["obj.0.other.moo"], "foo", "The test value is correct");
});
it("Will flatten an object structure with custom key transformer", () => {
const obj = {
"obj": [{
"other": {
"moo": "foo"
}
}]
};
const result = flattenValues(obj, undefined, "", {
transformKey: (key, info) => info.isArrayIndex ? "$" : key
});
assert.strictEqual(typeof result, "object", "The test type is correct");
assert.strictEqual(result["obj"] instanceof Array, true, "The test type is correct");
assert.strictEqual(typeof result["obj.$.other"], "object", "The test type is correct");
assert.strictEqual(typeof result["obj.$.other.moo"], "string", "The test type is correct");
assert.strictEqual(result["obj.$.other.moo"], "foo", "The test value is correct");
});
it("Will flatten an object structure with custom key transformer", () => {
const obj = {
"obj": [{
"other": {
"moo": "foo"
}
}]
};
const expected = {
"obj.$.other.moo": "foo"
};
const result = flattenValues(obj, undefined, "", {
transformKey: (key, info) => info.isArrayIndex ? "$" : key,
leavesOnly: true
});
assert.deepStrictEqual(result, expected, "The test type is correct");
});
it("Will handle an infinite recursive structure", () => {
const obj: any = {
"obj": [{
"other": {
moo: "foo"
}
}]
};
// Create an infinite recursion
obj.obj[0].other.obj = obj;
const result = flattenValues(obj);
assert.strictEqual(typeof result, "object", "The test type is correct");
assert.strictEqual(result["obj"] instanceof Array, true, "The test type is correct");
assert.strictEqual(typeof result["obj.0.other"], "object", "The test type is correct");
assert.strictEqual(typeof result["obj.0.other.moo"], "string", "The test type is correct");
assert.strictEqual(result["obj.0.other.moo"], "foo", "The test value is correct");
assert.strictEqual(typeof result["obj.0.other.obj"], "object", "The test value is correct");
assert.strictEqual(typeof result["obj.0.other.obj.0"], "undefined", "The test value is correct");
});
});
describe("furthest()", () => {
it("Will traverse the tree and find the last available leaf", () => {
const obj = {
"obj": [{
"other": {}
}]
};
const result = furthest(obj, "obj.0.other.val.another");
assert.strictEqual(result, "obj.0.other", "The value was retrieved correctly");
});
it("Will accept an array index wildcard", () => {
const obj = {
"obj": [{
"other": {}
}]
};
const result = furthest(obj, "obj.$.other.val.another");
assert.strictEqual(result, "obj.$.other", "The value was retrieved correctly");
});
it("Will work with a single leaf and no dot notation", () => {
const obj = {
"name": "Foo"
};
const result = furthest(obj, "name");
assert.strictEqual(result, "name", "The value was retrieved correctly");
});
});
describe("split()", () => {
it("Will split non-escaped strings correctly", () => {
const path = "foo.test.bar";
const result = split(path);
assert.strictEqual(result.length, 3, "The result length is correct");
assert.strictEqual(result[0], "foo", "The result is correct");
assert.strictEqual(result[1], "test", "The result is correct");
assert.strictEqual(result[2], "bar", "The result is correct");
});
it("Will split escaped strings correctly", () => {
const path = "foo.test@test\\.com";
const result = split(path);
assert.strictEqual(result.length, 2, "The result length is correct");
assert.strictEqual(result[0], "foo", "The result is correct");
assert.strictEqual(result[1], "test@test\\.com", "The result is correct");
});
});
describe("getMany()", () => {
it("Can get a field value from an object path", () => {
const obj = {
"obj": {
"val": "foo"
}
};
const result = getMany(obj, "obj.val");
assert.deepStrictEqual(result, ["foo"], "The value was retrieved correctly");
});
it("Can get a field value from an array path", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = getMany(obj, "arr.0.val");
assert.deepStrictEqual(result, ["foo"], "The value was retrieved correctly");
});
it("Can return the default value when a path does not exist", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = getMany(obj, "arr.0.nonExistent", "defaultVal");
assert.deepStrictEqual(result, ["defaultVal"], "The value was retrieved correctly");
});
it("Can return the default value when a sub-path does not exist", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = getMany(obj, "arr.3.nonExistent", "defaultVal");
assert.deepStrictEqual(result, ["defaultVal"], "The value was retrieved correctly");
});
it("Will return undefined when the full path is non-existent", () => {
const obj = {
"obj": {
"val": null
}
};
const result = getMany(obj, "obj.val.roo.foo.moo");
assert.deepStrictEqual(result, [], "The value was retrieved correctly");
});
it("Supports escaped paths to get data correctly", () => {
const obj = {
"foo": {
"jim@jones.com": "bar"
}
};
const path = joinEscaped("foo", "jim@jones.com");
const result = getMany(obj, path);
assert.deepStrictEqual(result, ["bar"], "The value is correct");
});
it("Supports auto-expanding arrays when arrayExpansion is true and root is not an array", () => {
const obj = {
"innerObj": {
"arr": [{
"thing": "thought"
}, {
"otherThing": "otherThought"
}, {
"subArr": [{
"value": "bar"
}, {
"value": "ram"
}]
}, {
"subArr": [{
"value": "you"
}, {
"value": undefined
}]
}, {
"subArr": [{
"value": "too"
}]
}]
}
};
const path = "innerObj.arr.subArr.value";
const result = getMany(obj, path, undefined, {arrayTraversal: true, arrayExpansion: true});
assert.deepStrictEqual(result, ["bar", "ram", "you", "too"], "The value is correct");
});
it("Correctly returns undefined when arrayTraversal is true and leaf nodes produce no result and no default value is provided", () => {
const obj = {
"arr": [{
"someValue": "bar"
}, {
"someValue": "ram"
}, {
"someValue": "you"
}]
};
const path = "arr.value";
const result = getMany(obj, path, undefined, {arrayTraversal: true});
assert.deepStrictEqual(result, [], "The value is correct");
});
it("Correctly expands result when a wildcard is used", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all docs in subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr.$";
const result = getMany(obj, path, undefined, {arrayTraversal: false, wildcardExpansion: true});
const expected = [{
"label": "one"
}, {
"label": "two"
}, {
"label": "three"
}];
assert.deepStrictEqual(result, expected, "The value is correct");
});
it("Correctly expands result when a wildcard is used with arrayTraversal enabled and a sub-document positional terminator", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all docs in subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr.$";
const result = getMany(obj, path, undefined, {arrayTraversal: true, wildcardExpansion: true});
const expected = [{
"label": "one"
}, {
"label": "two"
}, {
"label": "three"
}];
assert.deepStrictEqual(result, expected, "The value is correct");
});
it("Correctly expands result when a wildcard is used with arrayTraversal enabled and a non-positional terminator", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr";
const result = getMany(obj, path, undefined, {arrayTraversal: true, wildcardExpansion: true});
const expected = [[{
"label": "one"
}], [{
"label": "two"
}], [{
"label": "three"
}]];
assert.deepStrictEqual(result, expected, "The value is correct");
});
});
describe("get()", () => {
it("Can get a field value from an object path", () => {
const obj = {
"obj": {
"val": "foo"
}
};
const result = get(obj, "obj.val");
assert.strictEqual(result, "foo", "The value was retrieved correctly");
});
it("Can get a field value from an array path", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = get(obj, "arr.0.val");
assert.strictEqual(result, "foo", "The value was retrieved correctly");
});
it("Can return the default value when a path does not exist", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = get(obj, "arr.0.nonExistent", "defaultVal");
assert.strictEqual(result, "defaultVal", "The value was retrieved correctly");
});
it("Can return the default value when a sub-path does not exist", () => {
const obj = {
"arr": [{
"val": "foo"
}]
};
const result = get(obj, "arr.3.nonExistent", "defaultVal");
assert.strictEqual(result, "defaultVal", "The value was retrieved correctly");
});
it("Will return undefined when the full path is non-existent", () => {
const obj = {
"obj": {
"val": null
}
};
const result = get(obj, "obj.val.roo.foo.moo");
assert.strictEqual(result, undefined, "The value was retrieved correctly");
});
it("Supports escaped paths to get data correctly", () => {
const obj = {
"foo": {
"jim@jones.com": "bar"
}
};
const path = joinEscaped("foo", "jim@jones.com");
const result = get(obj, path);
assert.strictEqual(result, "bar", "The value is correct");
});
it("Supports auto-traversing arrays when arrayTraversal is true", () => {
const obj = {
"arr": [{
"thing": "thought"
}, {
"otherThing": "otherThought"
}, {
"value": "bar"
}, {
"value": "ram"
}, {
"value": "you"
}]
};
const path = "arr.value";
const result = get(obj, path, undefined, {arrayTraversal: true});
assert.strictEqual(result, "bar", "The value is correct");
});
it("Supports auto-expanding arrays when arrayExpansion is true", () => {
const obj = {
"arr": [{
"thing": "thought"
}, {
"otherThing": "otherThought"
}, {
"value": "bar"
}, {
"value": "ram"
}, {
"value": "you"
}, {
"value": undefined
}, {
"value": "too"
}]
};
const path = "arr.value";
const result = get(obj, path, undefined, {arrayTraversal: true, arrayExpansion: true});
assert.deepStrictEqual(result, ["bar", "ram", "you", "too"], "The value is correct");
});
it("Supports nested auto-expanding arrays when arrayExpansion is true", () => {
const obj = {
"arr": [{
"thing": "thought"
}, {
"otherThing": "otherThought"
}, {
"subArr": [{
"value": "bar"
}, {
"value": "ram"
}]
}, {
"subArr": [{
"value": "you"
}, {
"value": undefined
}]
}, {
"subArr": [{
"value": "too"
}]
}]
};
const path = "arr.subArr.value";
const result = get(obj, path, undefined, {arrayTraversal: true, arrayExpansion: true});
assert.deepStrictEqual(result, ["bar", "ram", "you", "too"], "The value is correct");
});
it("Correctly returns default value when arrayTraversal is true and leaf nodes produce no result", () => {
const obj = {
"arr": [{
"someValue": "bar"
}, {
"someValue": "ram"
}, {
"someValue": "you"
}]
};
const path = "arr.value";
const result = get(obj, path, "myDefaultVal", {arrayTraversal: true});
assert.strictEqual(result, "myDefaultVal", "The value is correct");
});
it("Correctly returns undefined when arrayTraversal is true and leaf nodes produce no result and no default value is provided", () => {
const obj = {
"arr": [{
"someValue": "bar"
}, {
"someValue": "ram"
}, {
"someValue": "you"
}]
};
const path = "arr.value";
const result = get(obj, path, undefined, {arrayTraversal: true});
assert.strictEqual(result, undefined, "The value is correct");
});
it("Correctly expands result when a wildcard is used", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all docs in subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr.$";
const result = get(obj, path, undefined, {arrayTraversal: false, wildcardExpansion: true});
const expected = [{
"label": "one"
}, {
"label": "two"
}, {
"label": "three"
}];
assert.deepStrictEqual(result, expected, "The value is correct");
});
it("Correctly expands result when a wildcard is used with arrayTraversal enabled and a sub-document positional terminator", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all docs in subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr.$";
const result = get(obj, path, undefined, {arrayTraversal: true, wildcardExpansion: true});
const expected = [{
"label": "one"
}, {
"label": "two"
}, {
"label": "three"
}];
assert.deepStrictEqual(result, expected, "The value is correct");
});
it("Correctly expands result when a wildcard is used with arrayTraversal enabled and a non-positional terminator", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.$.subArr.$.subSubArr";
const result = get(obj, path, undefined, {arrayTraversal: true, wildcardExpansion: true});
const expected = [[{
"label": "one"
}], [{
"label": "two"
}], [{
"label": "three"
}]];
assert.deepStrictEqual(result, expected, "The value is correct");
});
it("Correctly assigns path data when array expansion occurs", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}, {
"label": "two-point-two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.subArr.subSubArr";
const pathData: PathData = {
directPaths: []
}; // We are passing this in by reference and it will be modified by get()
const result = get(obj, path, undefined, {arrayTraversal: true, arrayExpansion: true, pathData});
const expected = [{
"label": "one"
}, {
"label": "two"
}, {
"label": "two-point-two"
}, {
"label": "three"
}];
const pathDataExpected = [
"arr.0.subArr.0.subSubArr.0",
"arr.1.subArr.0.subSubArr.0",
"arr.1.subArr.0.subSubArr.1",
"arr.2.subArr.0.subSubArr.0"
];
assert.deepStrictEqual(result, expected, "The value is correct");
assert.deepStrictEqual(pathData.directPaths, pathDataExpected, "The value is correct");
});
it("Correctly assigns path data when array expansion occurs with a target leaf node", () => {
const obj = {
"arr": [{
"subArr": [{
"label": "thought",
"subSubArr": [{
"label": "one"
}]
}]
}, {
"subArr": [{
"label": "is",
"subSubArr": [{
"label": "two"
}, {
"label": "two-point-two"
}]
}]
}, {
"subArr": [{
"label": "good",
"subSubArr": [{
"label": "three"
}]
}]
}]
};
// Meaning get me all label from all docs in subSubArr from all docs in
// subArr from all docs in arr
const path = "arr.subArr.subSubArr.label";
const pathData: PathData = {
directPaths: []
};
// We are passing this in by reference, and it will be modified by get()
const result = get(obj, path, undefined, {arrayTraversal: true, arrayExpansion: true, pathData});
const expected = [
"one",
"two",
"two-point-two",
"three"
];
const pathDataExpected = [
"arr.0.subArr.0.subSubArr.0.label",
"arr.1.subArr.0.subSubArr.0.label",
"arr.1.subArr.0.subSubArr.1.label",
"arr.2.subArr.0.subSubArr.0.label"
];
assert.deepStrictEqual(result, expected, "The value is correct");
assert.deepStrictEqual(pathData.directPaths, pathDataExpected, "The value is correct");
});
});
describe("set()", () => {
it("Can set a value on the passed object at the correct path with auto-created objects", () => {
const obj: any = {};
const newObj = set(obj, "foo.bar.thing", "foo");
assert.strictEqual(obj.foo.bar.thing, "foo", "The value was set correctly");
assert.strictEqual(newObj, obj, "The object reference is the same");
});
it("Can set a value on the passed object with an array index at the correct path", () => {
const obj = {
"arr": [1]
};
const newObj = set(obj, "arr.1", "foo");
assert.strictEqual(obj.arr[0], 1, "The value was set correctly");
assert.strictEqual(obj.arr[1], "foo", "The value was set correctly");
assert.strictEqual(newObj, obj, "The object reference is the same");
});
it("Can set a value on the passed object with an array index when no array currently exists at the correct path", () => {
const obj: any = {};
const newObj = set(obj, "arr.0", "foo");
assert.strictEqual(obj.arr[0], "foo", "The value was set correctly");
assert.strictEqual(newObj, obj, "The object reference is the same");
});
it("Can set a value on the passed object where the leaf node is an object", () => {
const obj: any = {
"foo": {
"bar": {
"ram": {
original: true
}
}
}
};
const newObj = set(obj, "foo.bar.ram", {copy: true});
assert.strictEqual(obj.foo.bar.ram.copy, true, "The value was set correctly");
assert.strictEqual(newObj, obj, "The object reference is the same");
});
it("Can set a value on the passed object with a single path key", () => {
const obj = {
"foo": true
};
const newObj = set(obj, "foo", false);
assert.strictEqual(obj.foo, false, "The value was set correctly");
assert.strictEqual(newObj, obj, "The object reference is the same");
});
it("Supports escaped paths to set data correctly", () => {
const obj = {
"foo": true
};
const path = joinEscaped("foo", "jim@jones.com");
const newObj = set(obj, path, false);
assert.strictEqual(newObj.foo["jim@jones.com"], false, "The value was set correctly");
});
it("Handles trying to set a value on a null correctly", () => {
const obj: any = {
"foo": null
};
set(obj, "foo.bar", true);
assert.strictEqual(typeof obj.foo, "object", "The value is correct");
assert.strictEqual(obj.foo.bar, true, "The value is correct");
});
it("Is not vulnerable to __proto__ pollution", () => {
const obj: any = {};
set(obj, "__proto__.polluted", true);
assert.strictEqual(obj.polluted, undefined, "The object prototype cannot be polluted");
});
});
describe("setImmutable()", () => {
it("Can de-reference all objects which contain a change (useful for state management systems)", () => {
const obj = {
"shouldNotChange1": {},
"shouldNotChange2": {},
"shouldChange1": {
"shouldChange2": {
"shouldNotChange3": {},
"shouldChange3": {
"value": false
}
}
}
};
const shouldNotChange1 = obj.shouldNotChange1;
const shouldNotChange2 = obj.shouldNotChange2;
const shouldNotChange3 = obj.shouldChange1.shouldChange2.shouldNotChange3;
const shouldChange1 = obj.shouldChange1;
const shouldChange2 = obj.shouldChange1.shouldChange2;
const shouldChange3 = obj.shouldChange1.shouldChange2.shouldChange3;
const newObj = set(obj, "shouldChange1.shouldChange2.shouldChange3.value", true, {immutable: true});
assert.notStrictEqual(newObj, obj, "Root object is not the same");
assert.strictEqual(obj.shouldChange1.shouldChange2.shouldChange3.value, false, "The value of the original object was not changed");
assert.strictEqual(obj.shouldNotChange1, shouldNotChange1, "Value did not change");
assert.strictEqual(obj.shouldNotChange2, shouldNotChange2, "Value did not change");
assert.strictEqual(obj.shouldChange1.shouldChange2.shouldNotChange3, shouldNotChange3, "Value did not change");
assert.strictEqual(obj.shouldChange1, shouldChange1, "Value did not change");
assert.strictEqual(obj.shouldChange1.shouldChange2, shouldChange2, "Value did not change");
assert.strictEqual(obj.shouldChange1.shouldChange2.shouldChange3, shouldChange3, "Value did not change");
assert.strictEqual(newObj.shouldChange1.shouldChange2.shouldChange3.value, true, "The value of the new object was changed");
assert.strictEqual(newObj.shouldNotChange1, shouldNotChange1, "Value did not change");
assert.strictEqual(newObj.shouldNotChange2, shouldNotChange2, "Value did not change");
assert.strictEqual(newObj.shouldChange1.shouldChange2.shouldNotChange3, shouldNotChange3, "Value did not change");
assert.notStrictEqual(newObj.shouldChange1, shouldChange1, "Value did not change");
assert.notStrictEqual(newObj.shouldChange1.shouldChange2, shouldChange2, "Value did not change");
assert.notStrictEqual(newObj.shouldChange1.shouldChange2.shouldChange3, shouldChange3, "Value did not change");
});
it("Can update a value in an object within a nested array", () => {
const obj = {
"foo": [{
"value": false
}]
};
const foo = obj.foo;
const fooType = type(obj.foo);
const newObj = set(obj, "foo.0.value", true, {immutable: true});
const newFooType = type(newObj.foo);
assert.notStrictEqual(newObj, obj, "Root object is not the same");
assert.strictEqual(fooType, newFooType, "Array type has not changed");
});
it("Is not vulnerable to __proto__ pollution", () => {
const obj: any = {};
setImmutable(obj, "__proto__.polluted", true);
assert.strictEqual(obj.polluted, undefined, "The object prototype cannot be polluted");
});
});
describe("unSet()", () => {
describe("Mutable", () => {
it("Will remove the required key from an object", () => {
const obj = {
"foo": {
"bar": [{
"moo": true,
"baa": "ram you"
}]
}
};
assert.strictEqual(obj.foo.bar[0].baa, "ram you", "Object has value");
const newObj = unSet(obj, "foo.bar.0.baa");
assert.strictEqual(obj.foo.bar[0].baa, undefined, "Object does not have value");
assert.strictEqual(obj.foo.bar[0].hasOwnProperty("baa"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same");
});
it("Will correctly handle a path to nowhere", () => {
const obj = {
"foo": {
"bar": [{
"moo": true,
"baa": "ram you"
}]
}
};
const newObj = unSet(obj, "foo.bar.2.baa");
assert.strictEqual(obj.foo.bar.hasOwnProperty("2"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same");
});
it("Is not vulnerable to __proto__ pollution", () => {
const obj: any = {};
obj.__proto__.unsetPolluted = false;
unSet(obj, "__proto__.unsetPolluted");
assert.strictEqual(obj.unsetPolluted, false, "The object prototype cannot be polluted");
});
});
describe("Immutable", () => {
it("Will remove the required key from an object", () => {
const obj = {
"foo": {
"bar": [{
"moo": true,
"baa": "ram you"
}, {
"two": {},
"three": []
}]
}
};
assert.strictEqual(obj.foo.bar[0].baa, "ram you", "Object has value");
const newObj = unSet(obj, "foo.bar.0.baa", {immutable: true});
// Check existing object is unchanged
assert.strictEqual(obj.foo.bar[0].baa, "ram you", "Data integrity");
assert.notStrictEqual(newObj, obj, "Root object should be different");
assert.notStrictEqual(newObj.foo, obj.foo, "foo object should be different");
assert.notStrictEqual(newObj.foo.bar, obj.foo.bar, "foo.bar object should be different");
assert.notStrictEqual(newObj.foo.bar[0], obj.foo.bar[0], "foo.bar[0] object should be different");
// Check new object is changed
assert.strictEqual(newObj.foo.bar[0].baa, undefined, "Object does not have value");
assert.strictEqual(newObj.foo.bar[0].hasOwnProperty("baa"), false, "Object does not have key");
// Check that new and old object do not share references to changed data
assert.notStrictEqual(newObj, obj, "Root object should be different");
assert.notStrictEqual(newObj.foo, obj.foo, "foo object should be different");
assert.notStrictEqual(newObj.foo.bar, obj.foo.bar, "foo.bar object should be different");
assert.notStrictEqual(newObj.foo.bar[0], obj.foo.bar[0], "foo.bar[0] object should be different");
// Check that new and old object share references to unchanged data
assert.strictEqual(newObj.foo.bar[1], obj.foo.bar[1], "foo.bar[1] object should be same");
});
it("Will correctly handle a path to nowhere", () => {
const obj = {
"foo": {
"bar": [{
"moo": true,
"baa": "ram you"
}]
}
};
const newObj = unSet(obj, "foo.bar.2.baa", {immutable: true});
assert.strictEqual(obj.foo.bar.hasOwnProperty("2"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same because nothing changed");
});
it("Is not vulnerable to __proto__ pollution", () => {
const obj: any = {};
obj.__proto__.unsetPolluted = false;
unSetImmutable(obj, "__proto__.unsetPolluted");
assert.strictEqual(obj.unsetPolluted, false, "The object prototype cannot be polluted");
});
});
describe("Escape at root", () => {
it("Will remove the required key from an object", () => {
const obj = {
"foo@foo.com": {
"bar": [{
"moo": true,
"baa": "ram you"
}]
}
};
assert.strictEqual(obj["foo@foo.com"].bar[0].baa, "ram you", "Object has value");
const newObj = unSet(obj, `${escape("foo@foo.com")}.bar.0.baa`);
assert.strictEqual(obj["foo@foo.com"].bar[0].baa, undefined, "Object does not have value");
assert.strictEqual(obj["foo@foo.com"].bar[0].hasOwnProperty("baa"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same");
});
it("Will correctly handle a path to nowhere", () => {
const obj = {
"foo@foo.com": {
"bar": [{
"moo": true,
"baa": "ram you"
}]
}
};
const newObj = unSet(obj, `${escape("foo@foo.com")}.bar.2.baa`);
assert.strictEqual(obj["foo@foo.com"].bar.hasOwnProperty("2"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same");
});
});
describe("Escape in child", () => {
it("Will remove the required key from an object", () => {
const obj = {
"foo": {
"bar": [{
"moo": true,
"baa@foo.com": "ram you"
}]
}
};
assert.strictEqual(obj.foo.bar[0]["baa@foo.com"], "ram you", "Object has value");
const newObj = unSet(obj, `foo.bar.0.${escape("baa@foo.com")}`);
assert.strictEqual(obj.foo.bar[0]["baa@foo.com"], undefined, "Object does not have value");
assert.strictEqual(obj.foo.bar[0].hasOwnProperty("baa@foo.com"), false, "Object does not have key");
assert.strictEqual(newObj, obj, "Root object is the same");
});
});
});
describe("match()", () => {
describe("Positive tests", () => {
it("Will return true for matching string", () => {
const result = match("Bookshop1", "Bookshop1");
assert.strictEqual(result, true);
});
it("Will return true for matching two objects with a matching query", () => {
const result = match({"profile": {"_id": "Bookshop1"}}, {"profile": {"_id": "Bookshop1"}});
assert.strictEqual(result, true);
});
it("Will return true for matching two objects with a matching query and extended source", () => {
const result = match({test: "Bookshop1", foo: true}, {test: "Bookshop1"});
assert.strictEqual(result, true);
});
it("Will match multiple keys and values", () => {
const result = match({test: "Bookshop1", foo: true}, {test: "Bookshop1", foo: true});
assert.strictEqual(result, true);
});
});
describe("Negative tests", () => {
it("Will return false for non-matching string", () => {
const result = match("Bookshop1", "Bookshop2");
assert.strictEqual(result, false);
});
it("Will return false for matching two objects with a matching query", () => {
const result = match({test: "Bookshop1"}, {test: "Bookshop2"});
assert.strictEqual(result, false);
});
it("Will return false for matching two objects with a matching query and extended source", () => {
const result = match({test: "Bookshop1", foo: true}, {test: "Bookshop2"});
assert.strictEqual(result, false);
});
it("Will match multiple keys and values", () => {
const result = match({test: "Bookshop1", foo: true}, {test: "Bookshop1", foo: false});
assert.strictEqual(result, false);
});
});
});
describe("findPath()", () => {
describe("Positive tests", () => {
it("Will return the correct path for a root string", () => {
const result: any = findPath("Bookshop1", "Bookshop1");
assert.deepEqual(result.path, [""]);
});
it("Will return the correct path for an object nested string", () => {
const result = findPath([{"_id": "Bookshop1"}], "Bookshop1");
assert.deepEqual(result.path, ["0._id"]);
});
it("Will return the correct path for a nested equal object", () => {
const result = findPath({"profile": {"_id": "Bookshop1"}}, {"profile": {"_id": "Bookshop1"}});
assert.deepEqual(result.path, [""]);
});
it("Will return the correct path for an array nested string", () => {
const result = findPath([{"_id": "Bookshop1"}], {"_id": "Bookshop1"});
assert.deepEqual(result.path, ["0"]);
});
it("Will return the correct path for a single-level nested string", () => {
const result = findPath({"profile": {"_id": "Bookshop1"}}, {"_id": "Bookshop1"});
assert.deepEqual(result.path, ["profile"]);
});
it("Will return the correct path for a complex nested string", () => {
const testObj = {
"items": [{
"_id": 1,
"title": "A night to remember",
"stockedBy": ["Bookshop1", "Bookshop4"],
"show": true
}, {
"_id": 2,
"title": "Dream a little dream",
"stockedBy": ["Bookshop1", "Bookshop2"],
"show": true
}]
};
const result = findPath(testObj, {"show": true});
assert.deepEqual(result.path, ["items.0", "items.1"]);
});
it("Will return the correct path for a complex nested object", () => {
const testObj = {
"items": [{
"_id": 1,
"title": "A night to remember",
"stockedBy": ["Bookshop1", "Bookshop4"]
}, {
"_id": 2,
"title": "Dream a little dream",
"stockedBy": ["Bookshop1", "Bookshop2"]
}]
};
const result = findPath(testObj, {_id: 2});
assert.deepEqual(result.path, ["items.1"]);
});
it("Will return the correct path/s for very complex objects", () => {
const data = {
"_id": "3310a727ba01440",
"label": "Project 1",
"type": "project",
"category": "root",
"acceptsCategory": [
"object",
"boolean"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {},
"items": [
{
"_id": "pagesFolder",
"label": "Pages",
"type": "folder",
"category": "folder",
"acceptsCategory": [
"page"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"isSelectable": false,
"data": {},
"items": [
{
"_id": "page1",
"label": "Page 1",
"type": "page",
"category": "component",
"acceptsCategory": [
"layout"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {
"schema": {
"selectedItem": {
"type": "Object",
"required": false
},
"hoveredItem": {
"type": "Object",
"required": false
},
"currentTool": {
"type": "String",
"required": false,
"default": "select"
},
"lang": {
"type": "String",
"required": false,
"default": "en"
},
"user": {
"type": "Object",
"required": false,
"default": {}
}
},
"stateToProps": {
"pipeline": [
{
"sessionState": {
"type": "globalState",
"fromId": "session",
"fromMethod": "get"
},
"setSessionState": {
"type": "globalState",
"fromId": "session",
"fromMethod": "set"
}
},
{
"lang": {
"type": "pipelineFunction",
"function": {
"type": "Program",
"ast": true
}
}
}
]
}
},
"items": [
{
"_id": "37df5e624ad4800",
"label": "Layer",
"isEditing": false,
"type": "layer",
"category": "layout",
"acceptsCategory": [
"layout"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {
"display": {
"type": "flex"
},
"position": {
"type": "none",
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"flex": {
"direction": "row",
"flexGrow": 1,
"flexShrink": 1,
"flexBasis": 0,
"flexBasisUnit": "px"
},
"isPureLayout": false
},
"items": [
{
"_id": "292a82d000fdb60",
"label": "Layer",
"isEditing": false,
"type": "layer",
"category": "layout",
"acceptsCategory": [
"layout"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"data": {
"display": {
"type": "flex"
},
"position": {
"type": "none",
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"flex": {
"direction": "column",
"flexGrow": 1,
"flexShrink": 1,
"flexBasis": 0,
"flexBasisUnit": "px"
},
"isPureLayout": false
},
"items": []
},
{
"_id": "22f4f59e9045740",
"label": "Layer",
"isEditing": false,
"type": "layer",
"category": "layout",
"acceptsCategory": [
"layout"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {
"display": {
"type": "flex"
},
"position": {
"type": "none",
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"flex": {
"direction": "column",
"flexGrow": 1,
"flexShrink": 1,
"flexBasis": 0,
"flexBasisUnit": "px"
},
"isPureLayout": false
},
"items": [
{
"_id": "375a0c74c911ce0",
"label": "Layer",
"isEditing": false,
"type": "layer",
"category": "layout",
"acceptsCategory": [
"layout"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {
"display": {
"type": "flex"
},
"position": {
"type": "none",
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"flex": {
"direction": "row",
"grow": 1,
"shrink": 1,
"basis": 0,
"basisUnit": "px"
},
"isPureLayout": false
},
"items": [
{
"_id": "396c2e9b36f31a0",
"label": "CrumbBar",
"isEditing": false,
"type": "component",
"component": "CrumbBar",
"category": "component",
"acceptsCategory": [],
"acceptsChildren": false,
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": false,
"data": {
"display": {
"type": "flex"
},
"position": {
"type": "none",
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"flex": {
"direction": "row",
"grow": 1,
"shrink": 1,
"basis": 0,
"basisUnit": "px"
},
"isPureLayout": false,
"props": {}
},
"items": []
}
]
}
]
}
]
}
]
}
]
},
{
"_id": "stateFolder",
"label": "State",
"type": "folder",
"category": "folder",
"acceptsCategory": [
"state"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"isSelectable": false,
"data": {},
"items": [
{
"_id": "user",
"label": "User State",
"type": "globalState",
"category": "state",
"acceptsCategory": [],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"data": {
"schema": {
"firstName": {
"type": "String",
"required": true
},
"lastName": {
"type": "String",
"required": true
},
"email": {
"type": "String",
"required": true
},
"dob": {
"type": "Date",
"required": false
}
},
"initialState": {}
}
},
{
"_id": "ui",
"label": "UI State",
"type": "globalState",
"category": "state",
"acceptsCategory": [],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"data": {
"schema": {
"selectedItemId": {
"type": "String",
"default": ""
},
"hoveredItemId": {
"type": "String",
"default": ""
},
"currentTool": {
"type": "String",
"default": "select"
}
},
"initialState": {}
}
},
{
"_id": "lang",
"label": "Language State",
"type": "globalState",
"category": "state",
"acceptsCategory": [],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"data": {
"schema": {
"lang": {
"type": "String",
"default": "en"
},
"available": {
"type": "Array",
"elementType": {
"_id": {
"type": "String",
"required": true
},
"label": {
"type": "String",
"required": true
},
"enabled": {
"type": "Boolean",
"required": true
}
}
}
},
"initialState": {
"lang": "en",
"available": [
{
"_id": "en",
"label": "English",
"enabled": true
}
]
}
}
}
]
},
{
"_id": "servicesFolder",
"label": "Services",
"type": "folder",
"category": "folder",
"acceptsCategory": [
"service"
],
"isExpanded": true,
"isEnabled": true,
"isSelected": false,
"isHovered": true,
"isSelectable": false,
"data": {},
"items": [
{
"_id": "utils.js",
"label": "utils.js",
"type": "file",
"category": "service",
"middleTabId": "codeEditor",
"isExpanded": true,
"isEnabled":