forked from sashin/sashinexists
run npm install to generate a package lock
This commit is contained in:
386
node_modules/@weborigami/async-tree/test/Tree.test.js
generated
vendored
Normal file
386
node_modules/@weborigami/async-tree/test/Tree.test.js
generated
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import MapTree from "../src/drivers/MapTree.js";
|
||||
import { DeepObjectTree, ObjectTree, Tree } from "../src/internal.js";
|
||||
import * as symbols from "../src/symbols.js";
|
||||
|
||||
describe("Tree", () => {
|
||||
test("assign applies one tree to another", async () => {
|
||||
const target = new DeepObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
more: {
|
||||
d: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const source = new DeepObjectTree({
|
||||
a: 4, // Overwrite existing value
|
||||
b: undefined, // Delete
|
||||
c: 5, // Add
|
||||
more: {
|
||||
// Should leave existing `more` keys alone.
|
||||
e: 6, // Add
|
||||
},
|
||||
// Add new subtree
|
||||
extra: {
|
||||
f: 7,
|
||||
},
|
||||
});
|
||||
|
||||
// Apply changes.
|
||||
const result = await Tree.assign(target, source);
|
||||
|
||||
assert.equal(result, target);
|
||||
const plain = await Tree.plain(target);
|
||||
assert.deepEqual(plain, {
|
||||
a: 4,
|
||||
c: 5,
|
||||
more: {
|
||||
d: 3,
|
||||
e: 6,
|
||||
},
|
||||
extra: {
|
||||
f: 7,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("assign() can apply updates to an array", async () => {
|
||||
const target = new ObjectTree(["a", "b", "c"]);
|
||||
await Tree.assign(target, ["d", "e"]);
|
||||
assert.deepEqual(await Tree.plain(target), ["d", "e", "c"]);
|
||||
});
|
||||
|
||||
test("clear() removes all values", async () => {
|
||||
const fixture = createFixture();
|
||||
await Tree.clear(fixture);
|
||||
assert.deepEqual(Array.from(await Tree.entries(fixture)), []);
|
||||
});
|
||||
|
||||
test("entries() returns the [key, value] pairs", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.deepEqual(Array.from(await Tree.entries(fixture)), [
|
||||
["Alice.md", "Hello, **Alice**."],
|
||||
["Bob.md", "Hello, **Bob**."],
|
||||
["Carol.md", "Hello, **Carol**."],
|
||||
]);
|
||||
});
|
||||
|
||||
test("forEach() invokes a callback for each entry", async () => {
|
||||
const fixture = createFixture();
|
||||
const results = {};
|
||||
await Tree.forEach(fixture, async (value, key) => {
|
||||
results[key] = value;
|
||||
});
|
||||
assert.deepEqual(results, {
|
||||
"Alice.md": "Hello, **Alice**.",
|
||||
"Bob.md": "Hello, **Bob**.",
|
||||
"Carol.md": "Hello, **Carol**.",
|
||||
});
|
||||
});
|
||||
|
||||
test("from() returns an async tree as is", async () => {
|
||||
const tree1 = new ObjectTree({
|
||||
a: "Hello, a.",
|
||||
});
|
||||
const tree2 = Tree.from(tree1);
|
||||
assert.equal(tree2, tree1);
|
||||
});
|
||||
|
||||
test("from() uses an object's unpack() method if defined", async () => {
|
||||
const obj = new String();
|
||||
/** @type {any} */ (obj).unpack = () => ({
|
||||
a: "Hello, a.",
|
||||
});
|
||||
const tree = Tree.from(obj);
|
||||
assert.deepEqual(await Tree.plain(tree), {
|
||||
a: "Hello, a.",
|
||||
});
|
||||
});
|
||||
|
||||
test("from returns a deep object tree if deep option is true", async () => {
|
||||
const obj = {
|
||||
sub: {
|
||||
a: 1,
|
||||
},
|
||||
};
|
||||
const tree = Tree.from(obj, { deep: true });
|
||||
assert(tree instanceof DeepObjectTree);
|
||||
});
|
||||
|
||||
test("from returns a deep object tree if object has [deep] symbol set", async () => {
|
||||
const obj = {
|
||||
sub: {
|
||||
a: 1,
|
||||
},
|
||||
};
|
||||
Object.defineProperty(obj, symbols.deep, { value: true });
|
||||
const tree = Tree.from(obj);
|
||||
assert(tree instanceof DeepObjectTree);
|
||||
});
|
||||
|
||||
test("from() creates a deferred tree if unpack() returns a promise", async () => {
|
||||
const obj = new String();
|
||||
/** @type {any} */ (obj).unpack = async () => ({
|
||||
a: "Hello, a.",
|
||||
});
|
||||
const tree = Tree.from(obj);
|
||||
assert.deepEqual(await Tree.plain(tree), {
|
||||
a: "Hello, a.",
|
||||
});
|
||||
});
|
||||
|
||||
test("from() autoboxes primitive values", async () => {
|
||||
const tree = Tree.from("Hello, world.");
|
||||
const slice = await tree.get("slice");
|
||||
const result = await slice(0, 5);
|
||||
assert.equal(result, "Hello");
|
||||
});
|
||||
|
||||
test("has returns true if the key exists", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.equal(await Tree.has(fixture, "Alice.md"), true);
|
||||
assert.equal(await Tree.has(fixture, "David.md"), false);
|
||||
});
|
||||
|
||||
test("isAsyncTree returns true if the object is a tree", () => {
|
||||
const missingGetAndKeys = {};
|
||||
assert(!Tree.isAsyncTree(missingGetAndKeys));
|
||||
|
||||
const missingIterator = {
|
||||
async get() {},
|
||||
};
|
||||
assert(!Tree.isAsyncTree(missingIterator));
|
||||
|
||||
const missingGet = {
|
||||
async keys() {},
|
||||
};
|
||||
assert(!Tree.isAsyncTree(missingGet));
|
||||
|
||||
const hasGetAndKeys = {
|
||||
async get() {},
|
||||
async keys() {},
|
||||
};
|
||||
assert(Tree.isAsyncTree(hasGetAndKeys));
|
||||
});
|
||||
|
||||
test("isAsyncMutableTree returns true if the object is a mutable tree", () => {
|
||||
assert.equal(
|
||||
Tree.isAsyncMutableTree({
|
||||
get() {},
|
||||
keys() {},
|
||||
}),
|
||||
false
|
||||
);
|
||||
assert.equal(Tree.isAsyncMutableTree(createFixture()), true);
|
||||
});
|
||||
|
||||
test("isTreelike() returns true if the argument can be cast to an async tree", () => {
|
||||
assert(!Tree.isTreelike(null));
|
||||
assert(Tree.isTreelike({}));
|
||||
assert(Tree.isTreelike([]));
|
||||
assert(Tree.isTreelike(new Map()));
|
||||
assert(Tree.isTreelike(new Set()));
|
||||
});
|
||||
|
||||
test("map() maps values", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: "Alice",
|
||||
more: {
|
||||
b: "Bob",
|
||||
},
|
||||
});
|
||||
const mapped = Tree.map(tree, {
|
||||
deep: true,
|
||||
value: (value) => value.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(mapped), {
|
||||
a: "ALICE",
|
||||
more: {
|
||||
b: "BOB",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("mapReduce() can map values and reduce them", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
more: {
|
||||
c: 3,
|
||||
},
|
||||
d: 4,
|
||||
});
|
||||
const reduced = await Tree.mapReduce(
|
||||
tree,
|
||||
(value) => value,
|
||||
async (values) => String.prototype.concat(...values)
|
||||
);
|
||||
assert.deepEqual(reduced, "1234");
|
||||
});
|
||||
|
||||
test("paths returns an array of paths to the values in the tree", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: {
|
||||
d: 3,
|
||||
e: 4,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(await Tree.paths(tree), ["a", "b", "c/d", "c/e"]);
|
||||
});
|
||||
|
||||
test("plain() produces a plain object version of a tree", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: 1,
|
||||
// Slashes should be normalized
|
||||
"sub1/": {
|
||||
b: 2,
|
||||
},
|
||||
sub2: {
|
||||
c: 3,
|
||||
},
|
||||
});
|
||||
const plain = await Tree.plain(tree);
|
||||
assert.deepEqual(plain, {
|
||||
a: 1,
|
||||
sub1: {
|
||||
b: 2,
|
||||
},
|
||||
sub2: {
|
||||
c: 3,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("plain() produces an array for an array-like tree", async () => {
|
||||
const original = ["a", "b", "c"];
|
||||
const tree = new ObjectTree(original);
|
||||
const plain = await Tree.plain(tree);
|
||||
assert.deepEqual(plain, original);
|
||||
});
|
||||
|
||||
test("plain() leaves an array-like tree as an object if keys aren't consecutive", async () => {
|
||||
const original = {
|
||||
0: "a",
|
||||
1: "b",
|
||||
// missing
|
||||
3: "c",
|
||||
};
|
||||
const tree = new ObjectTree(original);
|
||||
const plain = await Tree.plain(tree);
|
||||
assert.deepEqual(plain, original);
|
||||
});
|
||||
|
||||
test("plain() returns empty array or object for ObjectTree as necessary", async () => {
|
||||
const tree = new ObjectTree({});
|
||||
assert.deepEqual(await Tree.plain(tree), {});
|
||||
const arrayTree = new ObjectTree([]);
|
||||
assert.deepEqual(await Tree.plain(arrayTree), []);
|
||||
});
|
||||
|
||||
test("plain() awaits async properties", async () => {
|
||||
const object = {
|
||||
get name() {
|
||||
return Promise.resolve("Alice");
|
||||
},
|
||||
};
|
||||
assert.deepEqual(await Tree.plain(object), { name: "Alice" });
|
||||
});
|
||||
|
||||
test("plain() coerces TypedArray values to strings", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: new TextEncoder().encode("Hello, world."),
|
||||
});
|
||||
const plain = await Tree.plain(tree);
|
||||
assert.equal(plain.a, "Hello, world.");
|
||||
});
|
||||
|
||||
test("remove method removes a value", async () => {
|
||||
const fixture = createFixture();
|
||||
await Tree.remove(fixture, "Alice.md");
|
||||
assert.deepEqual(Array.from(await Tree.entries(fixture)), [
|
||||
["Bob.md", "Hello, **Bob**."],
|
||||
["Carol.md", "Hello, **Carol**."],
|
||||
]);
|
||||
});
|
||||
|
||||
test("toFunction returns a function that invokes a tree's get() method", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
});
|
||||
const fn = Tree.toFunction(tree);
|
||||
assert.equal(await fn("a"), 1);
|
||||
assert.equal(await fn("b"), 2);
|
||||
});
|
||||
|
||||
test("traverse() a path of keys", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a1: 1,
|
||||
a2: {
|
||||
b1: 2,
|
||||
b2: {
|
||||
c1: 3,
|
||||
c2: 4,
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(await Tree.traverse(tree), tree);
|
||||
assert.equal(await Tree.traverse(tree, "a1"), 1);
|
||||
assert.equal(await Tree.traverse(tree, "a2", "b2", "c2"), 4);
|
||||
assert.equal(
|
||||
await Tree.traverse(tree, "a2", "doesntexist", "c2"),
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
test("traverse() a function with fixed number of arguments", async () => {
|
||||
const tree = (a, b) => ({
|
||||
c: "Result",
|
||||
});
|
||||
assert.equal(await Tree.traverse(tree, "a", "b", "c"), "Result");
|
||||
});
|
||||
|
||||
test("traverse() from one tree into another", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: {
|
||||
b: new MapTree([
|
||||
["c", "Hello"],
|
||||
["d", "Goodbye"],
|
||||
]),
|
||||
},
|
||||
});
|
||||
assert.equal(await Tree.traverse(tree, "a", "b", "c"), "Hello");
|
||||
});
|
||||
|
||||
test("traversePath() traverses a slash-separated path", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: {
|
||||
b: {
|
||||
c: "Hello",
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(await Tree.traversePath(tree, "a/b/c"), "Hello");
|
||||
});
|
||||
|
||||
test("values() returns the store's values", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.deepEqual(Array.from(await Tree.values(fixture)), [
|
||||
"Hello, **Alice**.",
|
||||
"Hello, **Bob**.",
|
||||
"Hello, **Carol**.",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture() {
|
||||
return new ObjectTree({
|
||||
"Alice.md": "Hello, **Alice**.",
|
||||
"Bob.md": "Hello, **Bob**.",
|
||||
"Carol.md": "Hello, **Carol**.",
|
||||
});
|
||||
}
|
||||
54
node_modules/@weborigami/async-tree/test/browser/assert.js
generated
vendored
Normal file
54
node_modules/@weborigami/async-tree/test/browser/assert.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* A simple test runner for the browser to run the subset of the Node.s test
|
||||
* runner used by the project.
|
||||
*/
|
||||
|
||||
export default function assert(condition) {
|
||||
if (!condition) {
|
||||
throw new Error("Assertion failed");
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal = (actual, expected) => {
|
||||
if (Number.isNaN(actual) && Number.isNaN(expected)) {
|
||||
return;
|
||||
} else if (actual == expected) {
|
||||
return;
|
||||
} else {
|
||||
throw new Error(`Expected ${expected} but got ${actual}`);
|
||||
}
|
||||
};
|
||||
|
||||
// This is a simplified deepEqual test that examines the conditions we care
|
||||
// about. For reference, the actual Node assert.deepEqual is much more complex:
|
||||
// see https://github.com/nodejs/node/blob/main/lib/internal/util/comparisons.js
|
||||
assert.deepEqual = (actual, expected) => {
|
||||
if (actual === expected) {
|
||||
return;
|
||||
} else if (
|
||||
typeof actual === "object" &&
|
||||
actual != null &&
|
||||
typeof expected === "object" &&
|
||||
expected != null &&
|
||||
Object.keys(actual).length === Object.keys(expected).length
|
||||
) {
|
||||
for (const prop in actual) {
|
||||
if (!expected.hasOwnProperty(prop)) {
|
||||
break;
|
||||
}
|
||||
assert.deepEqual(actual[prop], expected[prop]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`Expected ${expected} but got ${actual}`);
|
||||
};
|
||||
|
||||
assert.rejects = async (promise) => {
|
||||
try {
|
||||
await promise;
|
||||
throw new Error("Expected promise to reject but it resolved");
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
35
node_modules/@weborigami/async-tree/test/browser/index.html
generated
vendored
Normal file
35
node_modules/@weborigami/async-tree/test/browser/index.html
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"node:assert": "./assert.js",
|
||||
"node:test": "./testRunner.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- Omit FileTree.test.js, which is Node.js only -->
|
||||
<!-- Omit SiteTree.test.js, which requires mocks -->
|
||||
<script type="module" src="../BrowserFileTree.test.js"></script>
|
||||
<script type="module" src="../DeferredTree.test.js"></script>
|
||||
<script type="module" src="../FunctionTree.test.js"></script>
|
||||
<script type="module" src="../MapTree.test.js"></script>
|
||||
<script type="module" src="../ObjectTree.test.js"></script>
|
||||
<script type="module" src="../SetTree.test.js"></script>
|
||||
<script type="module" src="../Tree.test.js"></script>
|
||||
<script type="module" src="../operations/cache.test.js"></script>
|
||||
<script type="module" src="../operations/merge.test.js"></script>
|
||||
<script type="module" src="../operations/deepMerge.test.js"></script>
|
||||
<script type="module" src="../transforms/cachedKeyMaps.test.js"></script>
|
||||
<script
|
||||
type="module"
|
||||
src="../transforms/keyFunctionsForExtensions.test.js"
|
||||
></script>
|
||||
<script type="module" src="../transforms/mapFn.test.js"></script>
|
||||
<script type="module" src="../utilities.test.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
51
node_modules/@weborigami/async-tree/test/browser/testRunner.js
generated
vendored
Normal file
51
node_modules/@weborigami/async-tree/test/browser/testRunner.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* A simple test runner for the browser to run the subset of the Node.s test
|
||||
* runner used by the project.
|
||||
*/
|
||||
|
||||
let promises = {};
|
||||
let currentSuite;
|
||||
|
||||
const markers = {
|
||||
success: "✅",
|
||||
skipped: "ー",
|
||||
fail: "❌",
|
||||
};
|
||||
|
||||
export async function describe(name, fn) {
|
||||
promises[name] = [];
|
||||
currentSuite = name;
|
||||
await fn();
|
||||
const results = await Promise.all(promises[name]);
|
||||
const someFailed = results.some((result) => result.result === "fail");
|
||||
const header = `${someFailed ? markers.fail : markers.success} ${name}`;
|
||||
console[someFailed ? "group" : "groupCollapsed"](header);
|
||||
for (const result of results) {
|
||||
const marker = markers[result.result];
|
||||
const name = result.name;
|
||||
const message = result.result === "fail" ? `: ${result.message}` : "";
|
||||
const skipped = result.result === "skipped" ? " [skipped]" : "";
|
||||
console.log(`${marker} ${name}${message}${skipped}`);
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
// Node test() calls can call an async function, but the test() function isn't
|
||||
// declared async. We implicitly wrap the test call with a Promise and add it to
|
||||
// the list of promises for the current suite.
|
||||
export async function test(name, fn) {
|
||||
promises[currentSuite].push(runTest(name, fn));
|
||||
}
|
||||
|
||||
test.skip = (name, fn) => {
|
||||
promises[currentSuite].push(Promise.resolve({ result: "skipped", name }));
|
||||
};
|
||||
|
||||
async function runTest(name, fn) {
|
||||
try {
|
||||
await fn();
|
||||
return { result: "success", name };
|
||||
} catch (/** @type {any} */ error) {
|
||||
return { result: "fail", name, message: error.message };
|
||||
}
|
||||
}
|
||||
153
node_modules/@weborigami/async-tree/test/drivers/BrowserFileTree.test.js
generated
vendored
Normal file
153
node_modules/@weborigami/async-tree/test/drivers/BrowserFileTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import BrowserFileTree from "../../src/drivers/BrowserFileTree.js";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
|
||||
// Skip these tests if we're not in a browser.
|
||||
const isBrowser = typeof window !== "undefined";
|
||||
if (isBrowser) {
|
||||
describe("BrowserFileTree", async () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const fixture = await createFixture();
|
||||
assert.deepEqual(Array.from(await fixture.keys()), [
|
||||
"Alice.md",
|
||||
"Bob.md",
|
||||
"Carol.md",
|
||||
"subfolder/",
|
||||
]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const fixture = await createFixture();
|
||||
const buffer = await fixture.get("Alice.md");
|
||||
assert.equal(text(buffer), "Hello, **Alice**.");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = await createFixture();
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
|
||||
test("getting empty key returns undefined", async () => {
|
||||
const fixture = await createFixture();
|
||||
assert.equal(await fixture.get(""), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const fixture = await createFixture();
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const fixture = await createFixture();
|
||||
const subfolder = await fixture.get("subfolder");
|
||||
assert.equal(subfolder.parent, fixture);
|
||||
});
|
||||
|
||||
test("can retrieve values with optional trailing slash", async () => {
|
||||
const fixture = await createFixture();
|
||||
assert(await fixture.get("Alice.md"));
|
||||
assert(await fixture.get("Alice.md/"));
|
||||
assert(await fixture.get("subfolder"));
|
||||
assert(await fixture.get("subfolder/"));
|
||||
});
|
||||
|
||||
test("can set a value", async () => {
|
||||
const fixture = await createFixture();
|
||||
|
||||
// Update existing key.
|
||||
await fixture.set("Alice.md", "Goodbye, **Alice**.");
|
||||
|
||||
// New key.
|
||||
await fixture.set("David.md", "Hello, **David**.");
|
||||
|
||||
// Delete key.
|
||||
await fixture.set("Bob.md", undefined);
|
||||
|
||||
// Delete non-existent key.
|
||||
await fixture.set("xyz", undefined);
|
||||
|
||||
assert.deepEqual(await strings(fixture), {
|
||||
"Alice.md": "Goodbye, **Alice**.",
|
||||
"Carol.md": "Hello, **Carol**.",
|
||||
"David.md": "Hello, **David**.",
|
||||
subfolder: {},
|
||||
});
|
||||
});
|
||||
|
||||
test("can create a subfolder via set", async () => {
|
||||
const fixture = await createFixture();
|
||||
const tree = {
|
||||
async get(key) {
|
||||
const name = key.replace(/\.md$/, "");
|
||||
return `Hello, **${name}**.`;
|
||||
},
|
||||
async keys() {
|
||||
return ["Ellen.md"];
|
||||
},
|
||||
};
|
||||
await fixture.set("more", tree);
|
||||
assert.deepEqual(await strings(fixture), {
|
||||
"Alice.md": "Hello, **Alice**.",
|
||||
"Bob.md": "Hello, **Bob**.",
|
||||
"Carol.md": "Hello, **Carol**.",
|
||||
more: {
|
||||
"Ellen.md": "Hello, **Ellen**.",
|
||||
},
|
||||
subfolder: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function createFile(directory, name, contents) {
|
||||
const file = await directory.getFileHandle(name, { create: true });
|
||||
const writable = await file.createWritable();
|
||||
await writable.write(contents);
|
||||
await writable.close();
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
async function createFixture() {
|
||||
const root = await navigator.storage.getDirectory();
|
||||
const directory = await root.getDirectoryHandle("async-tree", {
|
||||
create: true,
|
||||
});
|
||||
|
||||
// Create a new subdirectory for each test.
|
||||
const subdirectoryName = `test${count++}`;
|
||||
|
||||
// Delete any pre-existing subdirectory with that name.
|
||||
try {
|
||||
await directory.removeEntry(subdirectoryName, { recursive: true });
|
||||
} catch (e) {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
const subdirectory = await directory.getDirectoryHandle(subdirectoryName, {
|
||||
create: true,
|
||||
});
|
||||
|
||||
await createFile(subdirectory, "Alice.md", "Hello, **Alice**.");
|
||||
await createFile(subdirectory, "Bob.md", "Hello, **Bob**.");
|
||||
await createFile(subdirectory, "Carol.md", "Hello, **Carol**.");
|
||||
|
||||
await subdirectory.getDirectoryHandle("subfolder", {
|
||||
create: true,
|
||||
});
|
||||
|
||||
return new BrowserFileTree(subdirectory);
|
||||
}
|
||||
|
||||
async function strings(tree) {
|
||||
return Tree.plain(Tree.map(tree, (value) => text(value)));
|
||||
}
|
||||
|
||||
function text(arrayBuffer) {
|
||||
return new TextDecoder().decode(arrayBuffer);
|
||||
}
|
||||
17
node_modules/@weborigami/async-tree/test/drivers/DeepMapTree.test.js
generated
vendored
Normal file
17
node_modules/@weborigami/async-tree/test/drivers/DeepMapTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import DeepMapTree from "../../src/drivers/DeepMapTree.js";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
|
||||
describe("DeepMapTree", () => {
|
||||
test("returns a DeepMapTree for value that's a Map", async () => {
|
||||
const tree = new DeepMapTree([
|
||||
["a", 1],
|
||||
["map", new Map([["b", 2]])],
|
||||
]);
|
||||
const map = await tree.get("map");
|
||||
assert.equal(map instanceof DeepMapTree, true);
|
||||
assert.deepEqual(await Tree.plain(map), { b: 2 });
|
||||
assert.equal(map.parent, tree);
|
||||
});
|
||||
});
|
||||
35
node_modules/@weborigami/async-tree/test/drivers/DeepObjectTree.test.js
generated
vendored
Normal file
35
node_modules/@weborigami/async-tree/test/drivers/DeepObjectTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
||||
|
||||
describe("DeepObjectTree", () => {
|
||||
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
||||
const tree = createFixture();
|
||||
|
||||
const object = await tree.get("object");
|
||||
assert.equal(object instanceof DeepObjectTree, true);
|
||||
assert.deepEqual(await Tree.plain(object), { b: 2 });
|
||||
assert.equal(object.parent, tree);
|
||||
|
||||
const array = await tree.get("array");
|
||||
assert.equal(array instanceof DeepObjectTree, true);
|
||||
assert.deepEqual(await Tree.plain(array), [3]);
|
||||
assert.equal(array.parent, tree);
|
||||
});
|
||||
|
||||
test("adds trailing slashes to keys for subtrees including plain objects or arrays", async () => {
|
||||
const tree = createFixture();
|
||||
const keys = Array.from(await tree.keys());
|
||||
assert.deepEqual(keys, ["a", "object/", "array/"]);
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture() {
|
||||
return new DeepObjectTree({
|
||||
a: 1,
|
||||
object: {
|
||||
b: 2,
|
||||
},
|
||||
array: [3],
|
||||
});
|
||||
}
|
||||
22
node_modules/@weborigami/async-tree/test/drivers/DeferredTree.test.js
generated
vendored
Normal file
22
node_modules/@weborigami/async-tree/test/drivers/DeferredTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import DeferredTree from "../../src/drivers/DeferredTree.js";
|
||||
import { ObjectTree, Tree } from "../../src/internal.js";
|
||||
|
||||
describe("DeferredTree", () => {
|
||||
test("lazy-loads a treelike object", async () => {
|
||||
const tree = new DeferredTree(async () => ({ a: 1, b: 2, c: 3 }));
|
||||
assert.deepEqual(await Tree.plain(tree), { a: 1, b: 2, c: 3 });
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const object = {
|
||||
a: 1,
|
||||
};
|
||||
const parent = new ObjectTree({});
|
||||
const fixture = new DeferredTree(() => object);
|
||||
fixture.parent = parent;
|
||||
const tree = await fixture.tree();
|
||||
assert.equal(tree.parent, parent);
|
||||
});
|
||||
});
|
||||
116
node_modules/@weborigami/async-tree/test/drivers/ExplorableSiteTree.test.js
generated
vendored
Normal file
116
node_modules/@weborigami/async-tree/test/drivers/ExplorableSiteTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import assert from "node:assert";
|
||||
import { beforeEach, describe, mock, test } from "node:test";
|
||||
import ExplorableSiteTree from "../../src/drivers/ExplorableSiteTree.js";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
|
||||
const textDecoder = new TextDecoder();
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
const mockHost = "https://mock";
|
||||
|
||||
const mockResponses = {
|
||||
"/.keys.json": {
|
||||
data: JSON.stringify(["about/", "index.html"]),
|
||||
},
|
||||
"/about": {
|
||||
redirected: true,
|
||||
status: 301,
|
||||
url: "https://mock/about/",
|
||||
},
|
||||
"/about/.keys.json": {
|
||||
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
|
||||
},
|
||||
"/about/Alice.html": {
|
||||
data: "Hello, Alice!",
|
||||
},
|
||||
"/about/Bob.html": {
|
||||
data: "Hello, Bob!",
|
||||
},
|
||||
"/about/Carol.html": {
|
||||
data: "Hello, Carol!",
|
||||
},
|
||||
"/index.html": {
|
||||
data: "Home page",
|
||||
},
|
||||
};
|
||||
|
||||
describe("ExplorableSiteTree", () => {
|
||||
beforeEach(() => {
|
||||
mock.method(global, "fetch", mockFetch);
|
||||
});
|
||||
|
||||
test("can get the keys of a tree", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
const keys = await fixture.keys();
|
||||
assert.deepEqual(Array.from(keys), ["about/", "index.html"]);
|
||||
});
|
||||
|
||||
test("can get a plain value for a key", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
const arrayBuffer = await fixture.get("index.html");
|
||||
const text = textDecoder.decode(arrayBuffer);
|
||||
assert.equal(text, "Home page");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test("can return a new tree for a key that redirects", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
const about = await fixture.get("about");
|
||||
assert(about instanceof ExplorableSiteTree);
|
||||
assert.equal(about.href, "https://mock/about/");
|
||||
});
|
||||
|
||||
test("can convert a site to a plain object", async () => {
|
||||
const fixture = new ExplorableSiteTree(mockHost);
|
||||
// Convert buffers to strings.
|
||||
const strings = Tree.map(fixture, {
|
||||
deep: true,
|
||||
value: (value) => textDecoder.decode(value),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(strings), {
|
||||
about: {
|
||||
"Alice.html": "Hello, Alice!",
|
||||
"Bob.html": "Hello, Bob!",
|
||||
"Carol.html": "Hello, Carol!",
|
||||
},
|
||||
"index.html": "Home page",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function mockFetch(href) {
|
||||
if (!href.startsWith(mockHost)) {
|
||||
return { status: 404 };
|
||||
}
|
||||
const path = href.slice(mockHost.length);
|
||||
const mockedResponse = mockResponses[path];
|
||||
if (mockedResponse) {
|
||||
return Object.assign(
|
||||
{
|
||||
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: () => mockedResponse.data,
|
||||
},
|
||||
mockedResponse
|
||||
);
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
status: 404,
|
||||
};
|
||||
}
|
||||
192
node_modules/@weborigami/async-tree/test/drivers/FileTree.test.js
generated
vendored
Normal file
192
node_modules/@weborigami/async-tree/test/drivers/FileTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
import assert from "node:assert";
|
||||
import * as fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, test } from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import FileTree from "../../src/drivers/FileTree.js";
|
||||
import { ObjectTree, Tree } from "../../src/internal.js";
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const tempDirectory = path.join(dirname, "fixtures/temp");
|
||||
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
describe("FileTree", async () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
assert.deepEqual(Array.from(await fixture.keys()), [
|
||||
"Alice.md",
|
||||
"Bob.md",
|
||||
"Carol.md",
|
||||
"subfolder/",
|
||||
]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
const buffer = await fixture.get("Alice.md");
|
||||
const text = textDecoder.decode(buffer);
|
||||
assert.equal(text, "Hello, **Alice**.");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
|
||||
test("getting empty key returns undefined", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
assert.equal(await fixture.get(""), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test("can retrieve values with optional trailing slash", async () => {
|
||||
const fixture = createFixture("fixtures/markdown");
|
||||
assert(await fixture.get("Alice.md"));
|
||||
assert(await fixture.get("Alice.md/"));
|
||||
assert(await fixture.get("subfolder"));
|
||||
assert(await fixture.get("subfolder/"));
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const fixture = createFixture("fixtures");
|
||||
const markdown = await fixture.get("markdown");
|
||||
assert.equal(markdown.parent, fixture);
|
||||
});
|
||||
|
||||
test("can write out a file via set()", async () => {
|
||||
await createTempDirectory();
|
||||
|
||||
// Write out a file.
|
||||
const fileName = "file1";
|
||||
const fileText = "This is the first file.";
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set(fileName, fileText);
|
||||
|
||||
// Read it back in.
|
||||
const filePath = path.join(tempDirectory, fileName);
|
||||
const actualText = String(await fs.readFile(filePath));
|
||||
|
||||
assert.equal(fileText, actualText);
|
||||
|
||||
await removeTempDirectory();
|
||||
});
|
||||
|
||||
test("create subfolder via set() with empty object value", async () => {
|
||||
await createTempDirectory();
|
||||
|
||||
// Write out new, empty folder called "empty".
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set("empty", {});
|
||||
|
||||
// Verify folder exists and has no contents.
|
||||
const folderPath = path.join(tempDirectory, "empty");
|
||||
const stats = await fs.stat(folderPath);
|
||||
assert(stats.isDirectory());
|
||||
const files = await fs.readdir(folderPath);
|
||||
assert.deepEqual(files, []);
|
||||
|
||||
await removeTempDirectory();
|
||||
});
|
||||
|
||||
test("create subfolder via set() with empty tree value", async () => {
|
||||
await createTempDirectory();
|
||||
|
||||
// Write out new, empty folder called "empty".
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set("empty", new ObjectTree({}));
|
||||
|
||||
// Verify folder exists and has no contents.
|
||||
const folderPath = path.join(tempDirectory, "empty");
|
||||
const stats = await fs.stat(folderPath);
|
||||
assert(stats.isDirectory());
|
||||
const files = await fs.readdir(folderPath);
|
||||
assert.deepEqual(files, []);
|
||||
|
||||
await removeTempDirectory();
|
||||
});
|
||||
|
||||
test("can write out subfolder via set()", async () => {
|
||||
await createTempDirectory();
|
||||
|
||||
// Create a tiny set of "files".
|
||||
const obj = {
|
||||
file1: "This is the first file.",
|
||||
subfolder: {
|
||||
file2: "This is the second file.",
|
||||
},
|
||||
};
|
||||
|
||||
// Write out files as a new folder called "folder".
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set("folder", obj);
|
||||
|
||||
// Read them back in.
|
||||
const actualFiles = await tempFiles.get("folder");
|
||||
const strings = Tree.map(actualFiles, {
|
||||
deep: true,
|
||||
value: (buffer) => textDecoder.decode(buffer),
|
||||
});
|
||||
const plain = await Tree.plain(strings);
|
||||
assert.deepEqual(plain, obj);
|
||||
|
||||
await removeTempDirectory();
|
||||
});
|
||||
|
||||
test("can delete a file via set()", async () => {
|
||||
await createTempDirectory();
|
||||
const tempFile = path.join(tempDirectory, "file");
|
||||
await fs.writeFile(tempFile, "");
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set("file", undefined);
|
||||
let stats;
|
||||
try {
|
||||
stats = await fs.stat(tempFile);
|
||||
} catch (/** @type {any} */ error) {
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
assert(stats === undefined);
|
||||
await removeTempDirectory();
|
||||
});
|
||||
|
||||
test("can delete a folder via set()", async () => {
|
||||
await createTempDirectory();
|
||||
const folder = path.join(tempDirectory, "folder");
|
||||
await fs.mkdir(folder);
|
||||
const tempFiles = new FileTree(tempDirectory);
|
||||
await tempFiles.set("folder", undefined);
|
||||
let stats;
|
||||
try {
|
||||
stats = await fs.stat(folder);
|
||||
} catch (/** @type {any} */ error) {
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
assert(stats === undefined);
|
||||
await removeTempDirectory();
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture(fixturePath) {
|
||||
return new FileTree(path.join(dirname, fixturePath));
|
||||
}
|
||||
|
||||
async function createTempDirectory() {
|
||||
await fs.mkdir(tempDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
async function removeTempDirectory() {
|
||||
await fs.rm(tempDirectory, { recursive: true });
|
||||
}
|
||||
46
node_modules/@weborigami/async-tree/test/drivers/FunctionTree.test.js
generated
vendored
Normal file
46
node_modules/@weborigami/async-tree/test/drivers/FunctionTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import FunctionTree from "../../src/drivers/FunctionTree.js";
|
||||
|
||||
describe("FunctionTree", async () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.deepEqual(Array.from(await fixture.keys()), [
|
||||
"Alice.md",
|
||||
"Bob.md",
|
||||
"Carol.md",
|
||||
]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const fixture = createFixture();
|
||||
const alice = await fixture.get("Alice.md");
|
||||
assert.equal(alice, "Hello, **Alice**.");
|
||||
});
|
||||
|
||||
test("getting a value from function with multiple arguments curries the function", async () => {
|
||||
const fixture = new FunctionTree((a, b, c) => a + b + c);
|
||||
const fnA = await fixture.get(1);
|
||||
const fnAB = await fnA.get(2);
|
||||
const result = await fnAB.get(3);
|
||||
assert.equal(result, 6);
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture() {
|
||||
return new FunctionTree(
|
||||
(key) => {
|
||||
if (key?.endsWith?.(".md")) {
|
||||
const name = key.slice(0, -3);
|
||||
return `Hello, **${name}**.`;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
["Alice.md", "Bob.md", "Carol.md"]
|
||||
);
|
||||
}
|
||||
59
node_modules/@weborigami/async-tree/test/drivers/MapTree.test.js
generated
vendored
Normal file
59
node_modules/@weborigami/async-tree/test/drivers/MapTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import MapTree from "../../src/drivers/MapTree.js";
|
||||
import * as symbols from "../../src/symbols.js";
|
||||
|
||||
describe("MapTree", () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.deepEqual(Array.from(await fixture.keys()), ["a", "b", "c"]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const fixture = createFixture();
|
||||
const a = await fixture.get("a");
|
||||
assert.equal(a, 1);
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const map = new Map([["more", new Map([["a", 1]])]]);
|
||||
const fixture = new MapTree(map);
|
||||
const more = await fixture.get("more");
|
||||
assert.equal(more[symbols.parent], fixture);
|
||||
});
|
||||
|
||||
test("adds trailing slashes to keys for subtrees", async () => {
|
||||
const tree = new MapTree([
|
||||
["a", 1],
|
||||
["subtree", new MapTree([["b", 2]])],
|
||||
]);
|
||||
const keys = Array.from(await tree.keys());
|
||||
assert.deepEqual(keys, ["a", "subtree/"]);
|
||||
});
|
||||
|
||||
test("can retrieve values with optional trailing slash", async () => {
|
||||
const subtree = new MapTree([["b", 2]]);
|
||||
const tree = new MapTree([
|
||||
["a", 1],
|
||||
["subtree", subtree],
|
||||
]);
|
||||
assert.equal(await tree.get("a"), 1);
|
||||
assert.equal(await tree.get("a/"), 1);
|
||||
assert.equal(await tree.get("subtree"), subtree);
|
||||
assert.equal(await tree.get("subtree/"), subtree);
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.equal(await fixture.get("d"), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture() {
|
||||
const map = new Map([
|
||||
["a", 1],
|
||||
["b", 2],
|
||||
["c", 3],
|
||||
]);
|
||||
return new MapTree(map);
|
||||
}
|
||||
156
node_modules/@weborigami/async-tree/test/drivers/ObjectTree.test.js
generated
vendored
Normal file
156
node_modules/@weborigami/async-tree/test/drivers/ObjectTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { ObjectTree, Tree } from "../../src/internal.js";
|
||||
import * as symbols from "../../src/symbols.js";
|
||||
|
||||
describe("ObjectTree", () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.deepEqual(Array.from(await fixture.keys()), [
|
||||
"Alice.md",
|
||||
"Bob.md",
|
||||
"Carol.md",
|
||||
]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const fixture = createFixture();
|
||||
const alice = await fixture.get("Alice.md");
|
||||
assert.equal(alice, "Hello, **Alice**.");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = createFixture();
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const fixture = createFixture();
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test("can set a value", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
});
|
||||
|
||||
// Update existing key
|
||||
await tree.set("a", 4);
|
||||
|
||||
// Delete key
|
||||
await tree.set("b", undefined);
|
||||
|
||||
// Overwrite key with trailing slash
|
||||
await tree.set("c/", {});
|
||||
|
||||
// New key
|
||||
await tree.set("d", 5);
|
||||
|
||||
assert.deepEqual(await Tree.entries(tree), [
|
||||
["a", 4],
|
||||
["c/", {}],
|
||||
["d", 5],
|
||||
]);
|
||||
});
|
||||
|
||||
test("can wrap a class instance", async () => {
|
||||
class Foo {
|
||||
constructor() {
|
||||
this.a = 1;
|
||||
}
|
||||
|
||||
get prop() {
|
||||
return this._prop;
|
||||
}
|
||||
set prop(prop) {
|
||||
this._prop = prop;
|
||||
}
|
||||
}
|
||||
class Bar extends Foo {
|
||||
method() {}
|
||||
}
|
||||
const bar = new Bar();
|
||||
/** @type {any} */ (bar).extra = "Hello";
|
||||
const fixture = new ObjectTree(bar);
|
||||
assert.deepEqual(await Tree.entries(fixture), [
|
||||
["a", 1],
|
||||
["extra", "Hello"],
|
||||
["prop", undefined],
|
||||
]);
|
||||
assert.equal(await fixture.get("a"), 1);
|
||||
await fixture.set("prop", "Goodbye");
|
||||
assert.equal(bar.prop, "Goodbye");
|
||||
assert.equal(await fixture.get("prop"), "Goodbye");
|
||||
});
|
||||
|
||||
test("sets parent symbol on subobjects", async () => {
|
||||
const fixture = new ObjectTree({
|
||||
sub: {},
|
||||
});
|
||||
const sub = await fixture.get("sub");
|
||||
assert.equal(sub[symbols.parent], fixture);
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const fixture = new ObjectTree({
|
||||
a: 1,
|
||||
more: new ObjectTree({
|
||||
b: 2,
|
||||
}),
|
||||
});
|
||||
const more = await fixture.get("more");
|
||||
assert.equal(more.parent, fixture);
|
||||
});
|
||||
|
||||
test("adds trailing slashes to keys for subtrees", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a1: 1,
|
||||
a2: new ObjectTree({
|
||||
b1: 2,
|
||||
}),
|
||||
a3: 3,
|
||||
a4: new ObjectTree({
|
||||
b2: 4,
|
||||
}),
|
||||
});
|
||||
const keys = Array.from(await tree.keys());
|
||||
assert.deepEqual(keys, ["a1", "a2/", "a3", "a4/"]);
|
||||
});
|
||||
|
||||
test("can retrieve values with optional trailing slash", async () => {
|
||||
const subtree = {
|
||||
async get(key) {},
|
||||
async keys() {},
|
||||
};
|
||||
const tree = new ObjectTree({
|
||||
a: 1,
|
||||
subtree,
|
||||
});
|
||||
assert.equal(await tree.get("a"), 1);
|
||||
assert.equal(await tree.get("a/"), 1);
|
||||
assert.equal(await tree.get("subtree"), subtree);
|
||||
assert.equal(await tree.get("subtree/"), subtree);
|
||||
});
|
||||
|
||||
test("method on an object is bound to the object", async () => {
|
||||
const n = new Number(123);
|
||||
const tree = new ObjectTree(n);
|
||||
const method = await tree.get("toString");
|
||||
assert.equal(method(), "123");
|
||||
});
|
||||
});
|
||||
|
||||
function createFixture() {
|
||||
return new ObjectTree({
|
||||
"Alice.md": "Hello, **Alice**.",
|
||||
"Bob.md": "Hello, **Bob**.",
|
||||
"Carol.md": "Hello, **Carol**.",
|
||||
});
|
||||
}
|
||||
44
node_modules/@weborigami/async-tree/test/drivers/SetTree.test.js
generated
vendored
Normal file
44
node_modules/@weborigami/async-tree/test/drivers/SetTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import SetTree from "../../src/drivers/SetTree.js";
|
||||
import { ObjectTree } from "../../src/internal.js";
|
||||
|
||||
describe("SetTree", () => {
|
||||
test("can get the keys of the tree", async () => {
|
||||
const set = new Set(["a", "b", "c"]);
|
||||
const fixture = new SetTree(set);
|
||||
assert.deepEqual(Array.from(await fixture.keys()), [0, 1, 2]);
|
||||
});
|
||||
|
||||
test("can get the value for a key", async () => {
|
||||
const set = new Set(["a", "b", "c"]);
|
||||
const fixture = new SetTree(set);
|
||||
const a = await fixture.get(0);
|
||||
assert.equal(a, "a");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const set = new Set(["a", "b", "c"]);
|
||||
const fixture = new SetTree(set);
|
||||
assert.equal(await fixture.get(3), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const set = new Set(["a", "b", "c"]);
|
||||
const fixture = new SetTree(set);
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test("sets parent on subtrees", async () => {
|
||||
const set = new Set();
|
||||
set.add(new ObjectTree({}));
|
||||
const fixture = new SetTree(set);
|
||||
const subtree = await fixture.get(0);
|
||||
assert.equal(subtree.parent, fixture);
|
||||
});
|
||||
});
|
||||
92
node_modules/@weborigami/async-tree/test/drivers/SiteTree.test.js
generated
vendored
Normal file
92
node_modules/@weborigami/async-tree/test/drivers/SiteTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import assert from "node:assert";
|
||||
import { beforeEach, describe, mock, test } from "node:test";
|
||||
import SiteTree from "../../src/drivers/SiteTree.js";
|
||||
|
||||
const textDecoder = new TextDecoder();
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
const mockHost = "https://mock";
|
||||
|
||||
const mockResponses = {
|
||||
"/about": {
|
||||
redirected: true,
|
||||
status: 301,
|
||||
url: "https://mock/about/",
|
||||
},
|
||||
"/about/Alice.html": {
|
||||
data: "Hello, Alice!",
|
||||
},
|
||||
"/about/Bob.html": {
|
||||
data: "Hello, Bob!",
|
||||
},
|
||||
"/about/Carol.html": {
|
||||
data: "Hello, Carol!",
|
||||
},
|
||||
"/index.html": {
|
||||
data: "Home page",
|
||||
},
|
||||
};
|
||||
|
||||
describe("SiteTree", () => {
|
||||
beforeEach(() => {
|
||||
mock.method(global, "fetch", mockFetch);
|
||||
});
|
||||
|
||||
test("returns an empty array as the keys of a tree", async () => {
|
||||
const fixture = new SiteTree(mockHost);
|
||||
const keys = await fixture.keys();
|
||||
assert.deepEqual(Array.from(keys), []);
|
||||
});
|
||||
|
||||
test("can get a plain value for a key", async () => {
|
||||
const fixture = new SiteTree(mockHost);
|
||||
const arrayBuffer = await fixture.get("index.html");
|
||||
const text = textDecoder.decode(arrayBuffer);
|
||||
assert.equal(text, "Home page");
|
||||
});
|
||||
|
||||
test("immediately return a new tree for a key with a trailing slash", async () => {
|
||||
const fixture = new SiteTree(mockHost);
|
||||
const about = await fixture.get("about/");
|
||||
assert(about instanceof SiteTree);
|
||||
assert.equal(about.href, "https://mock/about/");
|
||||
});
|
||||
|
||||
test("getting an unsupported key returns undefined", async () => {
|
||||
const fixture = new SiteTree(mockHost);
|
||||
assert.equal(await fixture.get("xyz"), undefined);
|
||||
});
|
||||
|
||||
test("getting a null/undefined key throws an exception", async () => {
|
||||
const fixture = new SiteTree(mockHost);
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(null);
|
||||
});
|
||||
await assert.rejects(async () => {
|
||||
await fixture.get(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function mockFetch(href) {
|
||||
if (!href.startsWith(mockHost)) {
|
||||
return { status: 404 };
|
||||
}
|
||||
const path = href.slice(mockHost.length);
|
||||
const mockedResponse = mockResponses[path];
|
||||
if (mockedResponse) {
|
||||
return Object.assign(
|
||||
{
|
||||
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: () => mockedResponse.data,
|
||||
},
|
||||
mockedResponse
|
||||
);
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
status: 404,
|
||||
};
|
||||
}
|
||||
121
node_modules/@weborigami/async-tree/test/drivers/calendarTree.test.js
generated
vendored
Normal file
121
node_modules/@weborigami/async-tree/test/drivers/calendarTree.test.js
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import calendar from "../../src/drivers/calendarTree.js";
|
||||
import { toPlainValue } from "../../src/utilities.js";
|
||||
|
||||
describe("calendarTree", () => {
|
||||
test("without a start or end, returns a tree for today", async () => {
|
||||
const tree = calendar({
|
||||
value: (year, month, day) => `${year}-${month}-${day}`,
|
||||
});
|
||||
const plain = await toPlainValue(tree);
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = (today.getMonth() + 1).toString().padStart(2, "0");
|
||||
const day = today.getDate().toString().padStart(2, "0");
|
||||
assert.deepEqual(plain, {
|
||||
[year]: {
|
||||
[month]: {
|
||||
[day]: `${year}-${month}-${day}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("returns a tree for a month range", async () => {
|
||||
const tree = calendar({
|
||||
start: "2025-01",
|
||||
end: "2025-02",
|
||||
value: (year, month, day) => `${year}-${month}-${day}`,
|
||||
});
|
||||
const plain = await toPlainValue(tree);
|
||||
assert.deepEqual(plain, {
|
||||
2025: {
|
||||
"01": {
|
||||
"01": "2025-01-01",
|
||||
"02": "2025-01-02",
|
||||
"03": "2025-01-03",
|
||||
"04": "2025-01-04",
|
||||
"05": "2025-01-05",
|
||||
"06": "2025-01-06",
|
||||
"07": "2025-01-07",
|
||||
"08": "2025-01-08",
|
||||
"09": "2025-01-09",
|
||||
10: "2025-01-10",
|
||||
11: "2025-01-11",
|
||||
12: "2025-01-12",
|
||||
13: "2025-01-13",
|
||||
14: "2025-01-14",
|
||||
15: "2025-01-15",
|
||||
16: "2025-01-16",
|
||||
17: "2025-01-17",
|
||||
18: "2025-01-18",
|
||||
19: "2025-01-19",
|
||||
20: "2025-01-20",
|
||||
21: "2025-01-21",
|
||||
22: "2025-01-22",
|
||||
23: "2025-01-23",
|
||||
24: "2025-01-24",
|
||||
25: "2025-01-25",
|
||||
26: "2025-01-26",
|
||||
27: "2025-01-27",
|
||||
28: "2025-01-28",
|
||||
29: "2025-01-29",
|
||||
30: "2025-01-30",
|
||||
31: "2025-01-31",
|
||||
},
|
||||
"02": {
|
||||
"01": "2025-02-01",
|
||||
"02": "2025-02-02",
|
||||
"03": "2025-02-03",
|
||||
"04": "2025-02-04",
|
||||
"05": "2025-02-05",
|
||||
"06": "2025-02-06",
|
||||
"07": "2025-02-07",
|
||||
"08": "2025-02-08",
|
||||
"09": "2025-02-09",
|
||||
10: "2025-02-10",
|
||||
11: "2025-02-11",
|
||||
12: "2025-02-12",
|
||||
13: "2025-02-13",
|
||||
14: "2025-02-14",
|
||||
15: "2025-02-15",
|
||||
16: "2025-02-16",
|
||||
17: "2025-02-17",
|
||||
18: "2025-02-18",
|
||||
19: "2025-02-19",
|
||||
20: "2025-02-20",
|
||||
21: "2025-02-21",
|
||||
22: "2025-02-22",
|
||||
23: "2025-02-23",
|
||||
24: "2025-02-24",
|
||||
25: "2025-02-25",
|
||||
26: "2025-02-26",
|
||||
27: "2025-02-27",
|
||||
28: "2025-02-28",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("returns a tree for a day range", async () => {
|
||||
const tree = calendar({
|
||||
start: "2025-02-27",
|
||||
end: "2025-03-02",
|
||||
value: (year, month, day) => `${year}-${month}-${day}`,
|
||||
});
|
||||
const plain = await toPlainValue(tree);
|
||||
assert.deepEqual(plain, {
|
||||
2025: {
|
||||
"02": {
|
||||
27: "2025-02-27",
|
||||
28: "2025-02-28",
|
||||
},
|
||||
"03": {
|
||||
"01": "2025-03-01",
|
||||
"02": "2025-03-02",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Alice.md
generated
vendored
Normal file
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Alice.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, **Alice**.
|
||||
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Bob.md
generated
vendored
Normal file
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Bob.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, **Bob**.
|
||||
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Carol.md
generated
vendored
Normal file
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/Carol.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, **Carol**.
|
||||
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/subfolder/README.md
generated
vendored
Normal file
1
node_modules/@weborigami/async-tree/test/drivers/fixtures/markdown/subfolder/README.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This file exists to force the creation of the parent folder.
|
||||
41
node_modules/@weborigami/async-tree/test/extension.test.js
generated
vendored
Normal file
41
node_modules/@weborigami/async-tree/test/extension.test.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { extname, match, replace } from "../src/extension.js";
|
||||
|
||||
describe("extension", () => {
|
||||
test("extname", () => {
|
||||
assert.equal(extname(".\\"), "");
|
||||
assert.equal(extname("..\\"), ".\\");
|
||||
assert.equal(extname("file.ext\\"), ".ext\\");
|
||||
assert.equal(extname("file.ext\\\\"), ".ext\\\\");
|
||||
assert.equal(extname("file\\"), "");
|
||||
assert.equal(extname("file\\\\"), "");
|
||||
assert.equal(extname("file.\\"), ".\\");
|
||||
assert.equal(extname("file.\\\\"), ".\\\\");
|
||||
});
|
||||
|
||||
test("match", () => {
|
||||
assert.equal(match("file.md", ".md"), "file");
|
||||
assert.equal(match("file.md", ".txt"), null);
|
||||
assert.equal(match("file.md/", ".md"), "file/");
|
||||
assert.equal(match("file", ""), "file");
|
||||
assert.equal(match("file", "/"), null);
|
||||
assert.equal(match("file/", "/"), "file");
|
||||
});
|
||||
|
||||
test("match can handle multi-part extensions", () => {
|
||||
assert.equal(match("foo.ori.html", ".ori.html"), "foo");
|
||||
assert.equal(match("foo.ori.html", ".html"), "foo.ori");
|
||||
assert.equal(match("foo.ori.html", ".txt"), null);
|
||||
assert.equal(match("foo.ori.html/", ".ori.html"), "foo/");
|
||||
});
|
||||
|
||||
test("replace", () => {
|
||||
assert.equal(replace("file.md", ".md", ".html"), "file.html");
|
||||
assert.equal(replace("file.md", ".txt", ".html"), "file.md");
|
||||
assert.equal(replace("file.md/", ".md", ".html"), "file.html/");
|
||||
assert.equal(replace("folder/", "", ".html"), "folder.html");
|
||||
assert.equal(replace("folder", "/", ".html"), "folder");
|
||||
assert.equal(replace("folder/", "/", ".html"), "folder.html");
|
||||
});
|
||||
});
|
||||
15
node_modules/@weborigami/async-tree/test/jsonKeys.test.js
generated
vendored
Normal file
15
node_modules/@weborigami/async-tree/test/jsonKeys.test.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { DeepObjectTree } from "@weborigami/async-tree";
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import * as jsonKeys from "../src/jsonKeys.js";
|
||||
|
||||
describe("jsonKeys", () => {
|
||||
test("stringifies JSON Keys", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
about: {},
|
||||
"index.html": "Home",
|
||||
});
|
||||
const json = await jsonKeys.stringify(tree);
|
||||
assert.strictEqual(json, '["about/","index.html"]');
|
||||
});
|
||||
});
|
||||
63
node_modules/@weborigami/async-tree/test/operations/cache.test.js
generated
vendored
Normal file
63
node_modules/@weborigami/async-tree/test/operations/cache.test.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
|
||||
import cache from "../../src/operations/cache.js";
|
||||
|
||||
describe("cache", () => {
|
||||
test("caches reads of values from one tree into another", async () => {
|
||||
const objectCache = new ObjectTree({});
|
||||
const fixture = cache(
|
||||
new DeepObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
more: {
|
||||
d: 4,
|
||||
},
|
||||
}),
|
||||
objectCache
|
||||
);
|
||||
|
||||
const keys = [...(await fixture.keys())];
|
||||
assert.deepEqual(keys, ["a", "b", "c", "more/"]);
|
||||
|
||||
assert.equal(await objectCache.get("a"), undefined);
|
||||
assert.equal(await fixture.get("a"), 1);
|
||||
assert.equal(await objectCache.get("a"), 1); // Now in cache
|
||||
|
||||
assert.equal(await objectCache.get("b"), undefined);
|
||||
assert.equal(await fixture.get("b"), 2);
|
||||
assert.equal(await objectCache.get("b"), 2);
|
||||
|
||||
assert.equal(await objectCache.get("more"), undefined);
|
||||
const more = await fixture.get("more");
|
||||
assert.deepEqual([...(await more.keys())], ["d"]);
|
||||
assert.equal(await more.get("d"), 4);
|
||||
const moreCache = await objectCache.get("more");
|
||||
assert.equal(await moreCache.get("d"), 4);
|
||||
});
|
||||
|
||||
test("if a cache filter is supplied, it only caches values whose keys match the filter", async () => {
|
||||
const objectCache = new ObjectTree({});
|
||||
const fixture = cache(
|
||||
Tree.from({
|
||||
"a.txt": "a",
|
||||
"b.txt": "b",
|
||||
}),
|
||||
objectCache,
|
||||
Tree.from({
|
||||
"a.txt": true,
|
||||
})
|
||||
);
|
||||
|
||||
// Access some values to populate the cache.
|
||||
assert.equal(await fixture.get("a.txt"), "a");
|
||||
assert.equal(await fixture.get("b.txt"), "b");
|
||||
|
||||
// The a.txt value should be cached because it matches the filter.
|
||||
assert.equal(await objectCache.get("a.txt"), "a");
|
||||
|
||||
// The b.txt value should not be cached because it does not match the filter.
|
||||
assert.equal(await objectCache.get("b.txt"), undefined);
|
||||
});
|
||||
});
|
||||
90
node_modules/@weborigami/async-tree/test/operations/cachedKeyFunctions.test.js
generated
vendored
Normal file
90
node_modules/@weborigami/async-tree/test/operations/cachedKeyFunctions.test.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, ObjectTree } from "../../src/internal.js";
|
||||
import cachedKeyFunctions from "../../src/operations/cachedKeyFunctions.js";
|
||||
|
||||
describe("cachedKeyFunctions", () => {
|
||||
test("maps keys with caching", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
});
|
||||
|
||||
let callCount = 0;
|
||||
const addUnderscore = async (sourceKey, tree) => {
|
||||
callCount++;
|
||||
return `_${sourceKey}`;
|
||||
};
|
||||
|
||||
const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
|
||||
|
||||
assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
|
||||
assert.equal(callCount, 1);
|
||||
assert.equal(await inverseKey("_a", tree), "a");
|
||||
assert.equal(callCount, 1);
|
||||
assert.equal(await inverseKey("_b", tree), "b"); // Cache miss
|
||||
assert.equal(callCount, 2);
|
||||
|
||||
assert.equal(await key("a", tree), "_a");
|
||||
assert.equal(await key("a", tree), "_a");
|
||||
assert.equal(await key("b", tree), "_b");
|
||||
assert.equal(callCount, 2);
|
||||
|
||||
// `c` isn't in tree, so we should get undefined.
|
||||
assert.equal(await inverseKey("_c", tree), undefined);
|
||||
// But key mapping is still possible.
|
||||
assert.equal(await key("c", tree), "_c");
|
||||
// And now we have a cache hit.
|
||||
assert.equal(await inverseKey("_c", tree), "c");
|
||||
assert.equal(callCount, 3);
|
||||
});
|
||||
|
||||
test("maps keys with caching and deep option", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: "letter a",
|
||||
b: {
|
||||
c: "letter c",
|
||||
},
|
||||
});
|
||||
|
||||
let callCount = 0;
|
||||
const addUnderscore = async (sourceKey, tree) => {
|
||||
callCount++;
|
||||
return `_${sourceKey}`;
|
||||
};
|
||||
|
||||
const { inverseKey, key } = cachedKeyFunctions(addUnderscore, true);
|
||||
|
||||
assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
|
||||
assert.equal(await inverseKey("_a", tree), "a");
|
||||
assert.equal(callCount, 1);
|
||||
|
||||
// Subtree key left alone
|
||||
assert.equal(await inverseKey("_b", tree), undefined);
|
||||
assert.equal(await inverseKey("b", tree), "b");
|
||||
assert.equal(await inverseKey("b/", tree), "b/");
|
||||
assert.equal(callCount, 1);
|
||||
|
||||
assert.equal(await key("a", tree), "_a");
|
||||
assert.equal(await key("a", tree), "_a");
|
||||
assert.equal(callCount, 1);
|
||||
|
||||
assert.equal(await key("b/", tree), "b/");
|
||||
assert.equal(await key("b", tree), "b");
|
||||
assert.equal(callCount, 1);
|
||||
});
|
||||
|
||||
test("preserves trailing slashes", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: "letter a",
|
||||
});
|
||||
const addUnderscore = async (sourceKey) => `_${sourceKey}`;
|
||||
const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
|
||||
|
||||
assert.equal(await key("a/", tree), "_a/");
|
||||
assert.equal(await key("a", tree), "_a");
|
||||
|
||||
assert.equal(await inverseKey("_a/", tree), "a/");
|
||||
assert.equal(await inverseKey("_a", tree), "a");
|
||||
});
|
||||
});
|
||||
34
node_modules/@weborigami/async-tree/test/operations/concat.test.js
generated
vendored
Normal file
34
node_modules/@weborigami/async-tree/test/operations/concat.test.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import FunctionTree from "../../src/drivers/FunctionTree.js";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import concat from "../../src/operations/concat.js";
|
||||
|
||||
describe("concat", () => {
|
||||
test("concatenates deep tree values", async () => {
|
||||
const tree = Tree.from({
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "C",
|
||||
more: {
|
||||
d: "D",
|
||||
e: "E",
|
||||
},
|
||||
});
|
||||
const result = await concat.call(null, tree);
|
||||
assert.equal(result, "ABCDE");
|
||||
});
|
||||
|
||||
test("concatenates deep tree-like values", async () => {
|
||||
const letters = ["a", "b", "c"];
|
||||
const specimens = new FunctionTree(
|
||||
(letter) => ({
|
||||
lowercase: letter,
|
||||
uppercase: letter.toUpperCase(),
|
||||
}),
|
||||
letters
|
||||
);
|
||||
const result = await concat.call(null, specimens);
|
||||
assert.equal(result, "aAbBcC");
|
||||
});
|
||||
});
|
||||
42
node_modules/@weborigami/async-tree/test/operations/deepMerge.test.js
generated
vendored
Normal file
42
node_modules/@weborigami/async-tree/test/operations/deepMerge.test.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
||||
import mergeDeep from "../../src/operations/deepMerge.js";
|
||||
|
||||
describe("mergeDeep", () => {
|
||||
test("can merge deep", async () => {
|
||||
const fixture = mergeDeep(
|
||||
new DeepObjectTree({
|
||||
a: {
|
||||
b: 0, // Will be obscured by `b` below
|
||||
c: {
|
||||
d: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
new DeepObjectTree({
|
||||
a: {
|
||||
b: 1,
|
||||
c: {
|
||||
e: 3,
|
||||
},
|
||||
f: 4,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
a: {
|
||||
b: 1,
|
||||
c: {
|
||||
d: 2,
|
||||
e: 3,
|
||||
},
|
||||
f: 4,
|
||||
},
|
||||
});
|
||||
|
||||
// Parent of a subvalue is the merged tree
|
||||
const a = await fixture.get("a");
|
||||
assert.equal(a.parent, fixture);
|
||||
});
|
||||
});
|
||||
24
node_modules/@weborigami/async-tree/test/operations/deepReverse.test.js
generated
vendored
Normal file
24
node_modules/@weborigami/async-tree/test/operations/deepReverse.test.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import deepReverse from "../../src/operations/deepReverse.js";
|
||||
|
||||
describe("deepReverse", () => {
|
||||
test("reverses keys at all levels of a tree", async () => {
|
||||
const tree = {
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2,
|
||||
d: 3,
|
||||
},
|
||||
};
|
||||
const reversed = deepReverse.call(null, tree);
|
||||
assert.deepEqual(await Tree.plain(reversed), {
|
||||
b: {
|
||||
d: 3,
|
||||
c: 2,
|
||||
},
|
||||
a: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
22
node_modules/@weborigami/async-tree/test/operations/deepTake.test.js
generated
vendored
Normal file
22
node_modules/@weborigami/async-tree/test/operations/deepTake.test.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import deepTake from "../../src/operations/deepTake.js";
|
||||
|
||||
describe("deepTake", () => {
|
||||
test("traverses deeply and returns a limited number of items", async () => {
|
||||
const tree = {
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2,
|
||||
d: {
|
||||
e: 3,
|
||||
},
|
||||
f: 4,
|
||||
},
|
||||
g: 5,
|
||||
};
|
||||
const result = await deepTake(tree, 4);
|
||||
assert.deepEqual(await Tree.plain(result), [1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
28
node_modules/@weborigami/async-tree/test/operations/deepValues.test.js
generated
vendored
Normal file
28
node_modules/@weborigami/async-tree/test/operations/deepValues.test.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import deepValues from "../../src/operations/deepValues.js";
|
||||
|
||||
describe("deepValues", () => {
|
||||
test("returns in-order array of a tree's values", async () => {
|
||||
const tree = {
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2,
|
||||
d: {
|
||||
e: 3,
|
||||
},
|
||||
},
|
||||
f: {
|
||||
g: 4,
|
||||
},
|
||||
};
|
||||
const values = await deepValues(tree);
|
||||
assert.deepEqual(values, [1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
test("returns in-order array of values in nested arrays", async () => {
|
||||
const tree = [1, [2, 3], 4];
|
||||
const values = await deepValues(tree);
|
||||
assert.deepEqual(values, [1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
23
node_modules/@weborigami/async-tree/test/operations/deepValuesIterator.test.js
generated
vendored
Normal file
23
node_modules/@weborigami/async-tree/test/operations/deepValuesIterator.test.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { ObjectTree } from "../../src/internal.js";
|
||||
import deepValuesIterator from "../../src/operations/deepValuesIterator.js";
|
||||
|
||||
describe("deepValuesIterator", () => {
|
||||
test("returns an iterator of a tree's deep values", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
more: {
|
||||
c: 3,
|
||||
d: 4,
|
||||
},
|
||||
});
|
||||
const values = [];
|
||||
// The tree will be shallow, but we'll ask to expand the values.
|
||||
for await (const value of deepValuesIterator(tree, { expand: true })) {
|
||||
values.push(value);
|
||||
}
|
||||
assert.deepEqual(values, [1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
54
node_modules/@weborigami/async-tree/test/operations/group.test.js
generated
vendored
Normal file
54
node_modules/@weborigami/async-tree/test/operations/group.test.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import group from "../../src/operations/group.js";
|
||||
|
||||
describe("group transform", () => {
|
||||
test("groups an array using a group key function", async () => {
|
||||
const fonts = [
|
||||
{ name: "Aboreto", tags: ["Sans Serif"] },
|
||||
{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] },
|
||||
{ name: "Alegreya", tags: ["Serif"] },
|
||||
{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
|
||||
];
|
||||
const tree = Tree.from(fonts);
|
||||
const grouped = await group(tree, (value, key, tree) => value.tags);
|
||||
assert.deepEqual(await Tree.plain(grouped), {
|
||||
Geometric: [{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] }],
|
||||
Grotesque: [{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] }],
|
||||
"Sans Serif": [
|
||||
{ name: "Aboreto", tags: ["Sans Serif"] },
|
||||
{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] },
|
||||
{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
|
||||
],
|
||||
Serif: [{ name: "Alegreya", tags: ["Serif"] }],
|
||||
});
|
||||
});
|
||||
|
||||
test("groups an object using a group key function", async () => {
|
||||
const fonts = {
|
||||
Aboreto: { tags: ["Sans Serif"] },
|
||||
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
|
||||
Alegreya: { tags: ["Serif"] },
|
||||
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
|
||||
};
|
||||
const tree = Tree.from(fonts);
|
||||
const grouped = await group(tree, (value, key, tree) => value.tags);
|
||||
assert.deepEqual(await Tree.plain(grouped), {
|
||||
Geometric: {
|
||||
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
|
||||
},
|
||||
Grotesque: {
|
||||
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
|
||||
},
|
||||
"Sans Serif": {
|
||||
Aboreto: { tags: ["Sans Serif"] },
|
||||
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
|
||||
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
|
||||
},
|
||||
Serif: {
|
||||
Alegreya: { tags: ["Serif"] },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
17
node_modules/@weborigami/async-tree/test/operations/invokeFunctions.test.js
generated
vendored
Normal file
17
node_modules/@weborigami/async-tree/test/operations/invokeFunctions.test.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import invokeFunctions from "../../src/operations/invokeFunctions.js";
|
||||
|
||||
describe("invokeFunctions", () => {
|
||||
test("invokes function values, leaves other values as is", async () => {
|
||||
const fixture = invokeFunctions({
|
||||
a: 1,
|
||||
b: () => 2,
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
a: 1,
|
||||
b: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
82
node_modules/@weborigami/async-tree/test/operations/keyFunctionsForExtensions.test.js
generated
vendored
Normal file
82
node_modules/@weborigami/async-tree/test/operations/keyFunctionsForExtensions.test.js
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { ObjectTree, Tree } from "../../src/internal.js";
|
||||
import keyFunctionsForExtensions from "../../src/operations/keyFunctionsForExtensions.js";
|
||||
import map from "../../src/operations/map.js";
|
||||
|
||||
describe("keyMapsForExtensions", () => {
|
||||
test("returns key functions that pass a matching key through", async () => {
|
||||
const { inverseKey, key } = keyFunctionsForExtensions({
|
||||
sourceExtension: ".txt",
|
||||
});
|
||||
assert.equal(await inverseKey("file.txt"), "file.txt");
|
||||
assert.equal(await inverseKey("file.txt/"), "file.txt");
|
||||
assert.equal(await key("file.txt"), "file.txt");
|
||||
assert.equal(await key("file.txt/"), "file.txt/");
|
||||
assert.equal(await inverseKey("file.foo"), undefined);
|
||||
assert.equal(await key("file.foo"), undefined);
|
||||
});
|
||||
|
||||
test("returns key functions that can map extensions", async () => {
|
||||
const { inverseKey, key } = keyFunctionsForExtensions({
|
||||
resultExtension: ".json",
|
||||
sourceExtension: ".md",
|
||||
});
|
||||
assert.equal(await inverseKey("file.json"), "file.md");
|
||||
assert.equal(await inverseKey("file.json/"), "file.md");
|
||||
assert.equal(await key("file.md"), "file.json");
|
||||
assert.equal(await key("file.md/"), "file.json/");
|
||||
assert.equal(await inverseKey("file.foo"), undefined);
|
||||
assert.equal(await key("file.foo"), undefined);
|
||||
});
|
||||
|
||||
test("key functions can handle a slash as an explicit extension", async () => {
|
||||
const { inverseKey, key } = keyFunctionsForExtensions({
|
||||
resultExtension: ".html",
|
||||
sourceExtension: "/",
|
||||
});
|
||||
assert.equal(await inverseKey("file.html"), "file/");
|
||||
assert.equal(await inverseKey("file.html/"), "file/");
|
||||
assert.equal(await key("file"), undefined);
|
||||
assert.equal(await key("file/"), "file.html");
|
||||
});
|
||||
|
||||
test("works with map to handle keys that end in a given resultExtension", async () => {
|
||||
const files = new ObjectTree({
|
||||
"file1.txt": "will be mapped",
|
||||
file2: "won't be mapped",
|
||||
"file3.foo": "won't be mapped",
|
||||
});
|
||||
const { inverseKey, key } = keyFunctionsForExtensions({
|
||||
sourceExtension: ".txt",
|
||||
});
|
||||
const fixture = map(files, {
|
||||
inverseKey,
|
||||
key,
|
||||
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
"file1.txt": "WILL BE MAPPED",
|
||||
});
|
||||
});
|
||||
|
||||
test("works with map to change a key's resultExtension", async () => {
|
||||
const files = new ObjectTree({
|
||||
"file1.txt": "will be mapped",
|
||||
file2: "won't be mapped",
|
||||
"file3.foo": "won't be mapped",
|
||||
});
|
||||
const { inverseKey, key } = keyFunctionsForExtensions({
|
||||
resultExtension: ".upper",
|
||||
sourceExtension: ".txt",
|
||||
});
|
||||
const fixture = map(files, {
|
||||
inverseKey,
|
||||
key,
|
||||
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
"file1.upper": "WILL BE MAPPED",
|
||||
});
|
||||
});
|
||||
});
|
||||
204
node_modules/@weborigami/async-tree/test/operations/map.test.js
generated
vendored
Normal file
204
node_modules/@weborigami/async-tree/test/operations/map.test.js
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import FunctionTree from "../../src/drivers/FunctionTree.js";
|
||||
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
|
||||
import map from "../../src/operations/map.js";
|
||||
import * as trailingSlash from "../../src/trailingSlash.js";
|
||||
|
||||
describe("map", () => {
|
||||
test("returns identity graph if no key or value function is supplied", async () => {
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
};
|
||||
const mapped = map(tree, {});
|
||||
assert.deepEqual(await Tree.plain(mapped), {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
});
|
||||
});
|
||||
|
||||
test("maps values", async () => {
|
||||
const tree = new ObjectTree({
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
c: undefined, // Won't be mapped
|
||||
});
|
||||
const mapped = map(tree, {
|
||||
value: (sourceValue, sourceKey, innerTree) => {
|
||||
assert(sourceKey === "a" || sourceKey === "b");
|
||||
assert.equal(innerTree, tree);
|
||||
return sourceValue.toUpperCase();
|
||||
},
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(mapped), {
|
||||
a: "LETTER A",
|
||||
b: "LETTER B",
|
||||
c: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test("interprets a single function argument as the value function", async () => {
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
};
|
||||
const uppercaseValues = map(tree, (sourceValue, sourceKey, tree) => {
|
||||
assert(sourceKey === "a" || sourceKey === "b");
|
||||
return sourceValue.toUpperCase();
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(uppercaseValues), {
|
||||
a: "LETTER A",
|
||||
b: "LETTER B",
|
||||
});
|
||||
});
|
||||
|
||||
test("maps keys using key and inverseKey", async () => {
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
};
|
||||
const underscoreKeys = map(tree, {
|
||||
key: addUnderscore,
|
||||
inverseKey: removeUnderscore,
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
||||
_a: "letter a",
|
||||
_b: "letter b",
|
||||
});
|
||||
});
|
||||
|
||||
test("maps keys and values", async () => {
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
};
|
||||
const underscoreKeysUppercaseValues = map(tree, {
|
||||
key: addUnderscore,
|
||||
inverseKey: removeUnderscore,
|
||||
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
|
||||
_a: "LETTER A",
|
||||
_b: "LETTER B",
|
||||
});
|
||||
});
|
||||
|
||||
test("a shallow map is applied to async subtrees too", async () => {
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
more: {
|
||||
b: "letter b",
|
||||
},
|
||||
};
|
||||
const underscoreKeys = map(tree, {
|
||||
key: async (sourceKey, tree) => `_${sourceKey}`,
|
||||
inverseKey: async (resultKey, tree) => resultKey.slice(1),
|
||||
value: async (sourceValue, sourceKey, tree) => sourceKey,
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
||||
_a: "a",
|
||||
_more: "more",
|
||||
});
|
||||
});
|
||||
|
||||
test("value can provide a default key and inverse key functions", async () => {
|
||||
const uppercase = (s) => s.toUpperCase();
|
||||
uppercase.key = addUnderscore;
|
||||
uppercase.inverseKey = removeUnderscore;
|
||||
const tree = {
|
||||
a: "letter a",
|
||||
b: "letter b",
|
||||
};
|
||||
const mapped = map(tree, uppercase);
|
||||
assert.deepEqual(await Tree.plain(mapped), {
|
||||
_a: "LETTER A",
|
||||
_b: "LETTER B",
|
||||
});
|
||||
});
|
||||
|
||||
test("deep maps values", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: "letter a",
|
||||
more: {
|
||||
b: "letter b",
|
||||
},
|
||||
});
|
||||
const uppercaseValues = map(tree, {
|
||||
deep: true,
|
||||
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(uppercaseValues), {
|
||||
a: "LETTER A",
|
||||
more: {
|
||||
b: "LETTER B",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("deep maps leaf keys", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: "letter a",
|
||||
more: {
|
||||
b: "letter b",
|
||||
},
|
||||
});
|
||||
const underscoreKeys = map(tree, {
|
||||
deep: true,
|
||||
key: addUnderscore,
|
||||
inverseKey: removeUnderscore,
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
||||
_a: "letter a",
|
||||
more: {
|
||||
_b: "letter b",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("deep maps leaf keys and values", async () => {
|
||||
const tree = new DeepObjectTree({
|
||||
a: "letter a",
|
||||
more: {
|
||||
b: "letter b",
|
||||
},
|
||||
});
|
||||
const underscoreKeysUppercaseValues = map(tree, {
|
||||
deep: true,
|
||||
key: addUnderscore,
|
||||
inverseKey: removeUnderscore,
|
||||
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
|
||||
_a: "LETTER A",
|
||||
more: {
|
||||
_b: "LETTER B",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("needsSourceValue can be set to false in cases where the value isn't necessary", async () => {
|
||||
let flag = false;
|
||||
const tree = new FunctionTree(() => {
|
||||
flag = true;
|
||||
}, ["a", "b", "c"]);
|
||||
const mapped = map(tree, {
|
||||
needsSourceValue: false,
|
||||
value: () => "X",
|
||||
});
|
||||
assert.deepEqual(await Tree.plain(mapped), {
|
||||
a: "X",
|
||||
b: "X",
|
||||
c: "X",
|
||||
});
|
||||
assert(!flag);
|
||||
});
|
||||
});
|
||||
|
||||
function addUnderscore(key) {
|
||||
return `_${key}`;
|
||||
}
|
||||
|
||||
function removeUnderscore(key) {
|
||||
return trailingSlash.has(key) ? key : key.slice(1);
|
||||
}
|
||||
64
node_modules/@weborigami/async-tree/test/operations/merge.test.js
generated
vendored
Normal file
64
node_modules/@weborigami/async-tree/test/operations/merge.test.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
|
||||
import merge from "../../src/operations/merge.js";
|
||||
import * as symbols from "../../src/symbols.js";
|
||||
|
||||
describe("merge", () => {
|
||||
test("performs a shallow merge", async () => {
|
||||
const fixture = merge(
|
||||
{
|
||||
a: 1,
|
||||
// Will be obscured by `b` that follows
|
||||
b: {
|
||||
c: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
b: {
|
||||
d: 3,
|
||||
},
|
||||
e: {
|
||||
f: 4,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
a: 1,
|
||||
b: {
|
||||
d: 3,
|
||||
},
|
||||
e: {
|
||||
f: 4,
|
||||
},
|
||||
});
|
||||
|
||||
// Merge is shallow, and last tree wins, so `b/c` doesn't exist
|
||||
const c = await Tree.traverse(fixture, "b", "c");
|
||||
assert.equal(c, undefined);
|
||||
|
||||
// Parent of a subvalue is the merged tree
|
||||
const b = await fixture.get("b");
|
||||
assert.equal(b[symbols.parent], fixture);
|
||||
});
|
||||
|
||||
test("subtree can overwrite a leaf node", async () => {
|
||||
const fixture = merge(
|
||||
new ObjectTree({
|
||||
a: 1,
|
||||
}),
|
||||
new DeepObjectTree({
|
||||
a: {
|
||||
b: 2,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.deepEqual([...(await fixture.keys())], ["a/"]);
|
||||
assert.deepEqual(await Tree.plain(fixture), {
|
||||
a: {
|
||||
b: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
25
node_modules/@weborigami/async-tree/test/operations/regExpKeys.test.js
generated
vendored
Normal file
25
node_modules/@weborigami/async-tree/test/operations/regExpKeys.test.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
||||
import regExpKeys from "../../src/operations/regExpKeys.js";
|
||||
|
||||
describe("regExpKeys", () => {
|
||||
test("matches keys using regular expressions", async () => {
|
||||
const fixture = await regExpKeys(
|
||||
new DeepObjectTree({
|
||||
a: true,
|
||||
"b.*": true,
|
||||
c: {
|
||||
d: true,
|
||||
"e*": true,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert(await Tree.traverse(fixture, "a"));
|
||||
assert(!(await Tree.traverse(fixture, "alice")));
|
||||
assert(await Tree.traverse(fixture, "bob"));
|
||||
assert(await Tree.traverse(fixture, "brenda"));
|
||||
assert(await Tree.traverse(fixture, "c", "d"));
|
||||
assert(await Tree.traverse(fixture, "c", "eee"));
|
||||
});
|
||||
});
|
||||
23
node_modules/@weborigami/async-tree/test/operations/reverse.test.js
generated
vendored
Normal file
23
node_modules/@weborigami/async-tree/test/operations/reverse.test.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import reverse from "../../src/operations/reverse.js";
|
||||
|
||||
describe("reverse", () => {
|
||||
test("reverses a tree's top-level keys", async () => {
|
||||
const tree = {
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "C",
|
||||
};
|
||||
const reversed = reverse.call(null, tree);
|
||||
// @ts-ignore
|
||||
assert.deepEqual(Array.from(await reversed.keys()), ["c", "b", "a"]);
|
||||
// @ts-ignore
|
||||
assert.deepEqual(await Tree.plain(reversed), {
|
||||
c: "C",
|
||||
b: "B",
|
||||
a: "A",
|
||||
});
|
||||
});
|
||||
});
|
||||
25
node_modules/@weborigami/async-tree/test/operations/scope.test.js
generated
vendored
Normal file
25
node_modules/@weborigami/async-tree/test/operations/scope.test.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { ObjectTree } from "../../src/internal.js";
|
||||
import scope from "../../src/operations/scope.js";
|
||||
|
||||
describe("scope", () => {
|
||||
test("gets the first defined value from the scope trees", async () => {
|
||||
const outer = new ObjectTree({
|
||||
a: 1,
|
||||
b: 2,
|
||||
});
|
||||
const inner = new ObjectTree({
|
||||
a: 3,
|
||||
});
|
||||
inner.parent = outer;
|
||||
const innerScope = scope(inner);
|
||||
assert.deepEqual([...(await innerScope.keys())], ["a", "b"]);
|
||||
// Inner tree has precedence
|
||||
assert.equal(await innerScope.get("a"), 3);
|
||||
// If tree doesn't have value, finds value from parent
|
||||
assert.equal(await innerScope.get("b"), 2);
|
||||
assert.equal(await innerScope.get("c"), undefined);
|
||||
assert.deepEqual(innerScope.trees, [inner, outer]);
|
||||
});
|
||||
});
|
||||
48
node_modules/@weborigami/async-tree/test/operations/sort.test.js
generated
vendored
Normal file
48
node_modules/@weborigami/async-tree/test/operations/sort.test.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import sort from "../../src/operations/sort.js";
|
||||
|
||||
describe("sort", () => {
|
||||
test("sorts keys using default sort order", async () => {
|
||||
const tree = Tree.from({
|
||||
file10: null,
|
||||
file1: null,
|
||||
file9: null,
|
||||
});
|
||||
const sorted = sort(tree);
|
||||
assert.deepEqual(Array.from(await sorted.keys()), [
|
||||
"file1",
|
||||
"file10",
|
||||
"file9",
|
||||
]);
|
||||
});
|
||||
|
||||
test("invokes a comparison function", async () => {
|
||||
const tree = Tree.from({
|
||||
b: 2,
|
||||
c: 3,
|
||||
a: 1,
|
||||
});
|
||||
// Reverse order
|
||||
const compare = (a, b) => (a > b ? -1 : a < b ? 1 : 0);
|
||||
const sorted = sort(tree, { compare });
|
||||
assert.deepEqual(Array.from(await sorted.keys()), ["c", "b", "a"]);
|
||||
});
|
||||
|
||||
test("invokes a sortKey function", async () => {
|
||||
const tree = {
|
||||
Alice: { age: 48 },
|
||||
Bob: { age: 36 },
|
||||
Carol: { age: 42 },
|
||||
};
|
||||
const sorted = await sort(tree, {
|
||||
sortKey: async (key, tree) => Tree.traverse(tree, key, "age"),
|
||||
});
|
||||
assert.deepEqual(Array.from(await sorted.keys()), [
|
||||
"Bob",
|
||||
"Carol",
|
||||
"Alice",
|
||||
]);
|
||||
});
|
||||
});
|
||||
20
node_modules/@weborigami/async-tree/test/operations/take.test.js
generated
vendored
Normal file
20
node_modules/@weborigami/async-tree/test/operations/take.test.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { Tree } from "../../src/internal.js";
|
||||
import take from "../../src/operations/take.js";
|
||||
|
||||
describe("take", () => {
|
||||
test("limits the number of keys to the indicated count", async () => {
|
||||
const tree = {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: 4,
|
||||
};
|
||||
const result = await take(tree, 2);
|
||||
assert.deepEqual(await Tree.plain(result), {
|
||||
a: 1,
|
||||
b: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
36
node_modules/@weborigami/async-tree/test/trailingSlash.test.js
generated
vendored
Normal file
36
node_modules/@weborigami/async-tree/test/trailingSlash.test.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { add, has, remove, toggle } from "../src/trailingSlash.js";
|
||||
|
||||
describe("trailingSlash", () => {
|
||||
test("add adds a trailing slash to a string key for a truthy value", () => {
|
||||
assert.equal(add("key"), "key/");
|
||||
assert.equal(add("key/"), "key/");
|
||||
assert.equal(add(1), 1);
|
||||
});
|
||||
|
||||
test("has returns true if a string key has a trailing slash", () => {
|
||||
assert.equal(has("key/"), true);
|
||||
assert.equal(has("key"), false);
|
||||
assert.equal(has(1), false);
|
||||
});
|
||||
|
||||
test("remove removes a trailing slash from a string key", () => {
|
||||
assert.equal(remove("key/"), "key");
|
||||
assert.equal(remove("key"), "key");
|
||||
assert.equal(remove(1), 1);
|
||||
});
|
||||
|
||||
test("toggle removes a slash if present, adds one if not", () => {
|
||||
assert.equal(toggle("key/"), "key");
|
||||
assert.equal(toggle("key"), "key/");
|
||||
assert.equal(toggle(1), 1);
|
||||
});
|
||||
|
||||
test("toggle can force toggling on or off", () => {
|
||||
assert.equal(toggle("key/", false), "key");
|
||||
assert.equal(toggle("key/", true), "key/");
|
||||
assert.equal(toggle("key", false), "key");
|
||||
assert.equal(toggle("key", true), "key/");
|
||||
});
|
||||
});
|
||||
140
node_modules/@weborigami/async-tree/test/utilities.test.js
generated
vendored
Normal file
140
node_modules/@weborigami/async-tree/test/utilities.test.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, test } from "node:test";
|
||||
import { ObjectTree } from "../src/internal.js";
|
||||
import * as symbols from "../src/symbols.js";
|
||||
import * as utilities from "../src/utilities.js";
|
||||
|
||||
describe("utilities", () => {
|
||||
test("box returns a boxed value", () => {
|
||||
const string = "string";
|
||||
const stringObject = utilities.box(string);
|
||||
assert(stringObject instanceof String);
|
||||
assert.equal(stringObject, string);
|
||||
|
||||
const number = 1;
|
||||
const numberObject = utilities.box(number);
|
||||
assert(numberObject instanceof Number);
|
||||
assert.equal(numberObject, number);
|
||||
|
||||
const boolean = true;
|
||||
const booleanObject = utilities.box(boolean);
|
||||
assert(booleanObject instanceof Boolean);
|
||||
assert.equal(booleanObject, boolean);
|
||||
|
||||
const object = {};
|
||||
const boxedObject = utilities.box(object);
|
||||
assert.equal(boxedObject, object);
|
||||
});
|
||||
|
||||
test("getRealmObjectPrototype returns the object's root prototype", () => {
|
||||
const object = {};
|
||||
const proto = utilities.getRealmObjectPrototype(object);
|
||||
assert.equal(proto, Object.prototype);
|
||||
});
|
||||
|
||||
test("isPlainObject returns true if the object is a plain object", () => {
|
||||
assert.equal(utilities.isPlainObject({}), true);
|
||||
assert.equal(utilities.isPlainObject(new Object()), true);
|
||||
assert.equal(utilities.isPlainObject(Object.create(null)), true);
|
||||
class Foo {}
|
||||
assert.equal(utilities.isPlainObject(new Foo()), false);
|
||||
});
|
||||
|
||||
test("keysFromPath() returns the keys from a slash-separated path", () => {
|
||||
assert.deepEqual(utilities.keysFromPath(""), []);
|
||||
assert.deepEqual(utilities.keysFromPath("/"), []);
|
||||
assert.deepEqual(utilities.keysFromPath("a/b/c"), ["a/", "b/", "c"]);
|
||||
assert.deepEqual(utilities.keysFromPath("a/b/c/"), ["a/", "b/", "c/"]);
|
||||
assert.deepEqual(utilities.keysFromPath("/foo/"), ["foo/"]);
|
||||
assert.deepEqual(utilities.keysFromPath("a///b"), ["a/", "b"]);
|
||||
});
|
||||
|
||||
test("naturalOrder compares strings in natural order", () => {
|
||||
const strings = ["file10", "file1", "file9"];
|
||||
strings.sort(utilities.naturalOrder);
|
||||
assert.deepEqual(strings, ["file1", "file9", "file10"]);
|
||||
});
|
||||
|
||||
test("pathFromKeys() returns a slash-separated path from keys", () => {
|
||||
assert.equal(utilities.pathFromKeys([]), "");
|
||||
assert.equal(utilities.pathFromKeys(["a", "b", "c"]), "a/b/c");
|
||||
assert.equal(utilities.pathFromKeys(["a/", "b/", "c"]), "a/b/c");
|
||||
});
|
||||
|
||||
test("pipeline applies a series of functions to a value", async () => {
|
||||
const addOne = (n) => n + 1;
|
||||
const double = (n) => n * 2;
|
||||
const square = (n) => n * n;
|
||||
const result = await utilities.pipeline(1, addOne, double, square);
|
||||
assert.equal(result, 16);
|
||||
});
|
||||
|
||||
test("setParent sets a child's parent", () => {
|
||||
const parent = new ObjectTree({});
|
||||
|
||||
// Set [symbols.parent] on a plain object.
|
||||
const object = {};
|
||||
utilities.setParent(object, parent);
|
||||
assert.equal(object[symbols.parent], parent);
|
||||
|
||||
// Leave [symbols.parent] alone if it's already set.
|
||||
const childWithParent = {
|
||||
[symbols.parent]: "parent",
|
||||
};
|
||||
utilities.setParent(childWithParent, parent);
|
||||
assert.equal(childWithParent[symbols.parent], "parent");
|
||||
|
||||
// Set `parent` on a tree.
|
||||
const tree = new ObjectTree({});
|
||||
utilities.setParent(tree, parent);
|
||||
assert.equal(tree.parent, parent);
|
||||
|
||||
// Leave `parent` alone if it's already set.
|
||||
const treeWithParent = new ObjectTree({});
|
||||
treeWithParent.parent = "parent";
|
||||
utilities.setParent(treeWithParent, parent);
|
||||
assert.equal(treeWithParent.parent, "parent");
|
||||
});
|
||||
|
||||
test("toPlainValue returns the plainest representation of an object", async () => {
|
||||
class User {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal(await utilities.toPlainValue(1), 1);
|
||||
assert.equal(await utilities.toPlainValue("string"), "string");
|
||||
assert.deepEqual(await utilities.toPlainValue({ a: 1 }), { a: 1 });
|
||||
assert.equal(
|
||||
await utilities.toPlainValue(new TextEncoder().encode("bytes")),
|
||||
"bytes"
|
||||
);
|
||||
// ArrayBuffer with non-printable characters should be returned as base64
|
||||
assert.equal(
|
||||
await utilities.toPlainValue(new Uint8Array([1, 2, 3]).buffer),
|
||||
"AQID"
|
||||
);
|
||||
assert.equal(await utilities.toPlainValue(async () => "result"), "result");
|
||||
assert.deepEqual(await utilities.toPlainValue(new User("Alice")), {
|
||||
name: "Alice",
|
||||
});
|
||||
});
|
||||
|
||||
test("toString returns the value of an object's `toString` method", () => {
|
||||
const object = {
|
||||
toString: () => "text",
|
||||
};
|
||||
assert.equal(utilities.toString(object), "text");
|
||||
});
|
||||
|
||||
test("toString returns null for an object with no useful `toString`", () => {
|
||||
const object = {};
|
||||
assert.equal(utilities.toString(object), null);
|
||||
});
|
||||
|
||||
test("toString decodes an ArrayBuffer as UTF-8", () => {
|
||||
const arrayBuffer = new TextEncoder().encode("text").buffer;
|
||||
assert.equal(utilities.toString(arrayBuffer), "text");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user