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**.", }); }