cojson 0.13.29 → 0.13.31

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 (47) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/coValues/coPlainText.d.ts +0 -1
  4. package/dist/coValues/coPlainText.d.ts.map +1 -1
  5. package/dist/coValues/coPlainText.js +10 -10
  6. package/dist/coValues/coPlainText.js.map +1 -1
  7. package/dist/coValues/group.d.ts.map +1 -1
  8. package/dist/coValues/group.js +4 -1
  9. package/dist/coValues/group.js.map +1 -1
  10. package/dist/localNode.d.ts.map +1 -1
  11. package/dist/localNode.js +1 -1
  12. package/dist/localNode.js.map +1 -1
  13. package/dist/permissions.d.ts.map +1 -1
  14. package/dist/permissions.js +0 -12
  15. package/dist/permissions.js.map +1 -1
  16. package/dist/tests/coPlainText.test.js +68 -10
  17. package/dist/tests/coPlainText.test.js.map +1 -1
  18. package/dist/tests/group.inheritance.test.d.ts +2 -0
  19. package/dist/tests/group.inheritance.test.d.ts.map +1 -0
  20. package/dist/tests/group.inheritance.test.js +235 -0
  21. package/dist/tests/group.inheritance.test.js.map +1 -0
  22. package/dist/tests/group.invite.test.d.ts +2 -0
  23. package/dist/tests/group.invite.test.d.ts.map +1 -0
  24. package/dist/tests/group.invite.test.js +214 -0
  25. package/dist/tests/group.invite.test.js.map +1 -0
  26. package/dist/tests/group.removeMember.test.js +22 -0
  27. package/dist/tests/group.removeMember.test.js.map +1 -1
  28. package/dist/tests/group.roleOf.test.d.ts +2 -0
  29. package/dist/tests/group.roleOf.test.d.ts.map +1 -0
  30. package/dist/tests/group.roleOf.test.js +275 -0
  31. package/dist/tests/group.roleOf.test.js.map +1 -0
  32. package/dist/tests/group.test.js +1 -505
  33. package/dist/tests/group.test.js.map +1 -1
  34. package/dist/tests/permissions.test.js +0 -33
  35. package/dist/tests/permissions.test.js.map +1 -1
  36. package/package.json +3 -2
  37. package/src/coValues/coPlainText.ts +10 -19
  38. package/src/coValues/group.ts +4 -1
  39. package/src/localNode.ts +2 -1
  40. package/src/permissions.ts +0 -18
  41. package/src/tests/coPlainText.test.ts +79 -18
  42. package/src/tests/group.inheritance.test.ts +418 -0
  43. package/src/tests/group.invite.test.ts +350 -0
  44. package/src/tests/group.removeMember.test.ts +48 -0
  45. package/src/tests/group.roleOf.test.ts +450 -0
  46. package/src/tests/group.test.ts +0 -852
  47. package/src/tests/permissions.test.ts +0 -52
@@ -1,28 +1,12 @@
1
1
  import { afterEach, expect, test, vi } from "vitest";
2
2
  import { expectPlainText } from "../coValue.js";
3
3
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
4
- import { LocalNode } from "../localNode.js";
5
- import {
6
- nodeWithRandomAgentAndSessionID,
7
- randomAgentAndSessionID,
8
- } from "./testUtils.js";
4
+ import { nodeWithRandomAgentAndSessionID } from "./testUtils.js";
9
5
 
10
6
  const Crypto = await WasmCrypto.create();
11
7
 
12
8
  afterEach(() => void vi.unstubAllGlobals());
13
9
 
