@weborigami/async-tree 0.0.65 → 0.0.66-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/main.js +2 -0
- package/package.json +4 -4
- package/src/BrowserFileTree.js +28 -6
- package/src/DeepMapTree.js +2 -2
- package/src/DeepObjectTree.js +6 -9
- package/src/FileTree.js +14 -10
- package/src/MapTree.js +27 -4
- package/src/ObjectTree.js +53 -6
- package/src/OpenSiteTree.js +41 -0
- package/src/SetTree.js +0 -6
- package/src/SiteTree.js +24 -85
- package/src/Tree.d.ts +1 -2
- package/src/Tree.js +40 -52
- package/src/jsonKeys.js +4 -37
- package/src/operations/cache.js +0 -4
- package/src/operations/deepMerge.js +7 -10
- package/src/operations/merge.js +7 -10
- package/src/trailingSlash.js +54 -0
- package/src/transforms/cachedKeyFunctions.js +72 -34
- package/src/transforms/keyFunctionsForExtensions.js +24 -10
- package/src/transforms/mapFn.js +11 -17
- package/src/transforms/regExpKeys.js +17 -12
- package/src/utilities.js +34 -6
- package/test/BrowserFileTree.test.js +28 -5
- package/test/DeepMapTree.test.js +17 -0
- package/test/DeepObjectTree.test.js +17 -7
- package/test/FileTree.test.js +14 -7
- package/test/MapTree.test.js +21 -0
- package/test/ObjectTree.test.js +16 -12
- package/test/OpenSiteTree.test.js +113 -0
- package/test/SiteTree.test.js +14 -49
- package/test/Tree.test.js +19 -39
- package/test/browser/assert.js +9 -0
- package/test/browser/index.html +4 -4
- package/test/calendarTree.test.js +1 -1
- package/test/fixtures/markdown/subfolder/README.md +1 -0
- package/test/jsonKeys.test.js +0 -9
- package/test/operations/cache.test.js +1 -1
- package/test/operations/merge.test.js +20 -1
- package/test/trailingSlash.test.js +36 -0
- package/test/transforms/cachedKeyFunctions.test.js +90 -0
- package/test/transforms/keyFunctionsForExtensions.test.js +7 -3
- package/test/transforms/mapFn.test.js +29 -20
- package/test/utilities.test.js +6 -2
- package/test/transforms/cachedKeyMaps.test.js +0 -41
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Tree } from "../internal.js";
|
|
2
|
+
import * as trailingSlash from "../trailingSlash.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* A tree whose keys are strings interpreted as regular expressions.
|
|
@@ -57,25 +58,29 @@ export default async function regExpKeys(treelike) {
|
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
// Construct regular expression.
|
|
61
|
-
let text = key;
|
|
62
|
-
if (!text.startsWith("^")) {
|
|
63
|
-
text = "^" + text;
|
|
64
|
-
}
|
|
65
|
-
if (!text.endsWith("$")) {
|
|
66
|
-
text = text + "$";
|
|
67
|
-
}
|
|
68
|
-
const regExp = new RegExp(text);
|
|
69
|
-
|
|
70
61
|
// Get value.
|
|
71
62
|
let value = await tree.get(key);
|
|
72
|
-
|
|
63
|
+
|
|
64
|
+
let regExp;
|
|
65
|
+
if (trailingSlash.has(key) || Tree.isAsyncTree(value)) {
|
|
66
|
+
const baseKey = trailingSlash.remove(key);
|
|
67
|
+
regExp = new RegExp("^" + baseKey + "/?$");
|
|
68
|
+
// Subtree
|
|
73
69
|
value = regExpKeys(value);
|
|
74
70
|
if (!value.parent) {
|
|
75
71
|
value.parent = result;
|
|
76
72
|
}
|
|
73
|
+
} else {
|
|
74
|
+
// Construct regular expression.
|
|
75
|
+
let text = key;
|
|
76
|
+
if (!text.startsWith("^")) {
|
|
77
|
+
text = "^" + text;
|
|
78
|
+
}
|
|
79
|
+
if (!text.endsWith("$")) {
|
|
80
|
+
text = text + "$";
|
|
81
|
+
}
|
|
82
|
+
regExp = new RegExp(text);
|
|
77
83
|
}
|
|
78
|
-
|
|
79
84
|
map.set(regExp, value);
|
|
80
85
|
}
|
|
81
86
|
|
package/src/utilities.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Tree } from "./internal.js";
|
|
2
2
|
import * as symbols from "./symbols.js";
|
|
3
|
+
import * as trailingSlash from "./trailingSlash.js";
|
|
3
4
|
|
|
4
5
|
const textDecoder = new TextDecoder();
|
|
5
6
|
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
@@ -155,20 +156,35 @@ export function isUnpackable(obj) {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
/**
|
|
158
|
-
* Given a path like "/foo/bar/baz", return an array of keys like ["foo",
|
|
159
|
-
* "baz"].
|
|
159
|
+
* Given a path like "/foo/bar/baz", return an array of keys like ["foo/",
|
|
160
|
+
* "bar/", "baz"].
|
|
160
161
|
*
|
|
161
|
-
* Leading slashes are ignored. Consecutive slashes
|
|
162
|
-
*
|
|
162
|
+
* Leading slashes are ignored. Consecutive slashes will be ignored. Trailing
|
|
163
|
+
* slashes are preserved.
|
|
163
164
|
*
|
|
164
165
|
* @param {string} pathname
|
|
165
166
|
*/
|
|
166
167
|
export function keysFromPath(pathname) {
|
|
167
|
-
|
|
168
|
+
// Split the path at each slash
|
|
169
|
+
let keys = pathname.split("/");
|
|
168
170
|
if (keys[0] === "") {
|
|
169
171
|
// The path begins with a slash; drop that part.
|
|
170
172
|
keys.shift();
|
|
171
173
|
}
|
|
174
|
+
if (keys.at(-1) === "") {
|
|
175
|
+
// The path ends with a slash; drop that part.
|
|
176
|
+
keys.pop();
|
|
177
|
+
}
|
|
178
|
+
// Drop any empty keys
|
|
179
|
+
keys = keys.filter((key) => key !== "");
|
|
180
|
+
// Add the trailing slash back to all keys but the last
|
|
181
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
182
|
+
keys[i] += "/";
|
|
183
|
+
}
|
|
184
|
+
// Add trailing slash to last key if path ended with a slash
|
|
185
|
+
if (keys.length > 0 && trailingSlash.has(pathname)) {
|
|
186
|
+
keys[keys.length - 1] += "/";
|
|
187
|
+
}
|
|
172
188
|
return keys;
|
|
173
189
|
}
|
|
174
190
|
|
|
@@ -283,7 +299,19 @@ export async function toPlainValue(input) {
|
|
|
283
299
|
}
|
|
284
300
|
|
|
285
301
|
function toBase64(object) {
|
|
286
|
-
|
|
302
|
+
if (typeof Buffer !== "undefined") {
|
|
303
|
+
// Node.js environment
|
|
304
|
+
return Buffer.from(object).toString("base64");
|
|
305
|
+
} else {
|
|
306
|
+
// Browser environment
|
|
307
|
+
let binary = "";
|
|
308
|
+
const bytes = new Uint8Array(object);
|
|
309
|
+
const len = bytes.byteLength;
|
|
310
|
+
for (let i = 0; i < len; i++) {
|
|
311
|
+
binary += String.fromCharCode(bytes[i]);
|
|
312
|
+
}
|
|
313
|
+
return btoa(binary);
|
|
314
|
+
}
|
|
287
315
|
}
|
|
288
316
|
|
|
289
317
|
/**
|
|
@@ -13,16 +13,14 @@ if (isBrowser) {
|
|
|
13
13
|
"Alice.md",
|
|
14
14
|
"Bob.md",
|
|
15
15
|
"Carol.md",
|
|
16
|
+
"subfolder/",
|
|
16
17
|
]);
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
test("can get the value for a key", async () => {
|
|
20
21
|
const fixture = await createFixture();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"Bob.md": "Hello, **Bob**.",
|
|
24
|
-
"Carol.md": "Hello, **Carol**.",
|
|
25
|
-
});
|
|
22
|
+
const buffer = await fixture.get("Alice.md");
|
|
23
|
+
assert.equal(text(buffer), "Hello, **Alice**.");
|
|
26
24
|
});
|
|
27
25
|
|
|
28
26
|
test("getting an unsupported key returns undefined", async () => {
|
|
@@ -30,6 +28,11 @@ if (isBrowser) {
|
|
|
30
28
|
assert.equal(await fixture.get("xyz"), undefined);
|
|
31
29
|
});
|
|
32
30
|
|
|
31
|
+
test("getting empty key returns undefined", async () => {
|
|
32
|
+
const fixture = await createFixture();
|
|
33
|
+
assert.equal(await fixture.get(""), undefined);
|
|
34
|
+
});
|
|
35
|
+
|
|
33
36
|
test("getting a null/undefined key throws an exception", async () => {
|
|
34
37
|
const fixture = await createFixture();
|
|
35
38
|
await assert.rejects(async () => {
|
|
@@ -40,6 +43,20 @@ if (isBrowser) {
|
|
|
40
43
|
});
|
|
41
44
|
});
|
|
42
45
|
|
|
46
|
+
test("sets parent on subtrees", async () => {
|
|
47
|
+
const fixture = await createFixture();
|
|
48
|
+
const subfolder = await fixture.get("subfolder");
|
|
49
|
+
assert.equal(subfolder.parent, fixture);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
53
|
+
const fixture = await createFixture();
|
|
54
|
+
assert(await fixture.get("Alice.md"));
|
|
55
|
+
assert(await fixture.get("Alice.md/"));
|
|
56
|
+
assert(await fixture.get("subfolder"));
|
|
57
|
+
assert(await fixture.get("subfolder/"));
|
|
58
|
+
});
|
|
59
|
+
|
|
43
60
|
test("can set a value", async () => {
|
|
44
61
|
const fixture = await createFixture();
|
|
45
62
|
|
|
@@ -59,6 +76,7 @@ if (isBrowser) {
|
|
|
59
76
|
"Alice.md": "Goodbye, **Alice**.",
|
|
60
77
|
"Carol.md": "Hello, **Carol**.",
|
|
61
78
|
"David.md": "Hello, **David**.",
|
|
79
|
+
subfolder: {},
|
|
62
80
|
});
|
|
63
81
|
});
|
|
64
82
|
|
|
@@ -81,6 +99,7 @@ if (isBrowser) {
|
|
|
81
99
|
more: {
|
|
82
100
|
"Ellen.md": "Hello, **Ellen**.",
|
|
83
101
|
},
|
|
102
|
+
subfolder: {},
|
|
84
103
|
});
|
|
85
104
|
});
|
|
86
105
|
});
|
|
@@ -118,6 +137,10 @@ async function createFixture() {
|
|
|
118
137
|
await createFile(subdirectory, "Bob.md", "Hello, **Bob**.");
|
|
119
138
|
await createFile(subdirectory, "Carol.md", "Hello, **Carol**.");
|
|
120
139
|
|
|
140
|
+
await subdirectory.getDirectoryHandle("subfolder", {
|
|
141
|
+
create: true,
|
|
142
|
+
});
|
|
143
|
+
|
|
121
144
|
return new BrowserFileTree(subdirectory);
|
|
122
145
|
}
|
|
123
146
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import DeepMapTree from "../src/DeepMapTree.js";
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
|
+
|
|
6
|
+
describe("DeepMapTree", () => {
|
|
7
|
+
test("returns a DeepMapTree for value that's a Map", async () => {
|
|
8
|
+
const tree = new DeepMapTree([
|
|
9
|
+
["a", 1],
|
|
10
|
+
["map", new Map([["b", 2]])],
|
|
11
|
+
]);
|
|
12
|
+
const map = await tree.get("map");
|
|
13
|
+
assert.equal(map instanceof DeepMapTree, true);
|
|
14
|
+
assert.deepEqual(await Tree.plain(map), { b: 2 });
|
|
15
|
+
assert.equal(map.parent, tree);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -4,13 +4,7 @@ import { DeepObjectTree, Tree } from "../src/internal.js";
|
|
|
4
4
|
|
|
5
5
|
describe("DeepObjectTree", () => {
|
|
6
6
|
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
|
7
|
-
const tree =
|
|
8
|
-
a: 1,
|
|
9
|
-
object: {
|
|
10
|
-
b: 2,
|
|
11
|
-
},
|
|
12
|
-
array: [3],
|
|
13
|
-
});
|
|
7
|
+
const tree = createFixture();
|
|
14
8
|
|
|
15
9
|
const object = await tree.get("object");
|
|
16
10
|
assert.equal(object instanceof DeepObjectTree, true);
|
|
@@ -22,4 +16,20 @@ describe("DeepObjectTree", () => {
|
|
|
22
16
|
assert.deepEqual(await Tree.plain(array), [3]);
|
|
23
17
|
assert.equal(array.parent, tree);
|
|
24
18
|
});
|
|
19
|
+
|
|
20
|
+
test("adds trailing slashes to keys for subtrees including plain objects or arrays", async () => {
|
|
21
|
+
const tree = createFixture();
|
|
22
|
+
const keys = Array.from(await tree.keys());
|
|
23
|
+
assert.deepEqual(keys, ["a", "object/", "array/"]);
|
|
24
|
+
});
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
function createFixture() {
|
|
28
|
+
return new DeepObjectTree({
|
|
29
|
+
a: 1,
|
|
30
|
+
object: {
|
|
31
|
+
b: 2,
|
|
32
|
+
},
|
|
33
|
+
array: [3],
|
|
34
|
+
});
|
|
35
|
+
}
|
package/test/FileTree.test.js
CHANGED
|
@@ -18,6 +18,7 @@ describe("FileTree", async () => {
|
|
|
18
18
|
"Alice.md",
|
|
19
19
|
"Bob.md",
|
|
20
20
|
"Carol.md",
|
|
21
|
+
"subfolder/",
|
|
21
22
|
]);
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -33,6 +34,11 @@ describe("FileTree", async () => {
|
|
|
33
34
|
assert.equal(await fixture.get("xyz"), undefined);
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
test("getting empty key returns undefined", async () => {
|
|
38
|
+
const fixture = createFixture("fixtures/markdown");
|
|
39
|
+
assert.equal(await fixture.get(""), undefined);
|
|
40
|
+
});
|
|
41
|
+
|
|
36
42
|
test("getting a null/undefined key throws an exception", async () => {
|
|
37
43
|
const fixture = createFixture("fixtures/markdown");
|
|
38
44
|
await assert.rejects(async () => {
|
|
@@ -43,17 +49,18 @@ describe("FileTree", async () => {
|
|
|
43
49
|
});
|
|
44
50
|
});
|
|
45
51
|
|
|
46
|
-
test("
|
|
47
|
-
const fixture = createFixture("fixtures");
|
|
48
|
-
|
|
49
|
-
assert.
|
|
52
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
53
|
+
const fixture = createFixture("fixtures/markdown");
|
|
54
|
+
assert(await fixture.get("Alice.md"));
|
|
55
|
+
assert(await fixture.get("Alice.md/"));
|
|
56
|
+
assert(await fixture.get("subfolder"));
|
|
57
|
+
assert(await fixture.get("subfolder/"));
|
|
50
58
|
});
|
|
51
59
|
|
|
52
|
-
test("
|
|
60
|
+
test("sets parent on subtrees", async () => {
|
|
53
61
|
const fixture = createFixture("fixtures");
|
|
54
|
-
assert(await fixture.isKeyForSubtree("markdown"));
|
|
55
62
|
const markdown = await fixture.get("markdown");
|
|
56
|
-
assert(
|
|
63
|
+
assert.equal(markdown.parent, fixture);
|
|
57
64
|
});
|
|
58
65
|
|
|
59
66
|
test("can write out a file via set()", async () => {
|
package/test/MapTree.test.js
CHANGED
|
@@ -22,6 +22,27 @@ describe("MapTree", () => {
|
|
|
22
22
|
assert.equal(more[symbols.parent], fixture);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
test("adds trailing slashes to keys for subtrees", async () => {
|
|
26
|
+
const tree = new MapTree([
|
|
27
|
+
["a", 1],
|
|
28
|
+
["subtree", new MapTree([["b", 2]])],
|
|
29
|
+
]);
|
|
30
|
+
const keys = Array.from(await tree.keys());
|
|
31
|
+
assert.deepEqual(keys, ["a", "subtree/"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
35
|
+
const subtree = new MapTree([["b", 2]]);
|
|
36
|
+
const tree = new MapTree([
|
|
37
|
+
["a", 1],
|
|
38
|
+
["subtree", subtree],
|
|
39
|
+
]);
|
|
40
|
+
assert.equal(await tree.get("a"), 1);
|
|
41
|
+
assert.equal(await tree.get("a/"), 1);
|
|
42
|
+
assert.equal(await tree.get("subtree"), subtree);
|
|
43
|
+
assert.equal(await tree.get("subtree/"), subtree);
|
|
44
|
+
});
|
|
45
|
+
|
|
25
46
|
test("getting an unsupported key returns undefined", async () => {
|
|
26
47
|
const fixture = createFixture();
|
|
27
48
|
assert.equal(await fixture.get("d"), undefined);
|
package/test/ObjectTree.test.js
CHANGED
|
@@ -41,18 +41,21 @@ describe("ObjectTree", () => {
|
|
|
41
41
|
c: 3,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
// Update existing key
|
|
44
|
+
// Update existing key
|
|
45
45
|
await tree.set("a", 4);
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
await tree.set("d", 5);
|
|
49
|
-
|
|
50
|
-
// Delete key.
|
|
47
|
+
// Delete key
|
|
51
48
|
await tree.set("b", undefined);
|
|
52
49
|
|
|
50
|
+
// Overwrite key with trailing slash
|
|
51
|
+
await tree.set("c/", {});
|
|
52
|
+
|
|
53
|
+
// New key
|
|
54
|
+
await tree.set("d", 5);
|
|
55
|
+
|
|
53
56
|
assert.deepEqual(await Tree.entries(tree), [
|
|
54
57
|
["a", 4],
|
|
55
|
-
["c",
|
|
58
|
+
["c/", {}],
|
|
56
59
|
["d", 5],
|
|
57
60
|
]);
|
|
58
61
|
});
|
|
@@ -106,7 +109,7 @@ describe("ObjectTree", () => {
|
|
|
106
109
|
assert.equal(more.parent, fixture);
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
test("
|
|
112
|
+
test("adds trailing slashes to keys for subtrees", async () => {
|
|
110
113
|
const tree = new ObjectTree({
|
|
111
114
|
a1: 1,
|
|
112
115
|
a2: new ObjectTree({
|
|
@@ -118,21 +121,22 @@ describe("ObjectTree", () => {
|
|
|
118
121
|
}),
|
|
119
122
|
});
|
|
120
123
|
const keys = Array.from(await tree.keys());
|
|
121
|
-
|
|
122
|
-
keys.map(async (key) => await tree.isKeyForSubtree(key))
|
|
123
|
-
);
|
|
124
|
-
assert.deepEqual(subtrees, [false, true, false, true]);
|
|
124
|
+
assert.deepEqual(keys, ["a1", "a2/", "a3", "a4/"]);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
test("
|
|
127
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
128
128
|
const subtree = {
|
|
129
129
|
async get(key) {},
|
|
130
130
|
async keys() {},
|
|
131
131
|
};
|
|
132
132
|
const tree = new ObjectTree({
|
|
133
|
+
a: 1,
|
|
133
134
|
subtree,
|
|
134
135
|
});
|
|
136
|
+
assert.equal(await tree.get("a"), 1);
|
|
137
|
+
assert.equal(await tree.get("a/"), 1);
|
|
135
138
|
assert.equal(await tree.get("subtree"), subtree);
|
|
139
|
+
assert.equal(await tree.get("subtree/"), subtree);
|
|
136
140
|
});
|
|
137
141
|
|
|
138
142
|
test("method on an object is bound to the object", async () => {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
|
+
import OpenSiteTree from "../src/OpenSiteTree.js";
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
|
+
|
|
6
|
+
const textDecoder = new TextDecoder();
|
|
7
|
+
const textEncoder = new TextEncoder();
|
|
8
|
+
|
|
9
|
+
const mockHost = "https://mock";
|
|
10
|
+
|
|
11
|
+
const mockResponses = {
|
|
12
|
+
"/.keys.json": {
|
|
13
|
+
data: JSON.stringify(["about/", "index.html"]),
|
|
14
|
+
},
|
|
15
|
+
"/about": {
|
|
16
|
+
redirected: true,
|
|
17
|
+
status: 301,
|
|
18
|
+
url: "https://mock/about/",
|
|
19
|
+
},
|
|
20
|
+
"/about/.keys.json": {
|
|
21
|
+
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
|
|
22
|
+
},
|
|
23
|
+
"/about/Alice.html": {
|
|
24
|
+
data: "Hello, Alice!",
|
|
25
|
+
},
|
|
26
|
+
"/about/Bob.html": {
|
|
27
|
+
data: "Hello, Bob!",
|
|
28
|
+
},
|
|
29
|
+
"/about/Carol.html": {
|
|
30
|
+
data: "Hello, Carol!",
|
|
31
|
+
},
|
|
32
|
+
"/index.html": {
|
|
33
|
+
data: "Home page",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe("OpenSiteTree", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
mock.method(global, "fetch", mockFetch);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("can get the keys of a tree", async () => {
|
|
43
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
44
|
+
const keys = await fixture.keys();
|
|
45
|
+
assert.deepEqual(Array.from(keys), ["about/", "index.html"]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("can get a plain value for a key", async () => {
|
|
49
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
50
|
+
const arrayBuffer = await fixture.get("index.html");
|
|
51
|
+
const text = textDecoder.decode(arrayBuffer);
|
|
52
|
+
assert.equal(text, "Home page");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("getting an unsupported key returns undefined", async () => {
|
|
56
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
57
|
+
assert.equal(await fixture.get("xyz"), undefined);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("getting a null/undefined key throws an exception", async () => {
|
|
61
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
62
|
+
await assert.rejects(async () => {
|
|
63
|
+
await fixture.get(null);
|
|
64
|
+
});
|
|
65
|
+
await assert.rejects(async () => {
|
|
66
|
+
await fixture.get(undefined);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("can return a new tree for a key that redirects", async () => {
|
|
71
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
72
|
+
const about = await fixture.get("about");
|
|
73
|
+
assert(about instanceof OpenSiteTree);
|
|
74
|
+
assert.equal(about.href, "https://mock/about/");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("can convert a site to a plain object", async () => {
|
|
78
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
79
|
+
// Convert buffers to strings.
|
|
80
|
+
const strings = Tree.map(fixture, (value) => textDecoder.decode(value));
|
|
81
|
+
assert.deepEqual(await Tree.plain(strings), {
|
|
82
|
+
about: {
|
|
83
|
+
"Alice.html": "Hello, Alice!",
|
|
84
|
+
"Bob.html": "Hello, Bob!",
|
|
85
|
+
"Carol.html": "Hello, Carol!",
|
|
86
|
+
},
|
|
87
|
+
"index.html": "Home page",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
async function mockFetch(href) {
|
|
93
|
+
if (!href.startsWith(mockHost)) {
|
|
94
|
+
return { status: 404 };
|
|
95
|
+
}
|
|
96
|
+
const path = href.slice(mockHost.length);
|
|
97
|
+
const mockedResponse = mockResponses[path];
|
|
98
|
+
if (mockedResponse) {
|
|
99
|
+
return Object.assign(
|
|
100
|
+
{
|
|
101
|
+
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 200,
|
|
104
|
+
text: () => mockedResponse.data,
|
|
105
|
+
},
|
|
106
|
+
mockedResponse
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
status: 404,
|
|
112
|
+
};
|
|
113
|
+
}
|
package/test/SiteTree.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
3
|
import SiteTree from "../src/SiteTree.js";
|
|
4
|
-
import { Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
const textDecoder = new TextDecoder();
|
|
7
6
|
const textEncoder = new TextEncoder();
|
|
@@ -9,17 +8,11 @@ const textEncoder = new TextEncoder();
|
|
|
9
8
|
const mockHost = "https://mock";
|
|
10
9
|
|
|
11
10
|
const mockResponses = {
|
|
12
|
-
"/.keys.json": {
|
|
13
|
-
data: JSON.stringify(["about/"]),
|
|
14
|
-
},
|
|
15
11
|
"/about": {
|
|
16
12
|
redirected: true,
|
|
17
13
|
status: 301,
|
|
18
14
|
url: "https://mock/about/",
|
|
19
15
|
},
|
|
20
|
-
"/about/.keys.json": {
|
|
21
|
-
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
|
|
22
|
-
},
|
|
23
16
|
"/about/Alice.html": {
|
|
24
17
|
data: "Hello, Alice!",
|
|
25
18
|
},
|
|
@@ -29,6 +22,9 @@ const mockResponses = {
|
|
|
29
22
|
"/about/Carol.html": {
|
|
30
23
|
data: "Hello, Carol!",
|
|
31
24
|
},
|
|
25
|
+
"/index.html": {
|
|
26
|
+
data: "Home page",
|
|
27
|
+
},
|
|
32
28
|
};
|
|
33
29
|
|
|
34
30
|
describe("SiteTree", () => {
|
|
@@ -36,29 +32,24 @@ describe("SiteTree", () => {
|
|
|
36
32
|
mock.method(global, "fetch", mockFetch);
|
|
37
33
|
});
|
|
38
34
|
|
|
39
|
-
test("
|
|
35
|
+
test("returns an empty array as the keys of a tree", async () => {
|
|
40
36
|
const fixture = new SiteTree(mockHost);
|
|
41
|
-
const
|
|
42
|
-
assert.
|
|
37
|
+
const keys = await fixture.keys();
|
|
38
|
+
assert.deepEqual(Array.from(keys), []);
|
|
43
39
|
});
|
|
44
40
|
|
|
45
|
-
test("can get
|
|
41
|
+
test("can get a plain value for a key", async () => {
|
|
46
42
|
const fixture = new SiteTree(mockHost);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
assert.
|
|
50
|
-
"Alice.html",
|
|
51
|
-
"Bob.html",
|
|
52
|
-
"Carol.html",
|
|
53
|
-
]);
|
|
43
|
+
const arrayBuffer = await fixture.get("index.html");
|
|
44
|
+
const text = textDecoder.decode(arrayBuffer);
|
|
45
|
+
assert.equal(text, "Home page");
|
|
54
46
|
});
|
|
55
47
|
|
|
56
|
-
test("
|
|
48
|
+
test("immediately return a new tree for a key with a trailing slash", async () => {
|
|
57
49
|
const fixture = new SiteTree(mockHost);
|
|
58
|
-
const about = fixture.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
assert.equal(text, "Hello, Alice!");
|
|
50
|
+
const about = await fixture.get("about/");
|
|
51
|
+
assert(about instanceof SiteTree);
|
|
52
|
+
assert.equal(about.href, "https://mock/about/");
|
|
62
53
|
});
|
|
63
54
|
|
|
64
55
|
test("getting an unsupported key returns undefined", async () => {
|
|
@@ -75,32 +66,6 @@ describe("SiteTree", () => {
|
|
|
75
66
|
await fixture.get(undefined);
|
|
76
67
|
});
|
|
77
68
|
});
|
|
78
|
-
|
|
79
|
-
test("a redirect on a site with keys returns a SiteTree for the new URL", async () => {
|
|
80
|
-
const fixture = new SiteTree(mockHost);
|
|
81
|
-
const about = await fixture.get("about");
|
|
82
|
-
assert.equal(about.href, "https://mock/about/");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("can determine whether a key is for a subtree", async () => {
|
|
86
|
-
const fixture = new SiteTree(mockHost);
|
|
87
|
-
assert.equal(await fixture.isKeyForSubtree("about"), true);
|
|
88
|
-
const about = fixture.resolve("about");
|
|
89
|
-
assert.equal(await about.isKeyForSubtree("Alice.html"), false);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("can convert a SiteGraph to a plain object", async () => {
|
|
93
|
-
const fixture = new SiteTree(mockHost);
|
|
94
|
-
// Convert buffers to strings.
|
|
95
|
-
const strings = Tree.map(fixture, (value) => textDecoder.decode(value));
|
|
96
|
-
assert.deepEqual(await Tree.plain(strings), {
|
|
97
|
-
about: {
|
|
98
|
-
"Alice.html": "Hello, Alice!",
|
|
99
|
-
"Bob.html": "Hello, Bob!",
|
|
100
|
-
"Carol.html": "Hello, Carol!",
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
69
|
});
|
|
105
70
|
|
|
106
71
|
async function mockFetch(href) {
|