@weborigami/async-tree 0.0.66-beta.1 → 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.
Files changed (45) hide show
  1. package/main.js +2 -0
  2. package/package.json +4 -4
  3. package/src/BrowserFileTree.js +28 -6
  4. package/src/DeepMapTree.js +2 -2
  5. package/src/DeepObjectTree.js +6 -9
  6. package/src/FileTree.js +14 -10
  7. package/src/MapTree.js +27 -4
  8. package/src/ObjectTree.js +53 -6
  9. package/src/OpenSiteTree.js +41 -0
  10. package/src/SetTree.js +0 -6
  11. package/src/SiteTree.js +24 -85
  12. package/src/Tree.d.ts +0 -1
  13. package/src/Tree.js +18 -38
  14. package/src/jsonKeys.js +4 -37
  15. package/src/operations/cache.js +0 -4
  16. package/src/operations/deepMerge.js +7 -10
  17. package/src/operations/merge.js +7 -10
  18. package/src/trailingSlash.js +54 -0
  19. package/src/transforms/cachedKeyFunctions.js +72 -34
  20. package/src/transforms/keyFunctionsForExtensions.js +24 -10
  21. package/src/transforms/mapFn.js +11 -17
  22. package/src/transforms/regExpKeys.js +17 -12
  23. package/src/utilities.js +34 -6
  24. package/test/BrowserFileTree.test.js +28 -5
  25. package/test/DeepMapTree.test.js +17 -0
  26. package/test/DeepObjectTree.test.js +17 -7
  27. package/test/FileTree.test.js +14 -7
  28. package/test/MapTree.test.js +21 -0
  29. package/test/ObjectTree.test.js +16 -12
  30. package/test/OpenSiteTree.test.js +113 -0
  31. package/test/SiteTree.test.js +14 -49
  32. package/test/Tree.test.js +19 -39
  33. package/test/browser/assert.js +9 -0
  34. package/test/browser/index.html +4 -4
  35. package/test/calendarTree.test.js +1 -1
  36. package/test/fixtures/markdown/subfolder/README.md +1 -0
  37. package/test/jsonKeys.test.js +0 -9
  38. package/test/operations/cache.test.js +1 -1
  39. package/test/operations/merge.test.js +20 -1
  40. package/test/trailingSlash.test.js +36 -0
  41. package/test/transforms/cachedKeyFunctions.test.js +90 -0
  42. package/test/transforms/keyFunctionsForExtensions.test.js +7 -3
  43. package/test/transforms/mapFn.test.js +29 -20
  44. package/test/utilities.test.js +6 -2
  45. package/test/transforms/cachedKeyMaps.test.js +0 -41
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", "bar",
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 or a trailing slash will be
162
- * represented by the empty string.
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
- const keys = pathname.split("/");
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
- return Buffer.from(object).toString("base64");
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
- assert.deepEqual(await strings(fixture), {
22
- "Alice.md": "Hello, **Alice**.",
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 = new DeepObjectTree({
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
+ }
@@ -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("sets parent on subtrees", async () => {
47
- const fixture = createFixture("fixtures");
48
- const markdown = await fixture.get("markdown");
49
- assert.equal(markdown.parent, fixture);
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("can indicate which values are subtrees", async () => {
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(!(await markdown.isKeyForSubtree("a.txt")));
63
+ assert.equal(markdown.parent, fixture);
57
64
  });
58
65
 
59
66
  test("can write out a file via set()", async () => {
@@ -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);
@@ -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
- // New key.
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", 3],
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("isKeyForSubtree() indicates which values are subtrees", async () => {
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
- const subtrees = await Promise.all(
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("returns an async tree value as is", async () => {
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
+ }
@@ -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("resolve() returns a new SiteTree for the given relative route", () => {
35
+ test("returns an empty array as the keys of a tree", async () => {
40
36
  const fixture = new SiteTree(mockHost);
41
- const about = fixture.resolve("about");
42
- assert.equal(about.href, "https://mock/about/");
37
+ const keys = await fixture.keys();
38
+ assert.deepEqual(Array.from(keys), []);
43
39
  });
44
40
 
45
- test("can get the keys of the tree", async () => {
41
+ test("can get a plain value for a key", async () => {
46
42
  const fixture = new SiteTree(mockHost);
47
- const about = fixture.resolve("about");
48
- const keys = await about.keys();
49
- assert.deepEqual(Array.from(keys), [
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("can get the value for a key", async () => {
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.resolve("about");
59
- const arrayBuffer = await about.get("Alice.html");
60
- const text = textDecoder.decode(arrayBuffer);
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) {
package/test/Tree.test.js CHANGED
@@ -31,7 +31,8 @@ describe("Tree", () => {
31
31
  const result = await Tree.assign(target, source);
32
32
 
33
33
  assert.equal(result, target);
34
- assert.deepEqual(await Tree.plain(target), {
34
+ const plain = await Tree.plain(target);
35
+ assert.deepEqual(plain, {
35
36
  a: 4,
36
37
  c: 5,
37
38
  more: {
@@ -171,17 +172,6 @@ describe("Tree", () => {
171
172
  assert(Tree.isTreelike(new Set()));
172
173
  });
173
174
 
174
- test("isKeyForSubtree() returns true if the key is for a subtree", async () => {
175
- const tree = new DeepObjectTree({
176
- a: 1,
177
- more: {
178
- b: 2,
179
- },
180
- });
181
- assert(!(await Tree.isKeyForSubtree(tree, "a")));
182
- assert(await Tree.isKeyForSubtree(tree, "more"));
183
- });
184
-
185
175
  test("map() maps values", async () => {
186
176
  const tree = new DeepObjectTree({
187
177
  a: "Alice",
@@ -228,18 +218,26 @@ describe("Tree", () => {
228
218
  });
229
219
 
230
220
  test("plain() produces a plain object version of a tree", async () => {
231
- const original = {
221
+ const tree = new ObjectTree({
232
222
  a: 1,
233
- b: 2,
234
- c: 3,
235
- more: {
236
- d: 4,
237
- e: 5,
223
+ // Slashes should be normalized
224
+ "sub1/": {
225
+ b: 2,
238
226
  },
239
- };
240
- const tree = new ObjectTree(original);
227
+ sub2: {
228
+ c: 3,
229
+ },
230
+ });
241
231
  const plain = await Tree.plain(tree);
242
- assert.deepEqual(plain, original);
232
+ assert.deepEqual(plain, {
233
+ a: 1,
234
+ sub1: {
235
+ b: 2,
236
+ },
237
+ sub2: {
238
+ c: 3,
239
+ },
240
+ });
243
241
  });
244
242
 
245
243
  test("plain() produces an array for an array-like tree", async () => {
@@ -335,24 +333,6 @@ describe("Tree", () => {
335
333
  assert.equal(await Tree.traverse(tree, "a", "b", "c"), "Hello");
336
334
  });
337
335
 
338
- test("traversing a final empty string can unpack the last value", async () => {
339
- const unpackable = new String();
340
- /** @type {any} */ (unpackable).unpack = () => "Content";
341
- const tree = {
342
- unpackable,
343
- };
344
- const result = await Tree.traverse(tree, "unpackable", "");
345
- assert.equal(result, "Content");
346
- });
347
-
348
- test("traversing a final empty string returns a value as is if it's not unpackable", async () => {
349
- const tree = new ObjectTree({
350
- a: "Hello",
351
- });
352
- const result = await Tree.traverse(tree, "a", "");
353
- assert.equal(result, "Hello");
354
- });
355
-
356
336
  test("traversePath() traverses a slash-separated path", async () => {
357
337
  const tree = new ObjectTree({
358
338
  a: {
@@ -43,3 +43,12 @@ assert.deepEqual = (actual, expected) => {
43
43
 
44
44
  throw new Error(`Expected ${expected} but got ${actual}`);
45
45
  };
46
+
47
+ assert.rejects = async (promise) => {
48
+ try {
49
+ await promise;
50
+ throw new Error("Expected promise to reject but it resolved");
51
+ } catch (error) {
52
+ return;
53
+ }
54
+ };
@@ -22,13 +22,13 @@
22
22
  <script type="module" src="../Tree.test.js"></script>
23
23
  <script type="module" src="../operations/cache.test.js"></script>
24
24
  <script type="module" src="../operations/merge.test.js"></script>
25
- <script type="module" src="../operations/mergeDeep.test.js"></script>
26
- <script type="module" src="../transforms/cachedKeyFns.test.js"></script>
25
+ <script type="module" src="../operations/deepMerge.test.js"></script>
26
+ <script type="module" src="../transforms/cachedKeyMaps.test.js"></script>
27
27
  <script
28
28
  type="module"
29
- src="../transforms/keyMapsForExtensions.test.js"
29
+ src="../transforms/keyFunctionsForExtensions.test.js"
30
30
  ></script>
31
- <script type="module" src="../transforms/map.test.js"></script>
31
+ <script type="module" src="../transforms/mapFn.test.js"></script>
32
32
  <script type="module" src="../utilities.test.js"></script>
33
33
  </head>
34
34
  <body></body>