@automerge/automerge-repo 2.0.0-alpha.14 → 2.0.0-alpha.17
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/dist/AutomergeUrl.d.ts +19 -4
- package/dist/AutomergeUrl.d.ts.map +1 -1
- package/dist/AutomergeUrl.js +71 -24
- package/dist/DocHandle.d.ts +21 -17
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +83 -26
- package/dist/RemoteHeadsSubscriptions.d.ts +4 -4
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
- package/dist/RemoteHeadsSubscriptions.js +4 -1
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +17 -12
- package/dist/helpers/bufferFromHex.d.ts +3 -0
- package/dist/helpers/bufferFromHex.d.ts.map +1 -0
- package/dist/helpers/bufferFromHex.js +13 -0
- package/dist/helpers/headsAreSame.d.ts +2 -2
- package/dist/helpers/headsAreSame.d.ts.map +1 -1
- package/dist/helpers/mergeArrays.d.ts +1 -1
- package/dist/helpers/mergeArrays.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.js +6 -9
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +2 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/AutomergeUrl.ts +103 -26
- package/src/DocHandle.ts +130 -37
- package/src/RemoteHeadsSubscriptions.ts +11 -8
- package/src/Repo.ts +22 -11
- package/src/helpers/bufferFromHex.ts +14 -0
- package/src/helpers/headsAreSame.ts +2 -2
- package/src/helpers/tests/storage-adapter-tests.ts +13 -24
- package/src/storage/StorageSubsystem.ts +3 -1
- package/src/types.ts +4 -1
- package/test/AutomergeUrl.test.ts +130 -0
- package/test/DocHandle.test.ts +70 -4
- package/test/DocSynchronizer.test.ts +10 -3
- package/test/Repo.test.ts +117 -3
|
@@ -3,9 +3,11 @@ import bs58check from "bs58check"
|
|
|
3
3
|
import { describe, it } from "vitest"
|
|
4
4
|
import {
|
|
5
5
|
generateAutomergeUrl,
|
|
6
|
+
getHeadsFromUrl,
|
|
6
7
|
isValidAutomergeUrl,
|
|
7
8
|
parseAutomergeUrl,
|
|
8
9
|
stringifyAutomergeUrl,
|
|
10
|
+
UrlHeads,
|
|
9
11
|
} from "../src/AutomergeUrl.js"
|
|
10
12
|
import type {
|
|
11
13
|
AutomergeUrl,
|
|
@@ -102,3 +104,131 @@ describe("AutomergeUrl", () => {
|
|
|
102
104
|
})
|
|
103
105
|
})
|
|
104
106
|
})
|
|
107
|
+
|
|
108
|
+
describe("AutomergeUrl with heads", () => {
|
|
109
|
+
// Create some sample encoded heads for testing
|
|
110
|
+
const head1 = bs58check.encode(new Uint8Array([1, 2, 3, 4])) as string
|
|
111
|
+
const head2 = bs58check.encode(new Uint8Array([5, 6, 7, 8])) as string
|
|
112
|
+
const goodHeads = [head1, head2] as UrlHeads
|
|
113
|
+
const urlWithHeads = `${goodUrl}#${head1}|${head2}` as AutomergeUrl
|
|
114
|
+
const invalidHead = "not-base58-encoded"
|
|
115
|
+
const invalidHeads = [invalidHead] as UrlHeads
|
|
116
|
+
|
|
117
|
+
describe("stringifyAutomergeUrl", () => {
|
|
118
|
+
it("should stringify a url with heads", () => {
|
|
119
|
+
const url = stringifyAutomergeUrl({
|
|
120
|
+
documentId: goodDocumentId,
|
|
121
|
+
heads: goodHeads,
|
|
122
|
+
})
|
|
123
|
+
assert.strictEqual(url, urlWithHeads)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it("should throw if heads are not valid base58check", () => {
|
|
127
|
+
assert.throws(() =>
|
|
128
|
+
stringifyAutomergeUrl({
|
|
129
|
+
documentId: goodDocumentId,
|
|
130
|
+
heads: invalidHeads,
|
|
131
|
+
})
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe("parseAutomergeUrl", () => {
|
|
137
|
+
it("should parse a url with heads", () => {
|
|
138
|
+
const { documentId, heads } = parseAutomergeUrl(urlWithHeads)
|
|
139
|
+
assert.equal(documentId, goodDocumentId)
|
|
140
|
+
assert.deepEqual(heads, [head1, head2])
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it("should parse a url without heads", () => {
|
|
144
|
+
const { documentId, heads } = parseAutomergeUrl(goodUrl)
|
|
145
|
+
assert.equal(documentId, goodDocumentId)
|
|
146
|
+
assert.equal(heads, undefined)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("should throw on url with invalid heads encoding", () => {
|
|
150
|
+
const badUrl = `${goodUrl}#${invalidHead}` as AutomergeUrl
|
|
151
|
+
assert.throws(() => parseAutomergeUrl(badUrl))
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
describe("isValidAutomergeUrl", () => {
|
|
156
|
+
it("should return true for a valid url with heads", () => {
|
|
157
|
+
assert(isValidAutomergeUrl(urlWithHeads) === true)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it("should return false for a url with invalid heads", () => {
|
|
161
|
+
const badUrl = `${goodUrl}#${invalidHead}` as AutomergeUrl
|
|
162
|
+
assert(isValidAutomergeUrl(badUrl) === false)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe("getHeadsFromUrl", () => {
|
|
167
|
+
it("should return heads from a valid url", () => {
|
|
168
|
+
const heads = getHeadsFromUrl(urlWithHeads)
|
|
169
|
+
assert.deepEqual(heads, [head1, head2])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it("should return undefined for url without heads", () => {
|
|
173
|
+
const heads = getHeadsFromUrl(goodUrl)
|
|
174
|
+
assert.equal(heads, undefined)
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
it("should handle a single head correctly", () => {
|
|
178
|
+
const urlWithOneHead = `${goodUrl}#${head1}` as AutomergeUrl
|
|
179
|
+
const { heads } = parseAutomergeUrl(urlWithOneHead)
|
|
180
|
+
assert.deepEqual(heads, [head1])
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it("should round-trip urls with heads", () => {
|
|
184
|
+
const originalUrl = urlWithHeads
|
|
185
|
+
const parsed = parseAutomergeUrl(originalUrl)
|
|
186
|
+
const roundTripped = stringifyAutomergeUrl({
|
|
187
|
+
documentId: parsed.documentId,
|
|
188
|
+
heads: parsed.heads,
|
|
189
|
+
})
|
|
190
|
+
assert.equal(roundTripped, originalUrl)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe("should reject malformed urls", () => {
|
|
194
|
+
it("should reject urls with trailing delimiter", () => {
|
|
195
|
+
assert(!isValidAutomergeUrl(`${goodUrl}#${head1}:` as AutomergeUrl))
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it("should reject urls with empty head", () => {
|
|
199
|
+
assert(!isValidAutomergeUrl(`${goodUrl}#|${head1}` as AutomergeUrl))
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it("should reject urls with multiple hash characters", () => {
|
|
203
|
+
assert(
|
|
204
|
+
!isValidAutomergeUrl(`${goodUrl}#${head1}#${head2}` as AutomergeUrl)
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe("empty heads section", () => {
|
|
211
|
+
it("should treat bare # as empty heads array", () => {
|
|
212
|
+
const urlWithEmptyHeads = `${goodUrl}#` as AutomergeUrl
|
|
213
|
+
const { heads } = parseAutomergeUrl(urlWithEmptyHeads)
|
|
214
|
+
assert.deepEqual(heads, [])
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it("should round-trip empty heads array", () => {
|
|
218
|
+
const original = `${goodUrl}#` as AutomergeUrl
|
|
219
|
+
const parsed = parseAutomergeUrl(original)
|
|
220
|
+
const roundTripped = stringifyAutomergeUrl({
|
|
221
|
+
documentId: parsed.documentId,
|
|
222
|
+
heads: parsed.heads,
|
|
223
|
+
})
|
|
224
|
+
assert.equal(roundTripped, original)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it("should distinguish between no heads and empty heads", () => {
|
|
228
|
+
const noHeads = parseAutomergeUrl(goodUrl)
|
|
229
|
+
const emptyHeads = parseAutomergeUrl(`${goodUrl}#` as AutomergeUrl)
|
|
230
|
+
|
|
231
|
+
assert.equal(noHeads.heads, undefined)
|
|
232
|
+
assert.deepEqual(emptyHeads.heads, [])
|
|
233
|
+
})
|
|
234
|
+
})
|
package/test/DocHandle.test.ts
CHANGED
|
@@ -2,7 +2,11 @@ import * as A from "@automerge/automerge/next"
|
|
|
2
2
|
import assert from "assert"
|
|
3
3
|
import { decode } from "cbor-x"
|
|
4
4
|
import { describe, it, vi } from "vitest"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
encodeHeads,
|
|
7
|
+
generateAutomergeUrl,
|
|
8
|
+
parseAutomergeUrl,
|
|
9
|
+
} from "../src/AutomergeUrl.js"
|
|
6
10
|
import { eventPromise } from "../src/helpers/eventPromise.js"
|
|
7
11
|
import { pause } from "../src/helpers/pause.js"
|
|
8
12
|
import { DocHandle, DocHandleChangePayload } from "../src/index.js"
|
|
@@ -83,7 +87,7 @@ describe("DocHandle", () => {
|
|
|
83
87
|
handle.change(d => (d.foo = "bar"))
|
|
84
88
|
assert.equal(handle.isReady(), true)
|
|
85
89
|
|
|
86
|
-
const heads = A.getHeads(handle.docSync())
|
|
90
|
+
const heads = encodeHeads(A.getHeads(handle.docSync()))
|
|
87
91
|
assert.notDeepEqual(handle.heads(), [])
|
|
88
92
|
assert.deepEqual(heads, handle.heads())
|
|
89
93
|
})
|
|
@@ -113,8 +117,45 @@ describe("DocHandle", () => {
|
|
|
113
117
|
assert.equal(handle.isReady(), true)
|
|
114
118
|
|
|
115
119
|
const history = handle.history()
|
|
116
|
-
const
|
|
117
|
-
assert.deepEqual(
|
|
120
|
+
const viewHandle = handle.view(history[1])
|
|
121
|
+
assert.deepEqual(await viewHandle.doc(), { foo: "one" })
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it("should support fixed heads from construction", async () => {
|
|
125
|
+
const handle = setup()
|
|
126
|
+
handle.change(d => (d.foo = "zero"))
|
|
127
|
+
handle.change(d => (d.foo = "one"))
|
|
128
|
+
|
|
129
|
+
const history = handle.history()
|
|
130
|
+
const viewHandle = new DocHandle<TestDoc>(TEST_ID, { heads: history[0] })
|
|
131
|
+
viewHandle.update(() => A.clone(handle.docSync()!))
|
|
132
|
+
viewHandle.doneLoading()
|
|
133
|
+
|
|
134
|
+
assert.deepEqual(await viewHandle.doc(), { foo: "zero" })
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it("should prevent changes on fixed-heads handles", async () => {
|
|
138
|
+
const handle = setup()
|
|
139
|
+
handle.change(d => (d.foo = "zero"))
|
|
140
|
+
const viewHandle = handle.view(handle.heads()!)
|
|
141
|
+
|
|
142
|
+
assert.throws(() => viewHandle.change(d => (d.foo = "one")))
|
|
143
|
+
assert.throws(() =>
|
|
144
|
+
viewHandle.changeAt(handle.heads()!, d => (d.foo = "one"))
|
|
145
|
+
)
|
|
146
|
+
assert.throws(() => viewHandle.merge(handle))
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("should return fixed heads from heads()", async () => {
|
|
150
|
+
const handle = setup()
|
|
151
|
+
handle.change(d => (d.foo = "zero"))
|
|
152
|
+
const originalHeads = handle.heads()!
|
|
153
|
+
|
|
154
|
+
handle.change(d => (d.foo = "one"))
|
|
155
|
+
const viewHandle = handle.view(originalHeads)
|
|
156
|
+
|
|
157
|
+
assert.deepEqual(viewHandle.heads(), originalHeads)
|
|
158
|
+
assert.notDeepEqual(viewHandle.heads(), handle.heads())
|
|
118
159
|
})
|
|
119
160
|
|
|
120
161
|
it("should return diffs", async () => {
|
|
@@ -154,6 +195,31 @@ describe("DocHandle", () => {
|
|
|
154
195
|
])
|
|
155
196
|
})
|
|
156
197
|
|
|
198
|
+
it("should support diffing against another handle", async () => {
|
|
199
|
+
const handle = setup()
|
|
200
|
+
handle.change(d => (d.foo = "zero"))
|
|
201
|
+
const viewHandle = handle.view(handle.heads()!)
|
|
202
|
+
|
|
203
|
+
handle.change(d => (d.foo = "one"))
|
|
204
|
+
|
|
205
|
+
const patches = viewHandle.diff(handle)
|
|
206
|
+
assert.deepEqual(patches, [
|
|
207
|
+
{ action: "put", path: ["foo"], value: "" },
|
|
208
|
+
{ action: "splice", path: ["foo", 0], value: "one" },
|
|
209
|
+
])
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// TODO: alexg -- should i remove this test? should this fail or no?
|
|
213
|
+
it.skip("should fail diffing against unrelated handles", async () => {
|
|
214
|
+
const handle1 = setup()
|
|
215
|
+
const handle2 = setup()
|
|
216
|
+
|
|
217
|
+
handle1.change(d => (d.foo = "zero"))
|
|
218
|
+
handle2.change(d => (d.foo = "one"))
|
|
219
|
+
|
|
220
|
+
assert.throws(() => handle1.diff(handle2))
|
|
221
|
+
})
|
|
222
|
+
|
|
157
223
|
it("should allow direct access to decoded changes", async () => {
|
|
158
224
|
const handle = setup()
|
|
159
225
|
const time = Date.now()
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import assert from "assert"
|
|
2
2
|
import { describe, it } from "vitest"
|
|
3
3
|
import { next as Automerge } from "@automerge/automerge"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
encodeHeads,
|
|
6
|
+
generateAutomergeUrl,
|
|
7
|
+
parseAutomergeUrl,
|
|
8
|
+
} from "../src/AutomergeUrl.js"
|
|
5
9
|
import { DocHandle } from "../src/DocHandle.js"
|
|
6
10
|
import { eventPromise } from "../src/helpers/eventPromise.js"
|
|
7
11
|
import {
|
|
@@ -67,11 +71,14 @@ describe("DocSynchronizer", () => {
|
|
|
67
71
|
|
|
68
72
|
assert.equal(message1.peerId, "alice")
|
|
69
73
|
assert.equal(message1.documentId, handle.documentId)
|
|
70
|
-
assert.
|
|
74
|
+
assert.deepStrictEqual(message1.syncState.lastSentHeads, [])
|
|
71
75
|
|
|
72
76
|
assert.equal(message2.peerId, "alice")
|
|
73
77
|
assert.equal(message2.documentId, handle.documentId)
|
|
74
|
-
assert.
|
|
78
|
+
assert.deepStrictEqual(
|
|
79
|
+
encodeHeads(message2.syncState.lastSentHeads),
|
|
80
|
+
handle.heads()
|
|
81
|
+
)
|
|
75
82
|
})
|
|
76
83
|
|
|
77
84
|
it("still syncs with a peer after it disconnects and reconnects", async () => {
|
package/test/Repo.test.ts
CHANGED
|
@@ -3,7 +3,13 @@ import { MessageChannelNetworkAdapter } from "../../automerge-repo-network-messa
|
|
|
3
3
|
import assert from "assert"
|
|
4
4
|
import * as Uuid from "uuid"
|
|
5
5
|
import { describe, expect, it } from "vitest"
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
encodeHeads,
|
|
8
|
+
getHeadsFromUrl,
|
|
9
|
+
isValidAutomergeUrl,
|
|
10
|
+
parseAutomergeUrl,
|
|
11
|
+
UrlHeads,
|
|
12
|
+
} from "../src/AutomergeUrl.js"
|
|
7
13
|
import {
|
|
8
14
|
generateAutomergeUrl,
|
|
9
15
|
stringifyAutomergeUrl,
|
|
@@ -1175,7 +1181,10 @@ describe("Repo", () => {
|
|
|
1175
1181
|
bobHandle.documentId,
|
|
1176
1182
|
await charlieRepo!.storageSubsystem.id()
|
|
1177
1183
|
)
|
|
1178
|
-
assert.deepStrictEqual(
|
|
1184
|
+
assert.deepStrictEqual(
|
|
1185
|
+
encodeHeads(storedSyncState.sharedHeads),
|
|
1186
|
+
bobHandle.heads()
|
|
1187
|
+
)
|
|
1179
1188
|
|
|
1180
1189
|
teardown()
|
|
1181
1190
|
})
|
|
@@ -1275,7 +1284,7 @@ describe("Repo", () => {
|
|
|
1275
1284
|
|
|
1276
1285
|
const nextRemoteHeadsPromise = new Promise<{
|
|
1277
1286
|
storageId: StorageId
|
|
1278
|
-
heads:
|
|
1287
|
+
heads: UrlHeads
|
|
1279
1288
|
}>(resolve => {
|
|
1280
1289
|
handle.on("remote-heads", ({ storageId, heads }) => {
|
|
1281
1290
|
resolve({ storageId, heads })
|
|
@@ -1526,6 +1535,111 @@ describe("Repo", () => {
|
|
|
1526
1535
|
})
|
|
1527
1536
|
})
|
|
1528
1537
|
|
|
1538
|
+
describe("Repo heads-in-URLs functionality", () => {
|
|
1539
|
+
const setup = () => {
|
|
1540
|
+
const repo = new Repo({})
|
|
1541
|
+
const handle = repo.create()
|
|
1542
|
+
handle.change((doc: any) => (doc.title = "Hello World"))
|
|
1543
|
+
return { repo, handle }
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
it("finds a document view by URL with heads", async () => {
|
|
1547
|
+
const { repo, handle } = setup()
|
|
1548
|
+
const heads = handle.heads()!
|
|
1549
|
+
const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
|
|
1550
|
+
const view = repo.find(url)
|
|
1551
|
+
expect(view.docSync()).toEqual({ title: "Hello World" })
|
|
1552
|
+
})
|
|
1553
|
+
|
|
1554
|
+
it("returns a view, not the actual handle, when finding by URL with heads", async () => {
|
|
1555
|
+
const { repo, handle } = setup()
|
|
1556
|
+
const heads = handle.heads()!
|
|
1557
|
+
await handle.change((doc: any) => (doc.title = "Changed"))
|
|
1558
|
+
const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
|
|
1559
|
+
const view = repo.find(url)
|
|
1560
|
+
expect(view.docSync()).toEqual({ title: "Hello World" })
|
|
1561
|
+
expect(handle.docSync()).toEqual({ title: "Changed" })
|
|
1562
|
+
})
|
|
1563
|
+
|
|
1564
|
+
it("changes to a document view do not affect the original", async () => {
|
|
1565
|
+
const { repo, handle } = setup()
|
|
1566
|
+
const heads = handle.heads()!
|
|
1567
|
+
const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
|
|
1568
|
+
const view = repo.find(url)
|
|
1569
|
+
expect(() =>
|
|
1570
|
+
view.change((doc: any) => (doc.title = "Changed in View"))
|
|
1571
|
+
).toThrow()
|
|
1572
|
+
expect(handle.docSync()).toEqual({ title: "Hello World" })
|
|
1573
|
+
})
|
|
1574
|
+
|
|
1575
|
+
it("document views are read-only", async () => {
|
|
1576
|
+
const { repo, handle } = setup()
|
|
1577
|
+
const heads = handle.heads()!
|
|
1578
|
+
const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
|
|
1579
|
+
const view = repo.find(url)
|
|
1580
|
+
expect(() => view.change((doc: any) => (doc.title = "Changed"))).toThrow()
|
|
1581
|
+
})
|
|
1582
|
+
|
|
1583
|
+
it("finds the latest document when given a URL without heads", async () => {
|
|
1584
|
+
const { repo, handle } = setup()
|
|
1585
|
+
await handle.change((doc: any) => (doc.title = "Changed"))
|
|
1586
|
+
const found = repo.find(handle.url)
|
|
1587
|
+
expect(found.docSync()).toEqual({ title: "Changed" })
|
|
1588
|
+
})
|
|
1589
|
+
|
|
1590
|
+
it("getHeadsFromUrl returns heads array if present or undefined", () => {
|
|
1591
|
+
const { repo, handle } = setup()
|
|
1592
|
+
const heads = handle.heads()!
|
|
1593
|
+
const url = stringifyAutomergeUrl({ documentId: handle.documentId, heads })
|
|
1594
|
+
expect(getHeadsFromUrl(url)).toEqual(heads)
|
|
1595
|
+
|
|
1596
|
+
const urlWithoutHeads = generateAutomergeUrl()
|
|
1597
|
+
expect(getHeadsFromUrl(urlWithoutHeads)).toBeUndefined()
|
|
1598
|
+
})
|
|
1599
|
+
|
|
1600
|
+
it("isValidAutomergeUrl returns true for valid URLs", () => {
|
|
1601
|
+
const { repo, handle } = setup()
|
|
1602
|
+
const url = generateAutomergeUrl()
|
|
1603
|
+
expect(isValidAutomergeUrl(url)).toBe(true)
|
|
1604
|
+
|
|
1605
|
+
const urlWithHeads = stringifyAutomergeUrl({
|
|
1606
|
+
documentId: handle.documentId,
|
|
1607
|
+
heads: handle.heads()!,
|
|
1608
|
+
})
|
|
1609
|
+
expect(isValidAutomergeUrl(urlWithHeads)).toBe(true)
|
|
1610
|
+
})
|
|
1611
|
+
|
|
1612
|
+
it("isValidAutomergeUrl returns false for invalid URLs", () => {
|
|
1613
|
+
const { repo, handle } = setup()
|
|
1614
|
+
expect(isValidAutomergeUrl("not a url")).toBe(false)
|
|
1615
|
+
expect(isValidAutomergeUrl("automerge:invalidid")).toBe(false)
|
|
1616
|
+
expect(isValidAutomergeUrl("automerge:validid#invalidhead")).toBe(false)
|
|
1617
|
+
})
|
|
1618
|
+
|
|
1619
|
+
it("parseAutomergeUrl extracts documentId and heads", () => {
|
|
1620
|
+
const { repo, handle } = setup()
|
|
1621
|
+
const url = stringifyAutomergeUrl({
|
|
1622
|
+
documentId: handle.documentId,
|
|
1623
|
+
heads: handle.heads()!,
|
|
1624
|
+
})
|
|
1625
|
+
const parsed = parseAutomergeUrl(url)
|
|
1626
|
+
expect(parsed.documentId).toBe(handle.documentId)
|
|
1627
|
+
expect(parsed.heads).toEqual(handle.heads())
|
|
1628
|
+
})
|
|
1629
|
+
|
|
1630
|
+
it("stringifyAutomergeUrl creates valid URL", () => {
|
|
1631
|
+
const { repo, handle } = setup()
|
|
1632
|
+
const url = stringifyAutomergeUrl({
|
|
1633
|
+
documentId: handle.documentId,
|
|
1634
|
+
heads: handle.heads()!,
|
|
1635
|
+
})
|
|
1636
|
+
expect(isValidAutomergeUrl(url)).toBe(true)
|
|
1637
|
+
const parsed = parseAutomergeUrl(url)
|
|
1638
|
+
expect(parsed.documentId).toBe(handle.documentId)
|
|
1639
|
+
expect(parsed.heads).toEqual(handle.heads())
|
|
1640
|
+
})
|
|
1641
|
+
})
|
|
1642
|
+
|
|
1529
1643
|
const warn = console.warn
|
|
1530
1644
|
const NO_OP = () => {}
|
|
1531
1645
|
|