14
- test("should throw on creation if Intl.Segmenter is not available", () => {
15
- vi.stubGlobal("Intl", {
16
- Segmenter: undefined,
17
- });
18
-
19
- const node = nodeWithRandomAgentAndSessionID();
20
- const group = node.createGroup();
21
- expect(() => group.createPlainText()).toThrow(
22
- "Intl.Segmenter is not supported. Use a polyfill to get coPlainText support in Jazz. (eg. https://formatjs.github.io/docs/polyfills/intl-segmenter/)",
23
- );
24
- });
25
-
26
10
  test("Empty CoPlainText works", () => {
27
11
  const node = nodeWithRandomAgentAndSessionID();
28
12
 
@@ -86,7 +70,7 @@ test("Can insert and delete in CoPlainText", () => {
86
70
  content.insertBefore(2, "😍", "trusting");
87
71
  expect(content.toString()).toEqual("He😍llo, world");
88
72
 
89
- content.deleteRange({ from: 2, to: 4 }, "trusting");
73
+ content.deleteRange({ from: 2, to: 3 }, "trusting");
90
74
  expect(content.toString()).toEqual("Hello, world");
91
75
  });
92
76
 
@@ -204,3 +188,80 @@ test("insertBefore and insertAfter work as expected", () => {
204
188
  content.insertBefore(0, "!", "trusting"); // "!hey"
205
189
  expect(content.toString()).toEqual("!hey");
206
190
  });
191
+
192
+ test("Can delete a single grapheme", () => {
193
+ const node = nodeWithRandomAgentAndSessionID();
194
+ const coValue = node.createCoValue({
195
+ type: "coplaintext",
196
+ ruleset: { type: "unsafeAllowAll" },
197
+ meta: null,
198
+ ...Crypto.createdNowUnique(),
199
+ });
200
+ const content = expectPlainText(coValue.getCurrentContent());
201
+
202
+ content.insertAfter(0, "a̐éö̲", "trusting"); // 3 graphemes
203
+ content.deleteRange({ from: 1, to: 2 }, "trusting"); // delete the second grapheme
204
+ expect(content.toString()).toEqual("a̐ö̲");
205
+ });
206
+
207
+ test("Handles complex grapheme clusters correctly", () => {
208
+ const node = nodeWithRandomAgentAndSessionID();
209
+ const coValue = node.createCoValue({
210
+ type: "coplaintext",
211
+ ruleset: { type: "unsafeAllowAll" },
212
+ meta: null,
213
+ ...Crypto.createdNowUnique(),
214
+ });
215
+ const content = expectPlainText(coValue.getCurrentContent());
216
+
217
+ // Combining marks (should be treated as one grapheme each)
218
+ const combining = "a̐éö̲"; // 3 graphemes: [a̐][é][ö̲]
219
+ content.insertAfter(0, combining, "trusting");
220
+ expect(content.toString()).toEqual(combining);
221
+ content.deleteRange({ from: 1, to: 2 }, "trusting");
222
+ expect(content.toString()).toEqual("a̐ö̲");
223
+
224
+ // ZWJ emoji (family)
225
+ const family = "👨‍👩‍👧‍👦"; // 1 grapheme
226
+ content.insertAfter(2, family, "trusting");
227
+ expect(content.toString()).toEqual("a̐ö̲👨‍👩‍👧‍👦");
228
+ content.deleteRange({ from: 2, to: 3 }, "trusting");
229
+ expect(content.toString()).toEqual("a̐ö̲");
230
+
231
+ // Flag emoji (regional indicators)
232
+ const flag = "🇺🇸"; // 1 grapheme
233
+ content.insertAfter(2, flag, "trusting");
234
+ expect(content.toString()).toEqual("a̐ö̲🇺🇸");
235
+ content.deleteRange({ from: 2, to: 3 }, "trusting");
236
+ expect(content.toString()).toEqual("a̐ö̲");
237
+
238
+ // Emoji with skin tone modifier
239
+ const thumbsUp = "👍🏽"; // 1 grapheme
240
+ content.insertAfter(2, thumbsUp, "trusting");
241
+ expect(content.toString()).toEqual("a̐ö̲👍🏽");
242
+ content.deleteRange({ from: 2, to: 3 }, "trusting");
243
+ expect(content.toString()).toEqual("a̐ö̲");
244
+ });
245
+
246
+ test("Handle deletion of complex grapheme clusters correctly", () => {
247
+ const node = nodeWithRandomAgentAndSessionID();
248
+ const coValue = node.createCoValue({
249
+ type: "coplaintext",
250
+ ruleset: { type: "unsafeAllowAll" },
251
+ meta: null,
252
+ ...Crypto.createdNowUnique(),
253
+ });
254
+ const content = expectPlainText(coValue.getCurrentContent());
255
+
256
+ // Combining marks (should be treated as one grapheme each)
257
+ content.insertAfter(0, "👋 안녕!", "trusting");
258
+ expect(content.toString()).toEqual("👋 안녕!");
259
+
260
+ // Delete the first grapheme
261
+ content.deleteRange({ from: 0, to: 1 }, "trusting");
262
+ expect(content.toString()).toEqual(" 안녕!");
263
+
264
+ // Delete the second grapheme
265
+ content.deleteRange({ from: 1, to: 2 }, "trusting");
266
+ expect(content.toString()).toEqual(" 녕!");
267
+ });
@@ -0,0 +1,418 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import {
3
+ createThreeConnectedNodes,
4
+ createTwoConnectedNodes,
5
+ loadCoValueOrFail,
6
+ } from "./testUtils";
7
+
8
+ describe("extend", () => {
9
+ test("inherited writer roles should work correctly", async () => {
10
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
11
+
12
+ const group = node1.node.createGroup();
13
+ group.addMember(
14
+ await loadCoValueOrFail(node1.node, node2.accountID),
15
+ "writer",
16
+ );
17
+
18
+ const childGroup = node1.node.createGroup();
19
+ childGroup.extend(group);
20
+ childGroup.addMember(
21
+ await loadCoValueOrFail(node1.node, node2.accountID),
22
+ "writeOnly",
23
+ );
24
+
25
+ const map = childGroup.createMap();
26
+ map.set("test", "Written from the admin");
27
+
28
+ await map.core.waitForSync();
29
+
30
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
31
+
32
+ // The writer role should be able to see the edits from the admin
33
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
34
+ });
35
+
36
+ test("a user should be able to extend a group when his role on the parent group is writer", async () => {
37
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
38
+
39
+ const group = node1.node.createGroup();
40
+ group.addMember(
41
+ await loadCoValueOrFail(node1.node, node2.accountID),
42
+ "writer",
43
+ );
44
+
45
+ await group.core.waitForSync();
46
+
47
+ const groupOnNode2 = await loadCoValueOrFail(node2.node, group.id);
48
+
49
+ const childGroup = node2.node.createGroup();
50
+ childGroup.extend(groupOnNode2);
51
+
52
+ const map = childGroup.createMap();
53
+ map.set("test", "Written from node2");
54
+
55
+ await map.core.waitForSync();
56
+ await childGroup.core.waitForSync();
57
+
58
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
59
+
60
+ expect(mapOnNode2.get("test")).toEqual("Written from node2");
61
+ });
62
+
63
+ test("a user should be able to extend a group when his role on the parent group is reader", async () => {
64
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
65
+
66
+ const group = node1.node.createGroup();
67
+ group.addMember(
68
+ await loadCoValueOrFail(node1.node, node2.accountID),
69
+ "reader",
70
+ );
71
+
72
+ await group.core.waitForSync();
73
+
74
+ const groupOnNode2 = await loadCoValueOrFail(node2.node, group.id);
75
+
76
+ const childGroup = node2.node.createGroup();
77
+ childGroup.extend(groupOnNode2);
78
+
79
+ const map = childGroup.createMap();
80
+ map.set("test", "Written from node2");
81
+
82
+ await map.core.waitForSync();
83
+ await childGroup.core.waitForSync();
84
+
85
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
86
+
87
+ expect(mapOnNode2.get("test")).toEqual("Written from node2");
88
+ });
89
+
90
+ test("a user should be able to extend a group when his role on the parent group is writeOnly", async () => {
91
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
92
+
93
+ const group = node1.node.createGroup();
94
+ group.addMember(
95
+ await loadCoValueOrFail(node1.node, node2.accountID),
96
+ "writeOnly",
97
+ );
98
+
99
+ await group.core.waitForSync();
100
+
101
+ const groupOnNode2 = await loadCoValueOrFail(node2.node, group.id);
102
+
103
+ const childGroup = node2.node.createGroup();
104
+ childGroup.extend(groupOnNode2);
105
+
106
+ const map = childGroup.createMap();
107
+ map.set("test", "Written from node2");
108
+
109
+ await map.core.waitForSync();
110
+ await childGroup.core.waitForSync();
111
+
112
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
113
+
114
+ expect(mapOnNode2.get("test")).toEqual("Written from node2");
115
+ });
116
+
117
+ test("self-extend a group should not break anything", async () => {
118
+ const { node1 } = await createTwoConnectedNodes("server", "server");
119
+
120
+ const group = node1.node.createGroup();
121
+ group.extend(group);
122
+
123
+ const map = group.createMap();
124
+ map.set("test", "Hello!");
125
+
126
+ expect(map.get("test")).toEqual("Hello!");
127
+ });
128
+
129
+ test("should not break when introducing extend cycles", async () => {
130
+ const { node1 } = await createTwoConnectedNodes("server", "server");
131
+
132
+ const group = node1.node.createGroup();
133
+ const group2 = node1.node.createGroup();
134
+ const group3 = node1.node.createGroup();
135
+
136
+ group.extend(group2);
137
+ group2.extend(group3);
138
+ group3.extend(group);
139
+
140
+ const map = group.createMap();
141
+ map.set("test", "Hello!");
142
+
143
+ expect(map.get("test")).toEqual("Hello!");
144
+ });
145
+
146
+ test("a writerInvite role should not be inherited", async () => {
147
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
148
+
149
+ const group = node1.node.createGroup();
150
+ group.addMember(
151
+ await loadCoValueOrFail(node1.node, node2.accountID),
152
+ "writerInvite",
153
+ );
154
+
155
+ const childGroup = node1.node.createGroup();
156
+ childGroup.extend(group);
157
+
158
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
159
+ });
160
+ });
161
+
162
+ describe("unextend", () => {
163
+ test("should revoke roles", async () => {
164
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
165
+ "server",
166
+ "server",
167
+ "server",
168
+ );
169
+
170
+ // `parentGroup` has `alice` as a writer
171
+ const parentGroup = node1.node.createGroup();
172
+ const alice = await loadCoValueOrFail(node1.node, node2.accountID);
173
+ parentGroup.addMember(alice, "writer");
174
+ // `alice`'s role in `parentGroup` is `"writer"`
175
+ expect(parentGroup.roleOf(alice.id)).toBe("writer");
176
+
177
+ // `childGroup` has `bob` as a reader
178
+ const childGroup = node1.node.createGroup();
179
+ const bob = await loadCoValueOrFail(node1.node, node3.accountID);
180
+ childGroup.addMember(bob, "reader");
181
+ // `bob`'s role in `childGroup` is `"reader"`
182
+ expect(childGroup.roleOf(bob.id)).toBe("reader");
183
+
184
+ // `childGroup` has `parentGroup`'s members (in this case, `alice` as a writer)
185
+ childGroup.extend(parentGroup);
186
+ expect(childGroup.roleOf(alice.id)).toBe("writer");
187
+
188
+ // `childGroup` no longer has `parentGroup`'s members
189
+ await childGroup.revokeExtend(parentGroup);
190
+ expect(childGroup.roleOf(bob.id)).toBe("reader");
191
+ expect(childGroup.roleOf(alice.id)).toBe(undefined);
192
+ });
193
+
194
+ test("should do nothing if applied to a group that is not extended", async () => {
195
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
196
+ "server",
197
+ "server",
198
+ "server",
199
+ );
200
+
201
+ const parentGroup = node1.node.createGroup();
202
+ const alice = await loadCoValueOrFail(node1.node, node2.accountID);
203
+ parentGroup.addMember(alice, "writer");
204
+ const childGroup = node1.node.createGroup();
205
+ const bob = await loadCoValueOrFail(node1.node, node3.accountID);
206
+ childGroup.addMember(bob, "reader");
207
+ await childGroup.revokeExtend(parentGroup);
208
+ expect(childGroup.roleOf(bob.id)).toBe("reader");
209
+ expect(childGroup.roleOf(alice.id)).toBe(undefined);
210
+ });
211
+
212
+ test("should not throw if the revokeExtend is called twice", async () => {
213
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
214
+ "server",
215
+ "server",
216
+ "server",
217
+ );
218
+
219
+ const parentGroup = node1.node.createGroup();
220
+ const alice = await loadCoValueOrFail(node1.node, node2.accountID);
221
+ parentGroup.addMember(alice, "writer");
222
+ const childGroup = node1.node.createGroup();
223
+ const bob = await loadCoValueOrFail(node1.node, node3.accountID);
224
+ childGroup.addMember(bob, "reader");
225
+
226
+ childGroup.extend(parentGroup);
227
+
228
+ await childGroup.revokeExtend(parentGroup);
229
+ await childGroup.revokeExtend(parentGroup);
230
+ expect(childGroup.roleOf(bob.id)).toBe("reader");
231
+ expect(childGroup.roleOf(alice.id)).toBe(undefined);
232
+ });
233
+ });
234
+
235
+ describe("extend with role mapping", () => {
236
+ test("mapping to writer should add the ability to write", async () => {
237
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
238
+
239
+ const group = node1.node.createGroup();
240
+ group.addMember(
241
+ await loadCoValueOrFail(node1.node, node2.accountID),
242
+ "reader",
243
+ );
244
+
245
+ const childGroup = node1.node.createGroup();
246
+ childGroup.extend(group, "writer");
247
+
248
+ expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
249
+
250
+ const map = childGroup.createMap();
251
+ map.set("test", "Written from the admin");
252
+
253
+ await map.core.waitForSync();
254
+
255
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
256
+
257
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
258
+ mapOnNode2.set("test", "Written from the inherited role");
259
+ expect(mapOnNode2.get("test")).toEqual("Written from the inherited role");
260
+
261
+ await mapOnNode2.core.waitForSync();
262
+
263
+ expect(map.get("test")).toEqual("Written from the inherited role");
264
+ });
265
+
266
+ test("mapping to reader should remove the ability to write", async () => {
267
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
268
+
269
+ const group = node1.node.createGroup();
270
+ group.addMember(
271
+ await loadCoValueOrFail(node1.node, node2.accountID),
272
+ "writer",
273
+ );
274
+
275
+ const childGroup = node1.node.createGroup();
276
+ childGroup.extend(group, "reader");
277
+
278
+ expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
279
+
280
+ const map = childGroup.createMap();
281
+ map.set("test", "Written from the admin");
282
+
283
+ await map.core.waitForSync();
284
+
285
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
286
+
287
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
288
+
289
+ mapOnNode2.set("test", "Should not be visible");
290
+
291
+ await mapOnNode2.core.waitForSync();
292
+
293
+ expect(map.get("test")).toEqual("Written from the admin");
294
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
295
+ });
296
+
297
+ test("mapping to admin should add the ability to add members", async () => {
298
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
299
+ "server",
300
+ "server",
301
+ "server",
302
+ );
303
+
304
+ const group = node1.node.createGroup();
305
+ group.addMember(
306
+ await loadCoValueOrFail(node1.node, node2.accountID),
307
+ "reader",
308
+ );
309
+
310
+ const childGroup = node1.node.createGroup();
311
+ childGroup.extend(group, "admin");
312
+
313
+ expect(childGroup.roleOf(node2.accountID)).toEqual("admin");
314
+
315
+ await childGroup.core.waitForSync();
316
+
317
+ const childGroupOnNode2 = await loadCoValueOrFail(
318
+ node2.node,
319
+ childGroup.id,
320
+ );
321
+
322
+ childGroupOnNode2.addMember(
323
+ await loadCoValueOrFail(node2.node, node3.accountID),
324
+ "reader",
325
+ );
326
+
327
+ expect(childGroupOnNode2.roleOf(node3.accountID)).toEqual("reader");
328
+ });
329
+
330
+ test("mapping to reader should remove the ability to add members", async () => {
331
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
332
+ "server",
333
+ "server",
334
+ "server",
335
+ );
336
+
337
+ const group = node1.node.createGroup();
338
+ group.addMember(
339
+ await loadCoValueOrFail(node1.node, node2.accountID),
340
+ "admin",
341
+ );
342
+
343
+ const childGroup = node1.node.createGroup();
344
+ childGroup.extend(group, "reader");
345
+
346
+ expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
347
+
348
+ await childGroup.core.waitForSync();
349
+
350
+ const childGroupOnNode2 = await loadCoValueOrFail(
351
+ node2.node,
352
+ childGroup.id,
353
+ );
354
+
355
+ const accountToAdd = await loadCoValueOrFail(node2.node, node3.accountID);
356
+
357
+ expect(() => {
358
+ childGroupOnNode2.addMember(accountToAdd, "reader");
359
+ }).toThrow();
360
+
361
+ expect(childGroupOnNode2.roleOf(node3.accountID)).toEqual(undefined);
362
+ });
363
+
364
+ test("non-inheritable roles should not give access to the child group when role mapping is used", async () => {
365
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
366
+
367
+ const group = node1.node.createGroup();
368
+ group.addMember(
369
+ await loadCoValueOrFail(node1.node, node2.accountID),
370
+ "writeOnly",
371
+ );
372
+
373
+ const childGroup = node1.node.createGroup();
374
+ childGroup.extend(group, "reader");
375
+
376
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
377
+
378
+ const map = childGroup.createMap();
379
+ map.set("test", "Written from the admin");
380
+
381
+ await map.core.waitForSync();
382
+
383
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
384
+
385
+ expect(mapOnNode2.get("test")).toEqual(undefined);
386
+ });
387
+
388
+ test("invite roles should not give write access to the child group when role mapping is used", async () => {
389
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
390
+
391
+ const group = node1.node.createGroup();
392
+ group.addMember(
393
+ await loadCoValueOrFail(node1.node, node2.accountID),
394
+ "writerInvite",
395
+ );
396
+
397
+ const childGroup = node1.node.createGroup();
398
+ childGroup.extend(group, "writer");
399
+
400
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
401
+
402
+ const map = childGroup.createMap();
403
+ map.set("test", "Written from the admin");
404
+
405
+ await map.core.waitForSync();
406
+
407
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
408
+
409
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin"); // The invite roles have access to the readKey hence can read the values on inherited groups
410
+
411
+ mapOnNode2.set("test", "Should not be visible");
412
+
413
+ await mapOnNode2.core.waitForSync();
414
+
415
+ expect(map.get("test")).toEqual("Written from the admin");
416
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
417
+ });
418
+ });