@fluidframework/azure-end-to-end-tests 2.0.2 → 2.1.0-276326
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/lib/test/tree.spec.js +183 -53
- package/lib/test/tree.spec.js.map +1 -1
- package/package.json +21 -21
- package/src/test/tree.spec.ts +216 -72
package/lib/test/tree.spec.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import { strict as assert } from "node:assert";
|
|
6
6
|
import { ConnectionState } from "@fluidframework/container-loader";
|
|
7
7
|
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
8
|
-
import { TreeViewConfiguration, SchemaFactory
|
|
9
|
-
import { SharedTree } from "@fluidframework/tree/internal";
|
|
8
|
+
import { TreeViewConfiguration, SchemaFactory } from "@fluidframework/tree";
|
|
9
|
+
import { SharedTree, Tree, TreeStatus } from "@fluidframework/tree/internal";
|
|
10
10
|
import { createAzureClient, createContainerFromPayload, getContainerIdFromPayloadResponse, } from "./AzureClientFactory.js";
|
|
11
11
|
import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js";
|
|
12
12
|
import { getTestMatrix } from "./utils.js";
|
|
@@ -50,35 +50,41 @@ for (const testOpts of testMatrix) {
|
|
|
50
50
|
beforeEach("createAzureClient", () => {
|
|
51
51
|
client = createAzureClient();
|
|
52
52
|
});
|
|
53
|
+
async function waitForConnection(container) {
|
|
54
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
55
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
56
|
+
durationMs: connectTimeoutMs,
|
|
57
|
+
errorMsg: "container1 connect() timeout",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
53
61
|
/**
|
|
54
|
-
*
|
|
55
|
-
* it can set the initial objects to SharedTree and do basic SharedTree ops.
|
|
56
|
-
*
|
|
57
|
-
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
58
|
-
* be returned.
|
|
62
|
+
* Either creates a new azure client with a SharedTree, or loads an azure client from the existing summary tree (the "ephemeral" case).
|
|
59
63
|
*/
|
|
60
|
-
|
|
64
|
+
async function createOrLoad(summaryTree) {
|
|
61
65
|
let containerId;
|
|
62
|
-
let container1;
|
|
63
66
|
let treeData;
|
|
64
|
-
if (
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
({ container: container1 } = await client.getContainer(containerId, schema, "2"));
|
|
68
|
-
treeData = container1.initialObjects.tree1.viewWith(treeConfiguration);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
({ container: container1 } = await client.createContainer(schema, "2"));
|
|
72
|
-
treeData = container1.initialObjects.tree1.viewWith(treeConfiguration);
|
|
67
|
+
if (summaryTree === undefined) {
|
|
68
|
+
const { container } = await client.createContainer(schema, "2");
|
|
69
|
+
treeData = container.initialObjects.tree1.viewWith(treeConfiguration);
|
|
73
70
|
treeData.initialize(new StringArray([]));
|
|
74
|
-
containerId = await
|
|
71
|
+
containerId = await container.attach();
|
|
72
|
+
await waitForConnection(container);
|
|
75
73
|
}
|
|
76
|
-
|
|
77
|
-
await
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
else {
|
|
75
|
+
const containerResponse = await createContainerFromPayload(summaryTree, "test-user-id-1", "test-user-name-1");
|
|
76
|
+
containerId = getContainerIdFromPayloadResponse(containerResponse);
|
|
77
|
+
const { container } = await client.getContainer(containerId, schema, "2");
|
|
78
|
+
treeData = container.initialObjects.tree1.viewWith(treeConfiguration);
|
|
79
|
+
await waitForConnection(container);
|
|
81
80
|
}
|
|
81
|
+
return {
|
|
82
|
+
containerId,
|
|
83
|
+
treeData,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
it("can create/load a container with SharedTree and do basic ops", async () => {
|
|
87
|
+
const { treeData } = await createOrLoad(isEphemeral ? ephemeralSummaryTrees.createContainerWithSharedTree : undefined);
|
|
82
88
|
treeData.root.insertNew("test string 1");
|
|
83
89
|
assert.strictEqual(treeData.root.length, 1);
|
|
84
90
|
assert.strictEqual(treeData.root.at(0), "test string 1");
|
|
@@ -90,36 +96,9 @@ for (const testOpts of testMatrix) {
|
|
|
90
96
|
assert.strictEqual(treeData.root.length, 1);
|
|
91
97
|
assert.strictEqual(treeData.root.at(0), "test string 1");
|
|
92
98
|
});
|
|
93
|
-
/**
|
|
94
|
-
* Scenario: test when an Azure Client container is created,
|
|
95
|
-
* and it can be loaded by another container with SharedTree and do basic SharedTree ops.
|
|
96
|
-
*
|
|
97
|
-
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
98
|
-
* be returned.
|
|
99
|
-
*/
|
|
100
99
|
it("can create/load a container with SharedTree collaborate with basic ops", async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let treeData1;
|
|
104
|
-
if (isEphemeral) {
|
|
105
|
-
const containerResponse = await createContainerFromPayload(ephemeralSummaryTrees.createLoadContainerWithSharedTree, "test-user-id-1", "test-user-name-1");
|
|
106
|
-
containerId = getContainerIdFromPayloadResponse(containerResponse);
|
|
107
|
-
({ container: container1 } = await client.getContainer(containerId, schema, "2"));
|
|
108
|
-
treeData1 = container1.initialObjects.tree1.viewWith(treeConfiguration);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
({ container: container1 } = await client.createContainer(schema, "2"));
|
|
112
|
-
treeData1 = container1.initialObjects.tree1.viewWith(treeConfiguration);
|
|
113
|
-
treeData1.initialize(new StringArray([]));
|
|
114
|
-
containerId = await container1.attach();
|
|
115
|
-
}
|
|
116
|
-
if (container1.connectionState !== ConnectionState.Connected) {
|
|
117
|
-
await timeoutPromise((resolve) => container1.once("connected", () => resolve()), {
|
|
118
|
-
durationMs: connectTimeoutMs,
|
|
119
|
-
errorMsg: "container1 connect() timeout",
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
treeData1.root.insertNew("test string 1");
|
|
100
|
+
const { containerId, treeData } = await createOrLoad(isEphemeral ? ephemeralSummaryTrees.createLoadContainerWithSharedTree : undefined);
|
|
101
|
+
treeData.root.insertNew("test string 1");
|
|
123
102
|
const resources = client.getContainer(containerId, schema, "2");
|
|
124
103
|
await assert.doesNotReject(resources, () => true, "container cannot be retrieved from Azure Fluid Relay");
|
|
125
104
|
const { container: container2 } = await resources;
|
|
@@ -134,6 +113,157 @@ for (const testOpts of testMatrix) {
|
|
|
134
113
|
assert.strictEqual(treeData2.root.length, 1);
|
|
135
114
|
assert.strictEqual(treeData2.root.at(0), "test string 1");
|
|
136
115
|
});
|
|
116
|
+
if (!isEphemeral) {
|
|
117
|
+
{
|
|
118
|
+
class Nicknames extends sf.array("Nicknames", sf.string) {
|
|
119
|
+
}
|
|
120
|
+
class UserData extends sf.map("UserData", [sf.string, sf.number, sf.boolean]) {
|
|
121
|
+
}
|
|
122
|
+
class User extends sf.object("User", {
|
|
123
|
+
name: sf.string,
|
|
124
|
+
nicknames: Nicknames,
|
|
125
|
+
data: UserData,
|
|
126
|
+
}) {
|
|
127
|
+
}
|
|
128
|
+
it("can read and edit data", async () => {
|
|
129
|
+
const { container } = await client.createContainer(schema, "2");
|
|
130
|
+
await container.attach();
|
|
131
|
+
const view = container.initialObjects.tree1.viewWith(new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }));
|
|
132
|
+
const tags = [
|
|
133
|
+
["Age", 32],
|
|
134
|
+
["Favorite Snack", "Potato Chips"],
|
|
135
|
+
["Awake", true],
|
|
136
|
+
];
|
|
137
|
+
view.initialize(new User({
|
|
138
|
+
name: "Pardes",
|
|
139
|
+
nicknames: ["Alex", "Duder"],
|
|
140
|
+
data: new Map(tags),
|
|
141
|
+
}));
|
|
142
|
+
const user = view.root;
|
|
143
|
+
// Read data
|
|
144
|
+
assert.equal(user.name, "Pardes");
|
|
145
|
+
assert.deepEqual([...user.nicknames], ["Alex", "Duder"]);
|
|
146
|
+
assert.equal(user.data.get("Age"), 32);
|
|
147
|
+
assert.equal(user.data.get("Favorite Snack"), "Potato Chips");
|
|
148
|
+
assert.equal(user.data.get("Awake"), true);
|
|
149
|
+
// Mutate data
|
|
150
|
+
user.name = "Pardesio";
|
|
151
|
+
user.nicknames.insertAt(1, "Alexp");
|
|
152
|
+
user.data.set("Awake", false);
|
|
153
|
+
user.data.set("Favorite Sport", "Ultimate Frisbee");
|
|
154
|
+
// Read mutated data
|
|
155
|
+
assert.equal(user.name, "Pardesio");
|
|
156
|
+
assert.deepEqual([...user.nicknames], ["Alex", "Alexp", "Duder"]);
|
|
157
|
+
assert.equal(user.data.get("Age"), 32);
|
|
158
|
+
assert.equal(user.data.get("Awake"), false);
|
|
159
|
+
assert.equal(user.data.get("Favorite Sport"), "Ultimate Frisbee");
|
|
160
|
+
});
|
|
161
|
+
it("can handle undo/redo and transactions", async () => {
|
|
162
|
+
const { container } = await client.createContainer(schema, "2");
|
|
163
|
+
await container.attach();
|
|
164
|
+
const view = container.initialObjects.tree1.viewWith(new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }));
|
|
165
|
+
view.initialize(new User({
|
|
166
|
+
name: "Shrek",
|
|
167
|
+
nicknames: ["Ogre"],
|
|
168
|
+
data: new Map([["Color", "a5bf2e"]]),
|
|
169
|
+
}));
|
|
170
|
+
const user = view.root;
|
|
171
|
+
// Capture the Revertible so that changes can be undone
|
|
172
|
+
let revertible;
|
|
173
|
+
view.events.on("commitApplied", (_, getRevertible) => {
|
|
174
|
+
assert(getRevertible !== undefined);
|
|
175
|
+
revertible = getRevertible();
|
|
176
|
+
});
|
|
177
|
+
// Change a field, then revert the change
|
|
178
|
+
user.name = "Donkey";
|
|
179
|
+
assert.equal(user.name, "Donkey");
|
|
180
|
+
assert(revertible !== undefined);
|
|
181
|
+
revertible.revert();
|
|
182
|
+
assert.equal(user.name, "Shrek");
|
|
183
|
+
// Run a transaction which changes multiple fields, then revert it
|
|
184
|
+
Tree.runTransaction(user, (u) => {
|
|
185
|
+
u.name = "Donkey";
|
|
186
|
+
u.nicknames.removeRange();
|
|
187
|
+
u.data.set("Color", "8e8170");
|
|
188
|
+
});
|
|
189
|
+
assert.equal(user.name, "Donkey");
|
|
190
|
+
assert.equal(user.nicknames.length, 0);
|
|
191
|
+
assert.equal(user.data.get("Color"), "8e8170");
|
|
192
|
+
revertible.revert();
|
|
193
|
+
assert.equal(user.name, "Shrek");
|
|
194
|
+
assert.equal(user.nicknames[0], "Ogre");
|
|
195
|
+
assert.equal(user.data.get("Color"), "a5bf2e");
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
it("can use identifiers and the static Tree APIs", async () => {
|
|
199
|
+
class Widget extends sf.object("Widget", { id: sf.identifier }) {
|
|
200
|
+
}
|
|
201
|
+
const { container } = await client.createContainer(schema, "2");
|
|
202
|
+
await container.attach();
|
|
203
|
+
const view = container.initialObjects.tree1.viewWith(new TreeViewConfiguration({
|
|
204
|
+
schema: sf.array(Widget),
|
|
205
|
+
enableSchemaValidation: true,
|
|
206
|
+
}));
|
|
207
|
+
view.initialize([new Widget({}), new Widget({ id: "fidget" })]);
|
|
208
|
+
const widget = view.root.at(0);
|
|
209
|
+
assert(widget !== undefined);
|
|
210
|
+
const fidget = view.root.at(-1);
|
|
211
|
+
assert(fidget !== undefined);
|
|
212
|
+
// Test various Tree.* APIs and ensure they are working
|
|
213
|
+
assert.equal(Tree.contains(view.root, widget), true);
|
|
214
|
+
assert.equal(Tree.contains(fidget, widget), false);
|
|
215
|
+
assert.equal(Tree.is(fidget, Widget), true);
|
|
216
|
+
assert.equal(Tree.is(view.root, Widget), false);
|
|
217
|
+
assert.equal(Tree.key(widget), 0);
|
|
218
|
+
assert.equal(Tree.key(fidget), 1);
|
|
219
|
+
assert.equal(Tree.parent(widget), view.root);
|
|
220
|
+
assert.equal(Tree.schema(fidget), Widget);
|
|
221
|
+
assert.equal(typeof Tree.shortId(widget), "number");
|
|
222
|
+
assert.equal(Tree.shortId(fidget), "fidget");
|
|
223
|
+
assert.equal(Tree.status(widget), TreeStatus.InDocument);
|
|
224
|
+
});
|
|
225
|
+
it("can listen to events on a recursive tree", async () => {
|
|
226
|
+
class Doll extends sf.objectRecursive("Matryoshka", {
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
228
|
+
nested: sf.optionalRecursive([() => Doll]),
|
|
229
|
+
}) {
|
|
230
|
+
}
|
|
231
|
+
const { container } = await client.createContainer(schema, "2");
|
|
232
|
+
await container.attach();
|
|
233
|
+
const view = container.initialObjects.tree1.viewWith(new TreeViewConfiguration({ schema: Doll, enableSchemaValidation: true }));
|
|
234
|
+
// These nodes in the initial tree are unhydrated...
|
|
235
|
+
const depth1 = new Doll({ nested: new Doll({}) });
|
|
236
|
+
const depth0 = new Doll({ nested: depth1 });
|
|
237
|
+
view.initialize(depth0);
|
|
238
|
+
// ...and confirmed to be the same nodes we get when we read the tree after initialization
|
|
239
|
+
assert.equal(view.root, depth0);
|
|
240
|
+
assert.equal(view.root.nested, depth1);
|
|
241
|
+
// Record a list of the node events fired
|
|
242
|
+
const eventLog = [];
|
|
243
|
+
Tree.on(depth0, "nodeChanged", () => {
|
|
244
|
+
eventLog.push("depth0.nested changed");
|
|
245
|
+
});
|
|
246
|
+
Tree.on(depth1, "nodeChanged", () => {
|
|
247
|
+
eventLog.push("depth1.nested changed");
|
|
248
|
+
});
|
|
249
|
+
// This event registration happens on the unhydrated (not yet inserted) node
|
|
250
|
+
const newDepth2 = new Doll({});
|
|
251
|
+
Tree.on(newDepth2, "nodeChanged", () => {
|
|
252
|
+
eventLog.push("depth2.nested changed");
|
|
253
|
+
});
|
|
254
|
+
// Fire the events by doing mutations
|
|
255
|
+
depth1.nested = newDepth2; // "depth1.nested changed"
|
|
256
|
+
assert.equal(depth1.nested, newDepth2);
|
|
257
|
+
depth1.nested.nested = new Doll({}); // "depth2.nested changed"
|
|
258
|
+
depth0.nested = new Doll({}); // "depth0.nested changed"
|
|
259
|
+
// Ensure the events fired in the expected order
|
|
260
|
+
assert.deepEqual(eventLog, [
|
|
261
|
+
"depth1.nested changed",
|
|
262
|
+
"depth2.nested changed",
|
|
263
|
+
"depth0.nested changed",
|
|
264
|
+
]);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
137
267
|
});
|
|
138
268
|
}
|
|
139
269
|
//# sourceMappingURL=tree.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.spec.js","sourceRoot":"","sources":["../../src/test/tree.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { AzureClient } from \"@fluidframework/azure-client\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport { ContainerSchema, type IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\nimport {\n\tTreeViewConfiguration,\n\tSchemaFactory,\n\ttype TreeView,\n\ttype ITree,\n} from \"@fluidframework/tree\";\nimport { SharedTree } from \"@fluidframework/tree/internal\";\nimport type { AxiosResponse } from \"axios\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n} from \"./AzureClientFactory.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { getTestMatrix } from \"./utils.js\";\n\nconst sf = new SchemaFactory(\"d302b84c-75f6-4ecd-9663-524f467013e3\");\n\n/**\n * Define a class that is an array of strings\n * This class is used to create an array in the SharedTree\n */\nclass StringArray extends sf.array(\"StringArray\", sf.string) {\n\t/**\n\t * Remove the first item in the list if the list is not empty\n\t */\n\tpublic removeFirst(): void {\n\t\tif (this.length > 0) this.removeAt(0);\n\t}\n\n\t/**\n\t * Add an item to the beginning of the list\n\t */\n\tpublic insertNew(str: string): void {\n\t\tthis.insertAtStart(str);\n\t}\n}\n\n/**\n * This object is passed into the SharedTree via the schematize method.\n */\nconst treeConfiguration = new TreeViewConfiguration(\n\t// Specify the root type - StringArray.\n\t{ schema: StringArray },\n);\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`SharedTree with AzureClient (${testOpts.variant})`, () => {\n\t\tconst connectTimeoutMs = 10_000;\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tlet client: AzureClient;\n\t\tconst schema = {\n\t\t\tinitialObjects: {\n\t\t\t\ttree1: SharedTree,\n\t\t\t},\n\t\t} satisfies ContainerSchema;\n\n\t\tbeforeEach(\"createAzureClient\", () => {\n\t\t\tclient = createAzureClient();\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test when an Azure Client container is created,\n\t\t * it can set the initial objects to SharedTree and do basic SharedTree ops.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned.\n\t\t */\n\t\tit(\"can create a container with SharedTree and do basic ops\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container1: IFluidContainer;\n\t\t\tlet treeData: TreeView<typeof StringArray>;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.createContainerWithSharedTree,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container: container1 } = await client.getContainer(containerId, schema, \"2\"));\n\n\t\t\t\ttreeData = (container1.initialObjects.tree1 as ITree).viewWith(\n\t\t\t\t\ttreeConfiguration, // This is defined in schema.ts\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t({ container: container1 } = await client.createContainer(schema, \"2\"));\n\n\t\t\t\ttreeData = (container1.initialObjects.tree1 as ITree).viewWith(\n\t\t\t\t\ttreeConfiguration, // This is defined in schema.ts\n\t\t\t\t);\n\t\t\t\ttreeData.initialize(new StringArray([]));\n\n\t\t\t\tcontainerId = await container1.attach();\n\t\t\t}\n\n\t\t\tif (container1.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container1.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container1 connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttreeData.root.insertNew(\"test string 1\");\n\t\t\tassert.strictEqual(treeData.root.length, 1);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 1\");\n\n\t\t\ttreeData.root.insertNew(\"test string 2\");\n\t\t\tassert.strictEqual(treeData.root.length, 2);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 2\");\n\t\t\tassert.strictEqual(treeData.root.at(1), \"test string 1\");\n\n\t\t\ttreeData.root.removeFirst();\n\t\t\tassert.strictEqual(treeData.root.length, 1);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 1\");\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test when an Azure Client container is created,\n\t\t * and it can be loaded by another container with SharedTree and do basic SharedTree ops.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned.\n\t\t */\n\t\tit(\"can create/load a container with SharedTree collaborate with basic ops\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container1: IFluidContainer;\n\t\t\tlet treeData1: TreeView<typeof StringArray>;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.createLoadContainerWithSharedTree,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container: container1 } = await client.getContainer(containerId, schema, \"2\"));\n\n\t\t\t\ttreeData1 = (container1.initialObjects.tree1 as ITree).viewWith(treeConfiguration);\n\t\t\t} else {\n\t\t\t\t({ container: container1 } = await client.createContainer(schema, \"2\"));\n\n\t\t\t\ttreeData1 = (container1.initialObjects.tree1 as ITree).viewWith(treeConfiguration);\n\t\t\t\ttreeData1.initialize(new StringArray([]));\n\n\t\t\t\tcontainerId = await container1.attach();\n\t\t\t}\n\n\t\t\tif (container1.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container1.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container1 connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttreeData1.root.insertNew(\"test string 1\");\n\n\t\t\tconst resources = client.getContainer(containerId, schema, \"2\");\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tresources,\n\t\t\t\t() => true,\n\t\t\t\t\"container cannot be retrieved from Azure Fluid Relay\",\n\t\t\t);\n\t\t\tconst { container: container2 } = await resources;\n\t\t\tassert.deepStrictEqual(\n\t\t\t\tObject.keys(container2.initialObjects),\n\t\t\t\tObject.keys(schema.initialObjects),\n\t\t\t);\n\n\t\t\tif (container2.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container2.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container2 connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst treeData2 = container2.initialObjects.tree1.viewWith(treeConfiguration);\n\t\t\tassert.strictEqual(treeData2.root.length, 1);\n\t\t\tassert.strictEqual(treeData2.root.at(0), \"test string 1\");\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tree.spec.js","sourceRoot":"","sources":["../../src/test/tree.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { AzureClient } from \"@fluidframework/azure-client\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport { ContainerSchema, type IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\nimport { TreeViewConfiguration, SchemaFactory, type TreeView } from \"@fluidframework/tree\";\nimport { SharedTree, Tree, TreeStatus, type Revertible } from \"@fluidframework/tree/internal\";\nimport type { AxiosResponse } from \"axios\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n} from \"./AzureClientFactory.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { getTestMatrix } from \"./utils.js\";\n\nconst sf = new SchemaFactory(\"d302b84c-75f6-4ecd-9663-524f467013e3\");\n\n/**\n * Define a class that is an array of strings\n * This class is used to create an array in the SharedTree\n */\nclass StringArray extends sf.array(\"StringArray\", sf.string) {\n\t/**\n\t * Remove the first item in the list if the list is not empty\n\t */\n\tpublic removeFirst(): void {\n\t\tif (this.length > 0) this.removeAt(0);\n\t}\n\n\t/**\n\t * Add an item to the beginning of the list\n\t */\n\tpublic insertNew(str: string): void {\n\t\tthis.insertAtStart(str);\n\t}\n}\n\n/**\n * This object is passed into the SharedTree via the schematize method.\n */\nconst treeConfiguration = new TreeViewConfiguration(\n\t// Specify the root type - StringArray.\n\t{ schema: StringArray },\n);\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`SharedTree with AzureClient (${testOpts.variant})`, () => {\n\t\tconst connectTimeoutMs = 10_000;\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tlet client: AzureClient;\n\t\tconst schema = {\n\t\t\tinitialObjects: {\n\t\t\t\ttree1: SharedTree,\n\t\t\t},\n\t\t} satisfies ContainerSchema;\n\n\t\tbeforeEach(\"createAzureClient\", () => {\n\t\t\tclient = createAzureClient();\n\t\t});\n\n\t\tasync function waitForConnection(container: IFluidContainer): Promise<void> {\n\t\t\tif (container.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container1 connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Either creates a new azure client with a SharedTree, or loads an azure client from the existing summary tree (the \"ephemeral\" case).\n\t\t */\n\t\tasync function createOrLoad(\n\t\t\tsummaryTree?: (typeof ephemeralSummaryTrees)[keyof typeof ephemeralSummaryTrees],\n\t\t): Promise<{ containerId: string; treeData: TreeView<typeof StringArray> }> {\n\t\t\tlet containerId: string;\n\t\t\tlet treeData: TreeView<typeof StringArray>;\n\n\t\t\tif (summaryTree === undefined) {\n\t\t\t\tconst { container } = await client.createContainer(schema, \"2\");\n\t\t\t\ttreeData = container.initialObjects.tree1.viewWith(treeConfiguration);\n\t\t\t\ttreeData.initialize(new StringArray([]));\n\t\t\t\tcontainerId = await container.attach();\n\t\t\t\tawait waitForConnection(container);\n\t\t\t} else {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tsummaryTree,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\tconst { container } = await client.getContainer(containerId, schema, \"2\");\n\t\t\t\ttreeData = container.initialObjects.tree1.viewWith(treeConfiguration);\n\t\t\t\tawait waitForConnection(container);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcontainerId,\n\t\t\t\ttreeData,\n\t\t\t};\n\t\t}\n\n\t\tit(\"can create/load a container with SharedTree and do basic ops\", async () => {\n\t\t\tconst { treeData } = await createOrLoad(\n\t\t\t\tisEphemeral ? ephemeralSummaryTrees.createContainerWithSharedTree : undefined,\n\t\t\t);\n\n\t\t\ttreeData.root.insertNew(\"test string 1\");\n\t\t\tassert.strictEqual(treeData.root.length, 1);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 1\");\n\n\t\t\ttreeData.root.insertNew(\"test string 2\");\n\t\t\tassert.strictEqual(treeData.root.length, 2);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 2\");\n\t\t\tassert.strictEqual(treeData.root.at(1), \"test string 1\");\n\n\t\t\ttreeData.root.removeFirst();\n\t\t\tassert.strictEqual(treeData.root.length, 1);\n\t\t\tassert.strictEqual(treeData.root.at(0), \"test string 1\");\n\t\t});\n\n\t\tit(\"can create/load a container with SharedTree collaborate with basic ops\", async () => {\n\t\t\tconst { containerId, treeData } = await createOrLoad(\n\t\t\t\tisEphemeral ? ephemeralSummaryTrees.createLoadContainerWithSharedTree : undefined,\n\t\t\t);\n\n\t\t\ttreeData.root.insertNew(\"test string 1\");\n\n\t\t\tconst resources = client.getContainer(containerId, schema, \"2\");\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tresources,\n\t\t\t\t() => true,\n\t\t\t\t\"container cannot be retrieved from Azure Fluid Relay\",\n\t\t\t);\n\t\t\tconst { container: container2 } = await resources;\n\t\t\tassert.deepStrictEqual(\n\t\t\t\tObject.keys(container2.initialObjects),\n\t\t\t\tObject.keys(schema.initialObjects),\n\t\t\t);\n\n\t\t\tif (container2.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container2.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container2 connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst treeData2 = container2.initialObjects.tree1.viewWith(treeConfiguration);\n\t\t\tassert.strictEqual(treeData2.root.length, 1);\n\t\t\tassert.strictEqual(treeData2.root.at(0), \"test string 1\");\n\t\t});\n\n\t\tif (!isEphemeral) {\n\t\t\t{\n\t\t\t\tclass Nicknames extends sf.array(\"Nicknames\", sf.string) {}\n\t\t\t\tclass UserData extends sf.map(\"UserData\", [sf.string, sf.number, sf.boolean]) {}\n\t\t\t\tclass User extends sf.object(\"User\", {\n\t\t\t\t\tname: sf.string,\n\t\t\t\t\tnicknames: Nicknames,\n\t\t\t\t\tdata: UserData,\n\t\t\t\t}) {}\n\n\t\t\t\tit(\"can read and edit data\", async () => {\n\t\t\t\t\tconst { container } = await client.createContainer(schema, \"2\");\n\t\t\t\t\tawait container.attach();\n\t\t\t\t\tconst view = container.initialObjects.tree1.viewWith(\n\t\t\t\t\t\tnew TreeViewConfiguration({ schema: User, enableSchemaValidation: true }),\n\t\t\t\t\t);\n\n\t\t\t\t\tconst tags: [string, string | number | boolean][] = [\n\t\t\t\t\t\t[\"Age\", 32],\n\t\t\t\t\t\t[\"Favorite Snack\", \"Potato Chips\"],\n\t\t\t\t\t\t[\"Awake\", true],\n\t\t\t\t\t];\n\n\t\t\t\t\tview.initialize(\n\t\t\t\t\t\tnew User({\n\t\t\t\t\t\t\tname: \"Pardes\",\n\t\t\t\t\t\t\tnicknames: [\"Alex\", \"Duder\"],\n\t\t\t\t\t\t\tdata: new Map(tags),\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\tconst user = view.root;\n\t\t\t\t\t// Read data\n\t\t\t\t\tassert.equal(user.name, \"Pardes\");\n\t\t\t\t\tassert.deepEqual([...user.nicknames], [\"Alex\", \"Duder\"]);\n\t\t\t\t\tassert.equal(user.data.get(\"Age\"), 32);\n\t\t\t\t\tassert.equal(user.data.get(\"Favorite Snack\"), \"Potato Chips\");\n\t\t\t\t\tassert.equal(user.data.get(\"Awake\"), true);\n\t\t\t\t\t// Mutate data\n\t\t\t\t\tuser.name = \"Pardesio\";\n\t\t\t\t\tuser.nicknames.insertAt(1, \"Alexp\");\n\t\t\t\t\tuser.data.set(\"Awake\", false);\n\t\t\t\t\tuser.data.set(\"Favorite Sport\", \"Ultimate Frisbee\");\n\t\t\t\t\t// Read mutated data\n\t\t\t\t\tassert.equal(user.name, \"Pardesio\");\n\t\t\t\t\tassert.deepEqual([...user.nicknames], [\"Alex\", \"Alexp\", \"Duder\"]);\n\t\t\t\t\tassert.equal(user.data.get(\"Age\"), 32);\n\t\t\t\t\tassert.equal(user.data.get(\"Awake\"), false);\n\t\t\t\t\tassert.equal(user.data.get(\"Favorite Sport\"), \"Ultimate Frisbee\");\n\t\t\t\t});\n\n\t\t\t\tit(\"can handle undo/redo and transactions\", async () => {\n\t\t\t\t\tconst { container } = await client.createContainer(schema, \"2\");\n\t\t\t\t\tawait container.attach();\n\t\t\t\t\tconst view = container.initialObjects.tree1.viewWith(\n\t\t\t\t\t\tnew TreeViewConfiguration({ schema: User, enableSchemaValidation: true }),\n\t\t\t\t\t);\n\n\t\t\t\t\tview.initialize(\n\t\t\t\t\t\tnew User({\n\t\t\t\t\t\t\tname: \"Shrek\",\n\t\t\t\t\t\t\tnicknames: [\"Ogre\"],\n\t\t\t\t\t\t\tdata: new Map([[\"Color\", \"a5bf2e\"]]),\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\tconst user = view.root;\n\t\t\t\t\t// Capture the Revertible so that changes can be undone\n\t\t\t\t\tlet revertible: Revertible | undefined;\n\t\t\t\t\tview.events.on(\"commitApplied\", (_, getRevertible) => {\n\t\t\t\t\t\tassert(getRevertible !== undefined);\n\t\t\t\t\t\trevertible = getRevertible();\n\t\t\t\t\t});\n\t\t\t\t\t// Change a field, then revert the change\n\t\t\t\t\tuser.name = \"Donkey\";\n\t\t\t\t\tassert.equal(user.name, \"Donkey\");\n\t\t\t\t\tassert(revertible !== undefined);\n\t\t\t\t\trevertible.revert();\n\t\t\t\t\tassert.equal(user.name, \"Shrek\");\n\t\t\t\t\t// Run a transaction which changes multiple fields, then revert it\n\t\t\t\t\tTree.runTransaction(user, (u) => {\n\t\t\t\t\t\tu.name = \"Donkey\";\n\t\t\t\t\t\tu.nicknames.removeRange();\n\t\t\t\t\t\tu.data.set(\"Color\", \"8e8170\");\n\t\t\t\t\t});\n\n\t\t\t\t\tassert.equal(user.name, \"Donkey\");\n\t\t\t\t\tassert.equal(user.nicknames.length, 0);\n\t\t\t\t\tassert.equal(user.data.get(\"Color\"), \"8e8170\");\n\t\t\t\t\trevertible.revert();\n\t\t\t\t\tassert.equal(user.name, \"Shrek\");\n\t\t\t\t\tassert.equal(user.nicknames[0], \"Ogre\");\n\t\t\t\t\tassert.equal(user.data.get(\"Color\"), \"a5bf2e\");\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tit(\"can use identifiers and the static Tree APIs\", async () => {\n\t\t\t\tclass Widget extends sf.object(\"Widget\", { id: sf.identifier }) {}\n\n\t\t\t\tconst { container } = await client.createContainer(schema, \"2\");\n\t\t\t\tawait container.attach();\n\t\t\t\tconst view = container.initialObjects.tree1.viewWith(\n\t\t\t\t\tnew TreeViewConfiguration({\n\t\t\t\t\t\tschema: sf.array(Widget),\n\t\t\t\t\t\tenableSchemaValidation: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tview.initialize([new Widget({}), new Widget({ id: \"fidget\" })]);\n\n\t\t\t\tconst widget = view.root.at(0);\n\t\t\t\tassert(widget !== undefined);\n\t\t\t\tconst fidget = view.root.at(-1);\n\t\t\t\tassert(fidget !== undefined);\n\t\t\t\t// Test various Tree.* APIs and ensure they are working\n\t\t\t\tassert.equal(Tree.contains(view.root, widget), true);\n\t\t\t\tassert.equal(Tree.contains(fidget, widget), false);\n\t\t\t\tassert.equal(Tree.is(fidget, Widget), true);\n\t\t\t\tassert.equal(Tree.is(view.root, Widget), false);\n\t\t\t\tassert.equal(Tree.key(widget), 0);\n\t\t\t\tassert.equal(Tree.key(fidget), 1);\n\t\t\t\tassert.equal(Tree.parent(widget), view.root);\n\t\t\t\tassert.equal(Tree.schema(fidget), Widget);\n\t\t\t\tassert.equal(typeof Tree.shortId(widget), \"number\");\n\t\t\t\tassert.equal(Tree.shortId(fidget), \"fidget\");\n\t\t\t\tassert.equal(Tree.status(widget), TreeStatus.InDocument);\n\t\t\t});\n\n\t\t\tit(\"can listen to events on a recursive tree\", async () => {\n\t\t\t\tclass Doll extends sf.objectRecursive(\"Matryoshka\", {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\n\t\t\t\t\tnested: sf.optionalRecursive([() => Doll]),\n\t\t\t\t}) {}\n\n\t\t\t\tconst { container } = await client.createContainer(schema, \"2\");\n\t\t\t\tawait container.attach();\n\t\t\t\tconst view = container.initialObjects.tree1.viewWith(\n\t\t\t\t\tnew TreeViewConfiguration({ schema: Doll, enableSchemaValidation: true }),\n\t\t\t\t);\n\n\t\t\t\t// These nodes in the initial tree are unhydrated...\n\t\t\t\tconst depth1 = new Doll({ nested: new Doll({}) });\n\t\t\t\tconst depth0 = new Doll({ nested: depth1 });\n\t\t\t\tview.initialize(depth0);\n\t\t\t\t// ...and confirmed to be the same nodes we get when we read the tree after initialization\n\t\t\t\tassert.equal(view.root, depth0);\n\t\t\t\tassert.equal(view.root.nested, depth1);\n\t\t\t\t// Record a list of the node events fired\n\t\t\t\tconst eventLog: string[] = [];\n\t\t\t\tTree.on(depth0, \"nodeChanged\", () => {\n\t\t\t\t\teventLog.push(\"depth0.nested changed\");\n\t\t\t\t});\n\t\t\t\tTree.on(depth1, \"nodeChanged\", () => {\n\t\t\t\t\teventLog.push(\"depth1.nested changed\");\n\t\t\t\t});\n\t\t\t\t// This event registration happens on the unhydrated (not yet inserted) node\n\t\t\t\tconst newDepth2 = new Doll({});\n\t\t\t\tTree.on(newDepth2, \"nodeChanged\", () => {\n\t\t\t\t\teventLog.push(\"depth2.nested changed\");\n\t\t\t\t});\n\t\t\t\t// Fire the events by doing mutations\n\t\t\t\tdepth1.nested = newDepth2; // \"depth1.nested changed\"\n\t\t\t\tassert.equal(depth1.nested, newDepth2);\n\t\t\t\tdepth1.nested.nested = new Doll({}); // \"depth2.nested changed\"\n\t\t\t\tdepth0.nested = new Doll({}); // \"depth0.nested changed\"\n\t\t\t\t// Ensure the events fired in the expected order\n\t\t\t\tassert.deepEqual(eventLog, [\n\t\t\t\t\t\"depth1.nested changed\",\n\t\t\t\t\t\"depth2.nested changed\",\n\t\t\t\t\t\"depth0.nested changed\",\n\t\t\t\t]);\n\t\t\t});\n\t\t}\n\t});\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/azure-end-to-end-tests",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0-276326",
|
|
4
4
|
"description": "Azure client end to end tests",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/microsoft/FluidFramework.git",
|
|
9
|
-
"directory": "
|
|
9
|
+
"directory": "packages/service-clients/end-to-end-tests/azure-client"
|
|
10
10
|
},
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"author": "Microsoft and contributors",
|
|
@@ -33,27 +33,27 @@
|
|
|
33
33
|
"temp-directory": "nyc/.nyc_output"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@fluid-experimental/data-objects": "
|
|
37
|
-
"@fluid-internal/client-utils": "
|
|
38
|
-
"@fluid-internal/mocha-test-setup": "
|
|
39
|
-
"@fluidframework/aqueduct": "
|
|
40
|
-
"@fluidframework/azure-client": "
|
|
36
|
+
"@fluid-experimental/data-objects": "2.1.0-276326",
|
|
37
|
+
"@fluid-internal/client-utils": "2.1.0-276326",
|
|
38
|
+
"@fluid-internal/mocha-test-setup": "2.1.0-276326",
|
|
39
|
+
"@fluidframework/aqueduct": "2.1.0-276326",
|
|
40
|
+
"@fluidframework/azure-client": "2.1.0-276326",
|
|
41
41
|
"@fluidframework/azure-client-legacy": "npm:@fluidframework/azure-client@^1.2.0",
|
|
42
|
-
"@fluidframework/container-definitions": "
|
|
43
|
-
"@fluidframework/container-loader": "
|
|
44
|
-
"@fluidframework/core-interfaces": "
|
|
45
|
-
"@fluidframework/counter": "
|
|
46
|
-
"@fluidframework/datastore-definitions": "
|
|
47
|
-
"@fluidframework/fluid-static": "
|
|
48
|
-
"@fluidframework/map": "
|
|
42
|
+
"@fluidframework/container-definitions": "2.1.0-276326",
|
|
43
|
+
"@fluidframework/container-loader": "2.1.0-276326",
|
|
44
|
+
"@fluidframework/core-interfaces": "2.1.0-276326",
|
|
45
|
+
"@fluidframework/counter": "2.1.0-276326",
|
|
46
|
+
"@fluidframework/datastore-definitions": "2.1.0-276326",
|
|
47
|
+
"@fluidframework/fluid-static": "2.1.0-276326",
|
|
48
|
+
"@fluidframework/map": "2.1.0-276326",
|
|
49
49
|
"@fluidframework/map-legacy": "npm:@fluidframework/map@^1.4.0",
|
|
50
|
-
"@fluidframework/matrix": "
|
|
51
|
-
"@fluidframework/runtime-definitions": "
|
|
52
|
-
"@fluidframework/sequence": "
|
|
53
|
-
"@fluidframework/telemetry-utils": "
|
|
54
|
-
"@fluidframework/test-runtime-utils": "
|
|
55
|
-
"@fluidframework/test-utils": "
|
|
56
|
-
"@fluidframework/tree": "
|
|
50
|
+
"@fluidframework/matrix": "2.1.0-276326",
|
|
51
|
+
"@fluidframework/runtime-definitions": "2.1.0-276326",
|
|
52
|
+
"@fluidframework/sequence": "2.1.0-276326",
|
|
53
|
+
"@fluidframework/telemetry-utils": "2.1.0-276326",
|
|
54
|
+
"@fluidframework/test-runtime-utils": "2.1.0-276326",
|
|
55
|
+
"@fluidframework/test-utils": "2.1.0-276326",
|
|
56
|
+
"@fluidframework/tree": "2.1.0-276326",
|
|
57
57
|
"axios": "^1.6.2",
|
|
58
58
|
"cross-env": "^7.0.3",
|
|
59
59
|
"mocha": "^10.2.0",
|
package/src/test/tree.spec.ts
CHANGED
|
@@ -9,13 +9,8 @@ import { AzureClient } from "@fluidframework/azure-client";
|
|
|
9
9
|
import { ConnectionState } from "@fluidframework/container-loader";
|
|
10
10
|
import { ContainerSchema, type IFluidContainer } from "@fluidframework/fluid-static";
|
|
11
11
|
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
SchemaFactory,
|
|
15
|
-
type TreeView,
|
|
16
|
-
type ITree,
|
|
17
|
-
} from "@fluidframework/tree";
|
|
18
|
-
import { SharedTree } from "@fluidframework/tree/internal";
|
|
12
|
+
import { TreeViewConfiguration, SchemaFactory, type TreeView } from "@fluidframework/tree";
|
|
13
|
+
import { SharedTree, Tree, TreeStatus, type Revertible } from "@fluidframework/tree/internal";
|
|
19
14
|
import type { AxiosResponse } from "axios";
|
|
20
15
|
|
|
21
16
|
import {
|
|
@@ -72,46 +67,53 @@ for (const testOpts of testMatrix) {
|
|
|
72
67
|
client = createAzureClient();
|
|
73
68
|
});
|
|
74
69
|
|
|
70
|
+
async function waitForConnection(container: IFluidContainer): Promise<void> {
|
|
71
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
72
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
73
|
+
durationMs: connectTimeoutMs,
|
|
74
|
+
errorMsg: "container1 connect() timeout",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
75
79
|
/**
|
|
76
|
-
*
|
|
77
|
-
* it can set the initial objects to SharedTree and do basic SharedTree ops.
|
|
78
|
-
*
|
|
79
|
-
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
80
|
-
* be returned.
|
|
80
|
+
* Either creates a new azure client with a SharedTree, or loads an azure client from the existing summary tree (the "ephemeral" case).
|
|
81
81
|
*/
|
|
82
|
-
|
|
82
|
+
async function createOrLoad(
|
|
83
|
+
summaryTree?: (typeof ephemeralSummaryTrees)[keyof typeof ephemeralSummaryTrees],
|
|
84
|
+
): Promise<{ containerId: string; treeData: TreeView<typeof StringArray> }> {
|
|
83
85
|
let containerId: string;
|
|
84
|
-
let container1: IFluidContainer;
|
|
85
86
|
let treeData: TreeView<typeof StringArray>;
|
|
86
|
-
|
|
87
|
+
|
|
88
|
+
if (summaryTree === undefined) {
|
|
89
|
+
const { container } = await client.createContainer(schema, "2");
|
|
90
|
+
treeData = container.initialObjects.tree1.viewWith(treeConfiguration);
|
|
91
|
+
treeData.initialize(new StringArray([]));
|
|
92
|
+
containerId = await container.attach();
|
|
93
|
+
await waitForConnection(container);
|
|
94
|
+
} else {
|
|
87
95
|
const containerResponse: AxiosResponse | undefined = await createContainerFromPayload(
|
|
88
|
-
|
|
96
|
+
summaryTree,
|
|
89
97
|
"test-user-id-1",
|
|
90
98
|
"test-user-name-1",
|
|
91
99
|
);
|
|
92
|
-
containerId = getContainerIdFromPayloadResponse(containerResponse);
|
|
93
|
-
({ container: container1 } = await client.getContainer(containerId, schema, "2"));
|
|
94
|
-
|
|
95
|
-
treeData = (container1.initialObjects.tree1 as ITree).viewWith(
|
|
96
|
-
treeConfiguration, // This is defined in schema.ts
|
|
97
|
-
);
|
|
98
|
-
} else {
|
|
99
|
-
({ container: container1 } = await client.createContainer(schema, "2"));
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
containerId = await container1.attach();
|
|
101
|
+
containerId = getContainerIdFromPayloadResponse(containerResponse);
|
|
102
|
+
const { container } = await client.getContainer(containerId, schema, "2");
|
|
103
|
+
treeData = container.initialObjects.tree1.viewWith(treeConfiguration);
|
|
104
|
+
await waitForConnection(container);
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
return {
|
|
108
|
+
containerId,
|
|
109
|
+
treeData,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
it("can create/load a container with SharedTree and do basic ops", async () => {
|
|
114
|
+
const { treeData } = await createOrLoad(
|
|
115
|
+
isEphemeral ? ephemeralSummaryTrees.createContainerWithSharedTree : undefined,
|
|
116
|
+
);
|
|
115
117
|
|
|
116
118
|
treeData.root.insertNew("test string 1");
|
|
117
119
|
assert.strictEqual(treeData.root.length, 1);
|
|
@@ -127,44 +129,12 @@ for (const testOpts of testMatrix) {
|
|
|
127
129
|
assert.strictEqual(treeData.root.at(0), "test string 1");
|
|
128
130
|
});
|
|
129
131
|
|
|
130
|
-
/**
|
|
131
|
-
* Scenario: test when an Azure Client container is created,
|
|
132
|
-
* and it can be loaded by another container with SharedTree and do basic SharedTree ops.
|
|
133
|
-
*
|
|
134
|
-
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
135
|
-
* be returned.
|
|
136
|
-
*/
|
|
137
132
|
it("can create/load a container with SharedTree collaborate with basic ops", async () => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (isEphemeral) {
|
|
142
|
-
const containerResponse: AxiosResponse | undefined = await createContainerFromPayload(
|
|
143
|
-
ephemeralSummaryTrees.createLoadContainerWithSharedTree,
|
|
144
|
-
"test-user-id-1",
|
|
145
|
-
"test-user-name-1",
|
|
146
|
-
);
|
|
147
|
-
containerId = getContainerIdFromPayloadResponse(containerResponse);
|
|
148
|
-
({ container: container1 } = await client.getContainer(containerId, schema, "2"));
|
|
149
|
-
|
|
150
|
-
treeData1 = (container1.initialObjects.tree1 as ITree).viewWith(treeConfiguration);
|
|
151
|
-
} else {
|
|
152
|
-
({ container: container1 } = await client.createContainer(schema, "2"));
|
|
153
|
-
|
|
154
|
-
treeData1 = (container1.initialObjects.tree1 as ITree).viewWith(treeConfiguration);
|
|
155
|
-
treeData1.initialize(new StringArray([]));
|
|
156
|
-
|
|
157
|
-
containerId = await container1.attach();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (container1.connectionState !== ConnectionState.Connected) {
|
|
161
|
-
await timeoutPromise((resolve) => container1.once("connected", () => resolve()), {
|
|
162
|
-
durationMs: connectTimeoutMs,
|
|
163
|
-
errorMsg: "container1 connect() timeout",
|
|
164
|
-
});
|
|
165
|
-
}
|
|
133
|
+
const { containerId, treeData } = await createOrLoad(
|
|
134
|
+
isEphemeral ? ephemeralSummaryTrees.createLoadContainerWithSharedTree : undefined,
|
|
135
|
+
);
|
|
166
136
|
|
|
167
|
-
|
|
137
|
+
treeData.root.insertNew("test string 1");
|
|
168
138
|
|
|
169
139
|
const resources = client.getContainer(containerId, schema, "2");
|
|
170
140
|
await assert.doesNotReject(
|
|
@@ -189,5 +159,179 @@ for (const testOpts of testMatrix) {
|
|
|
189
159
|
assert.strictEqual(treeData2.root.length, 1);
|
|
190
160
|
assert.strictEqual(treeData2.root.at(0), "test string 1");
|
|
191
161
|
});
|
|
162
|
+
|
|
163
|
+
if (!isEphemeral) {
|
|
164
|
+
{
|
|
165
|
+
class Nicknames extends sf.array("Nicknames", sf.string) {}
|
|
166
|
+
class UserData extends sf.map("UserData", [sf.string, sf.number, sf.boolean]) {}
|
|
167
|
+
class User extends sf.object("User", {
|
|
168
|
+
name: sf.string,
|
|
169
|
+
nicknames: Nicknames,
|
|
170
|
+
data: UserData,
|
|
171
|
+
}) {}
|
|
172
|
+
|
|
173
|
+
it("can read and edit data", async () => {
|
|
174
|
+
const { container } = await client.createContainer(schema, "2");
|
|
175
|
+
await container.attach();
|
|
176
|
+
const view = container.initialObjects.tree1.viewWith(
|
|
177
|
+
new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const tags: [string, string | number | boolean][] = [
|
|
181
|
+
["Age", 32],
|
|
182
|
+
["Favorite Snack", "Potato Chips"],
|
|
183
|
+
["Awake", true],
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
view.initialize(
|
|
187
|
+
new User({
|
|
188
|
+
name: "Pardes",
|
|
189
|
+
nicknames: ["Alex", "Duder"],
|
|
190
|
+
data: new Map(tags),
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const user = view.root;
|
|
195
|
+
// Read data
|
|
196
|
+
assert.equal(user.name, "Pardes");
|
|
197
|
+
assert.deepEqual([...user.nicknames], ["Alex", "Duder"]);
|
|
198
|
+
assert.equal(user.data.get("Age"), 32);
|
|
199
|
+
assert.equal(user.data.get("Favorite Snack"), "Potato Chips");
|
|
200
|
+
assert.equal(user.data.get("Awake"), true);
|
|
201
|
+
// Mutate data
|
|
202
|
+
user.name = "Pardesio";
|
|
203
|
+
user.nicknames.insertAt(1, "Alexp");
|
|
204
|
+
user.data.set("Awake", false);
|
|
205
|
+
user.data.set("Favorite Sport", "Ultimate Frisbee");
|
|
206
|
+
// Read mutated data
|
|
207
|
+
assert.equal(user.name, "Pardesio");
|
|
208
|
+
assert.deepEqual([...user.nicknames], ["Alex", "Alexp", "Duder"]);
|
|
209
|
+
assert.equal(user.data.get("Age"), 32);
|
|
210
|
+
assert.equal(user.data.get("Awake"), false);
|
|
211
|
+
assert.equal(user.data.get("Favorite Sport"), "Ultimate Frisbee");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("can handle undo/redo and transactions", async () => {
|
|
215
|
+
const { container } = await client.createContainer(schema, "2");
|
|
216
|
+
await container.attach();
|
|
217
|
+
const view = container.initialObjects.tree1.viewWith(
|
|
218
|
+
new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
view.initialize(
|
|
222
|
+
new User({
|
|
223
|
+
name: "Shrek",
|
|
224
|
+
nicknames: ["Ogre"],
|
|
225
|
+
data: new Map([["Color", "a5bf2e"]]),
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const user = view.root;
|
|
230
|
+
// Capture the Revertible so that changes can be undone
|
|
231
|
+
let revertible: Revertible | undefined;
|
|
232
|
+
view.events.on("commitApplied", (_, getRevertible) => {
|
|
233
|
+
assert(getRevertible !== undefined);
|
|
234
|
+
revertible = getRevertible();
|
|
235
|
+
});
|
|
236
|
+
// Change a field, then revert the change
|
|
237
|
+
user.name = "Donkey";
|
|
238
|
+
assert.equal(user.name, "Donkey");
|
|
239
|
+
assert(revertible !== undefined);
|
|
240
|
+
revertible.revert();
|
|
241
|
+
assert.equal(user.name, "Shrek");
|
|
242
|
+
// Run a transaction which changes multiple fields, then revert it
|
|
243
|
+
Tree.runTransaction(user, (u) => {
|
|
244
|
+
u.name = "Donkey";
|
|
245
|
+
u.nicknames.removeRange();
|
|
246
|
+
u.data.set("Color", "8e8170");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
assert.equal(user.name, "Donkey");
|
|
250
|
+
assert.equal(user.nicknames.length, 0);
|
|
251
|
+
assert.equal(user.data.get("Color"), "8e8170");
|
|
252
|
+
revertible.revert();
|
|
253
|
+
assert.equal(user.name, "Shrek");
|
|
254
|
+
assert.equal(user.nicknames[0], "Ogre");
|
|
255
|
+
assert.equal(user.data.get("Color"), "a5bf2e");
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
it("can use identifiers and the static Tree APIs", async () => {
|
|
260
|
+
class Widget extends sf.object("Widget", { id: sf.identifier }) {}
|
|
261
|
+
|
|
262
|
+
const { container } = await client.createContainer(schema, "2");
|
|
263
|
+
await container.attach();
|
|
264
|
+
const view = container.initialObjects.tree1.viewWith(
|
|
265
|
+
new TreeViewConfiguration({
|
|
266
|
+
schema: sf.array(Widget),
|
|
267
|
+
enableSchemaValidation: true,
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
view.initialize([new Widget({}), new Widget({ id: "fidget" })]);
|
|
272
|
+
|
|
273
|
+
const widget = view.root.at(0);
|
|
274
|
+
assert(widget !== undefined);
|
|
275
|
+
const fidget = view.root.at(-1);
|
|
276
|
+
assert(fidget !== undefined);
|
|
277
|
+
// Test various Tree.* APIs and ensure they are working
|
|
278
|
+
assert.equal(Tree.contains(view.root, widget), true);
|
|
279
|
+
assert.equal(Tree.contains(fidget, widget), false);
|
|
280
|
+
assert.equal(Tree.is(fidget, Widget), true);
|
|
281
|
+
assert.equal(Tree.is(view.root, Widget), false);
|
|
282
|
+
assert.equal(Tree.key(widget), 0);
|
|
283
|
+
assert.equal(Tree.key(fidget), 1);
|
|
284
|
+
assert.equal(Tree.parent(widget), view.root);
|
|
285
|
+
assert.equal(Tree.schema(fidget), Widget);
|
|
286
|
+
assert.equal(typeof Tree.shortId(widget), "number");
|
|
287
|
+
assert.equal(Tree.shortId(fidget), "fidget");
|
|
288
|
+
assert.equal(Tree.status(widget), TreeStatus.InDocument);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("can listen to events on a recursive tree", async () => {
|
|
292
|
+
class Doll extends sf.objectRecursive("Matryoshka", {
|
|
293
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
294
|
+
nested: sf.optionalRecursive([() => Doll]),
|
|
295
|
+
}) {}
|
|
296
|
+
|
|
297
|
+
const { container } = await client.createContainer(schema, "2");
|
|
298
|
+
await container.attach();
|
|
299
|
+
const view = container.initialObjects.tree1.viewWith(
|
|
300
|
+
new TreeViewConfiguration({ schema: Doll, enableSchemaValidation: true }),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// These nodes in the initial tree are unhydrated...
|
|
304
|
+
const depth1 = new Doll({ nested: new Doll({}) });
|
|
305
|
+
const depth0 = new Doll({ nested: depth1 });
|
|
306
|
+
view.initialize(depth0);
|
|
307
|
+
// ...and confirmed to be the same nodes we get when we read the tree after initialization
|
|
308
|
+
assert.equal(view.root, depth0);
|
|
309
|
+
assert.equal(view.root.nested, depth1);
|
|
310
|
+
// Record a list of the node events fired
|
|
311
|
+
const eventLog: string[] = [];
|
|
312
|
+
Tree.on(depth0, "nodeChanged", () => {
|
|
313
|
+
eventLog.push("depth0.nested changed");
|
|
314
|
+
});
|
|
315
|
+
Tree.on(depth1, "nodeChanged", () => {
|
|
316
|
+
eventLog.push("depth1.nested changed");
|
|
317
|
+
});
|
|
318
|
+
// This event registration happens on the unhydrated (not yet inserted) node
|
|
319
|
+
const newDepth2 = new Doll({});
|
|
320
|
+
Tree.on(newDepth2, "nodeChanged", () => {
|
|
321
|
+
eventLog.push("depth2.nested changed");
|
|
322
|
+
});
|
|
323
|
+
// Fire the events by doing mutations
|
|
324
|
+
depth1.nested = newDepth2; // "depth1.nested changed"
|
|
325
|
+
assert.equal(depth1.nested, newDepth2);
|
|
326
|
+
depth1.nested.nested = new Doll({}); // "depth2.nested changed"
|
|
327
|
+
depth0.nested = new Doll({}); // "depth0.nested changed"
|
|
328
|
+
// Ensure the events fired in the expected order
|
|
329
|
+
assert.deepEqual(eventLog, [
|
|
330
|
+
"depth1.nested changed",
|
|
331
|
+
"depth2.nested changed",
|
|
332
|
+
"depth0.nested changed",
|
|
333
|
+
]);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
192
336
|
});
|
|
193
337
|
}
|