Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e90d9de5ed | ||
|
|
9a7250f192 | ||
|
|
4154b12f14 | ||
|
|
9df5016667 | ||
|
|
1becaccdd9 | ||
|
|
ea4e9a0007 | ||
|
|
a4e48d1ddf | ||
|
|
0a39a92b33 | ||
|
|
bd819243eb | ||
|
|
2ec19defcb | ||
|
|
336f7b1b1d | ||
|
|
8abf5b85ff | ||
|
|
320e8cbe18 | ||
|
|
49150f4adb |
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.17",
|
"version": "13.5.22",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1711,9 +1711,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib0": {
|
"lib0": {
|
||||||
"version": "0.2.42",
|
"version": "0.2.43",
|
||||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.42.tgz",
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.43.tgz",
|
||||||
"integrity": "sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q==",
|
"integrity": "sha512-MJ1KLoz5p3gljIUBfdjjNuL/wlWHHK6+DrcIRhzSRLvtAu1XNdRtRGATYM51KSTI0P2nxJZFQM8rwCH6ga9KUw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"isomorphic.js": "^0.2.4"
|
"isomorphic.js": "^0.2.4"
|
||||||
}
|
}
|
||||||
@@ -2494,9 +2494,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "2.58.0",
|
"version": "2.60.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.58.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz",
|
||||||
"integrity": "sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw==",
|
"integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.17",
|
"version": "13.5.22",
|
||||||
"description": "Shared Editing Library",
|
"description": "Shared Editing Library",
|
||||||
"main": "./dist/yjs.cjs",
|
"main": "./dist/yjs.cjs",
|
||||||
"module": "./dist/yjs.mjs",
|
"module": "./dist/yjs.mjs",
|
||||||
"types": "./dist/src/index.d.ts",
|
"types": "./dist/src/index.d.ts",
|
||||||
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "GitHub Sponsors ❤",
|
"type": "GitHub Sponsors ❤",
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"./src/index.js": "./src/index.js",
|
"./src/index.js": "./src/index.js",
|
||||||
"./tests/testHelper.js": "./tests/testHelper.js",
|
"./tests/testHelper.js": "./tests/testHelper.js",
|
||||||
|
"./testHelper": "./dist/testHelper.mjs",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
"dist/src",
|
"dist/src",
|
||||||
"src",
|
"src",
|
||||||
"tests/testHelper.js",
|
"tests/testHelper.js",
|
||||||
|
"dist/testHelper.mjs",
|
||||||
"sponsor-y.js"
|
"sponsor-y.js"
|
||||||
],
|
],
|
||||||
"dictionaries": {
|
"dictionaries": {
|
||||||
@@ -71,7 +74,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://docs.yjs.dev",
|
"homepage": "https://docs.yjs.dev",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.42"
|
"lib0": "^0.2.43"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
@@ -80,7 +83,7 @@
|
|||||||
"http-server": "^0.12.3",
|
"http-server": "^0.12.3",
|
||||||
"jsdoc": "^3.6.7",
|
"jsdoc": "^3.6.7",
|
||||||
"markdownlint-cli": "^0.23.2",
|
"markdownlint-cli": "^0.23.2",
|
||||||
"rollup": "^2.58.0",
|
"rollup": "^2.60.0",
|
||||||
"standard": "^16.0.4",
|
"standard": "^16.0.4",
|
||||||
"tui-jsdoc-template": "^1.2.2",
|
"tui-jsdoc-template": "^1.2.2",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
|
|||||||
@@ -60,6 +60,23 @@ export default [{
|
|||||||
sourcemap: true
|
sourcemap: true
|
||||||
},
|
},
|
||||||
external: id => /^lib0\//.test(id)
|
external: id => /^lib0\//.test(id)
|
||||||
|
}, {
|
||||||
|
input: './tests/testHelper.js',
|
||||||
|
output: {
|
||||||
|
name: 'Y',
|
||||||
|
file: 'dist/testHelper.mjs',
|
||||||
|
format: 'esm',
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
external: id => /^lib0\//.test(id) || id === 'yjs',
|
||||||
|
plugins: [{
|
||||||
|
resolveId (importee) {
|
||||||
|
if (importee === '../src/index.js') {
|
||||||
|
return 'yjs'
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}]
|
||||||
}, {
|
}, {
|
||||||
input: './tests/index.js',
|
input: './tests/index.js',
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ export {
|
|||||||
ContentString,
|
ContentString,
|
||||||
ContentType,
|
ContentType,
|
||||||
AbstractType,
|
AbstractType,
|
||||||
RelativePosition,
|
|
||||||
getTypeChildren,
|
getTypeChildren,
|
||||||
createRelativePositionFromTypeIndex,
|
createRelativePositionFromTypeIndex,
|
||||||
createRelativePositionFromJSON,
|
createRelativePositionFromJSON,
|
||||||
createAbsolutePositionFromRelativePosition,
|
createAbsolutePositionFromRelativePosition,
|
||||||
compareRelativePositions,
|
compareRelativePositions,
|
||||||
|
AbsolutePosition,
|
||||||
|
RelativePosition,
|
||||||
ID,
|
ID,
|
||||||
createID,
|
createID,
|
||||||
compareIDs,
|
compareIDs,
|
||||||
@@ -41,6 +42,7 @@ export {
|
|||||||
createSnapshot,
|
createSnapshot,
|
||||||
createDeleteSet,
|
createDeleteSet,
|
||||||
createDeleteSetFromStructStore,
|
createDeleteSetFromStructStore,
|
||||||
|
cleanupYTextFormatting,
|
||||||
snapshot,
|
snapshot,
|
||||||
emptySnapshot,
|
emptySnapshot,
|
||||||
findRootTypeKey,
|
findRootTypeKey,
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import {
|
|||||||
|
|
||||||
import * as error from 'lib0/error'
|
import * as error from 'lib0/error'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} guid
|
||||||
|
* @param {Object<string, any>} opts
|
||||||
|
*/
|
||||||
|
const createDocFromOpts = (guid, opts) => new Doc({ guid, ...opts, shouldLoad: opts.shouldLoad || opts.autoLoad || false })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@@ -61,7 +67,7 @@ export class ContentDoc {
|
|||||||
* @return {ContentDoc}
|
* @return {ContentDoc}
|
||||||
*/
|
*/
|
||||||
copy () {
|
copy () {
|
||||||
return new ContentDoc(this.doc)
|
return new ContentDoc(createDocFromOpts(this.doc.guid, this.opts))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,4 +138,4 @@ export class ContentDoc {
|
|||||||
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
||||||
* @return {ContentDoc}
|
* @return {ContentDoc}
|
||||||
*/
|
*/
|
||||||
export const readContentDoc = decoder => new ContentDoc(new Doc({ guid: decoder.readString(), ...decoder.readAny() }))
|
export const readContentDoc = decoder => new ContentDoc(createDocFromOpts(decoder.readString(), decoder.readAny()))
|
||||||
|
|||||||
@@ -706,9 +706,9 @@ export class YTextEvent extends YEvent {
|
|||||||
addOp()
|
addOp()
|
||||||
}
|
}
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
attributes[key] = value
|
|
||||||
} else {
|
|
||||||
delete attributes[key]
|
delete attributes[key]
|
||||||
|
} else {
|
||||||
|
attributes[key] = value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.delete(transaction)
|
item.delete(transaction)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const generateNewClientId = random.uint32
|
|||||||
* @property {string | null} [DocOpts.collectionid] Associate this document with a collection. This only plays a role if your provider has a concept of collection.
|
* @property {string | null} [DocOpts.collectionid] Associate this document with a collection. This only plays a role if your provider has a concept of collection.
|
||||||
* @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well.
|
* @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well.
|
||||||
* @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically.
|
* @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically.
|
||||||
|
* @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +39,7 @@ export class Doc extends Observable {
|
|||||||
/**
|
/**
|
||||||
* @param {DocOpts} [opts] configuration
|
* @param {DocOpts} [opts] configuration
|
||||||
*/
|
*/
|
||||||
constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false } = {}) {
|
constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) {
|
||||||
super()
|
super()
|
||||||
this.gc = gc
|
this.gc = gc
|
||||||
this.gcFilter = gcFilter
|
this.gcFilter = gcFilter
|
||||||
@@ -67,7 +68,7 @@ export class Doc extends Observable {
|
|||||||
* @type {Item?}
|
* @type {Item?}
|
||||||
*/
|
*/
|
||||||
this._item = null
|
this._item = null
|
||||||
this.shouldLoad = autoLoad
|
this.shouldLoad = shouldLoad
|
||||||
this.autoLoad = autoLoad
|
this.autoLoad = autoLoad
|
||||||
this.meta = meta
|
this.meta = meta
|
||||||
}
|
}
|
||||||
@@ -248,16 +249,12 @@ export class Doc extends Observable {
|
|||||||
if (item !== null) {
|
if (item !== null) {
|
||||||
this._item = null
|
this._item = null
|
||||||
const content = /** @type {ContentDoc} */ (item.content)
|
const content = /** @type {ContentDoc} */ (item.content)
|
||||||
if (item.deleted) {
|
content.doc = new Doc({ guid: this.guid, ...content.opts, shouldLoad: false })
|
||||||
// @ts-ignore
|
content.doc._item = item
|
||||||
content.doc = null
|
|
||||||
} else {
|
|
||||||
content.doc = new Doc({ guid: this.guid, ...content.opts })
|
|
||||||
content.doc._item = item
|
|
||||||
}
|
|
||||||
transact(/** @type {any} */ (item).parent.doc, transaction => {
|
transact(/** @type {any} */ (item).parent.doc, transaction => {
|
||||||
|
const doc = content.doc
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
transaction.subdocsAdded.add(content.doc)
|
transaction.subdocsAdded.add(doc)
|
||||||
}
|
}
|
||||||
transaction.subdocsRemoved.add(this)
|
transaction.subdocsRemoved.add(this)
|
||||||
}, null, true)
|
}, null, true)
|
||||||
|
|||||||
@@ -331,8 +331,8 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) {
|
if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) {
|
||||||
doc.clientID = generateNewClientId()
|
|
||||||
logging.print(logging.ORANGE, logging.BOLD, '[yjs] ', logging.UNBOLD, logging.RED, 'Changed the client-id because another client seems to be using it.')
|
logging.print(logging.ORANGE, logging.BOLD, '[yjs] ', logging.UNBOLD, logging.RED, 'Changed the client-id because another client seems to be using it.')
|
||||||
|
doc.clientID = generateNewClientId()
|
||||||
}
|
}
|
||||||
// @todo Merge all the transactions into one and provide send the data as a single update message
|
// @todo Merge all the transactions into one and provide send the data as a single update message
|
||||||
doc.emit('afterTransactionCleanup', [transaction, doc])
|
doc.emit('afterTransactionCleanup', [transaction, doc])
|
||||||
@@ -360,7 +360,7 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
doc.subdocs.add(subdoc)
|
doc.subdocs.add(subdoc)
|
||||||
})
|
})
|
||||||
subdocsRemoved.forEach(subdoc => doc.subdocs.delete(subdoc))
|
subdocsRemoved.forEach(subdoc => doc.subdocs.delete(subdoc))
|
||||||
doc.emit('subdocs', [{ loaded: subdocsLoaded, added: subdocsAdded, removed: subdocsRemoved }])
|
doc.emit('subdocs', [{ loaded: subdocsLoaded, added: subdocsAdded, removed: subdocsRemoved }, doc, transaction])
|
||||||
subdocsRemoved.forEach(subdoc => subdoc.destroy())
|
subdocsRemoved.forEach(subdoc => subdoc.destroy())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ export const decodeStateVector = decodedState => readStateVector(new DSDecoderV1
|
|||||||
*/
|
*/
|
||||||
export const writeStateVector = (encoder, sv) => {
|
export const writeStateVector = (encoder, sv) => {
|
||||||
encoding.writeVarUint(encoder.restEncoder, sv.size)
|
encoding.writeVarUint(encoder.restEncoder, sv.size)
|
||||||
sv.forEach((clock, client) => {
|
Array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||||
encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping
|
encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping
|
||||||
encoding.writeVarUint(encoder.restEncoder, clock)
|
encoding.writeVarUint(encoder.restEncoder, clock)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export const testSubdoc = tc => {
|
|||||||
subdocs.get('a').load()
|
subdocs.get('a').load()
|
||||||
t.compare(event, [[], [], ['a']])
|
t.compare(event, [[], [], ['a']])
|
||||||
|
|
||||||
subdocs.set('b', new Y.Doc({ guid: 'a' }))
|
subdocs.set('b', new Y.Doc({ guid: 'a', shouldLoad: false }))
|
||||||
t.compare(event, [['a'], [], []])
|
t.compare(event, [['a'], [], []])
|
||||||
subdocs.get('b').load()
|
subdocs.get('b').load()
|
||||||
t.compare(event, [[], [], ['a']])
|
t.compare(event, [[], [], ['a']])
|
||||||
@@ -124,3 +124,107 @@ export const testSubdoc = tc => {
|
|||||||
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
|
t.compare(Array.from(doc2.getSubdocGuids()), ['a', 'c'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testSubdocLoadEdgeCases = tc => {
|
||||||
|
const ydoc = new Y.Doc()
|
||||||
|
const yarray = ydoc.getArray()
|
||||||
|
const subdoc1 = new Y.Doc()
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
let lastEvent = null
|
||||||
|
ydoc.on('subdocs', event => {
|
||||||
|
lastEvent = event
|
||||||
|
})
|
||||||
|
yarray.insert(0, [subdoc1])
|
||||||
|
t.assert(subdoc1.shouldLoad)
|
||||||
|
t.assert(subdoc1.autoLoad === false)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc1))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc1))
|
||||||
|
// destroy and check whether lastEvent adds it again to added (it shouldn't)
|
||||||
|
subdoc1.destroy()
|
||||||
|
const subdoc2 = yarray.get(0)
|
||||||
|
t.assert(subdoc1 !== subdoc2)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc2))
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.loaded.has(subdoc2))
|
||||||
|
// load
|
||||||
|
subdoc2.load()
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.added.has(subdoc2))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc2))
|
||||||
|
// apply from remote
|
||||||
|
const ydoc2 = new Y.Doc()
|
||||||
|
ydoc2.on('subdocs', event => {
|
||||||
|
lastEvent = event
|
||||||
|
})
|
||||||
|
Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc))
|
||||||
|
const subdoc3 = ydoc2.getArray().get(0)
|
||||||
|
t.assert(subdoc3.shouldLoad === false)
|
||||||
|
t.assert(subdoc3.autoLoad === false)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc3))
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.loaded.has(subdoc3))
|
||||||
|
// load
|
||||||
|
subdoc3.load()
|
||||||
|
t.assert(subdoc3.shouldLoad)
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.added.has(subdoc3))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc3))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testSubdocLoadEdgeCasesAutoload = tc => {
|
||||||
|
const ydoc = new Y.Doc()
|
||||||
|
const yarray = ydoc.getArray()
|
||||||
|
const subdoc1 = new Y.Doc({ autoLoad: true })
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
let lastEvent = null
|
||||||
|
ydoc.on('subdocs', event => {
|
||||||
|
lastEvent = event
|
||||||
|
})
|
||||||
|
yarray.insert(0, [subdoc1])
|
||||||
|
t.assert(subdoc1.shouldLoad)
|
||||||
|
t.assert(subdoc1.autoLoad)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc1))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc1))
|
||||||
|
// destroy and check whether lastEvent adds it again to added (it shouldn't)
|
||||||
|
subdoc1.destroy()
|
||||||
|
const subdoc2 = yarray.get(0)
|
||||||
|
t.assert(subdoc1 !== subdoc2)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc2))
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.loaded.has(subdoc2))
|
||||||
|
// load
|
||||||
|
subdoc2.load()
|
||||||
|
t.assert(lastEvent !== null && !lastEvent.added.has(subdoc2))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc2))
|
||||||
|
// apply from remote
|
||||||
|
const ydoc2 = new Y.Doc()
|
||||||
|
ydoc2.on('subdocs', event => {
|
||||||
|
lastEvent = event
|
||||||
|
})
|
||||||
|
Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc))
|
||||||
|
const subdoc3 = ydoc2.getArray().get(0)
|
||||||
|
t.assert(subdoc1.shouldLoad)
|
||||||
|
t.assert(subdoc1.autoLoad)
|
||||||
|
t.assert(lastEvent !== null && lastEvent.added.has(subdoc3))
|
||||||
|
t.assert(lastEvent !== null && lastEvent.loaded.has(subdoc3))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testSubdocsUndo = tc => {
|
||||||
|
const ydoc = new Y.Doc()
|
||||||
|
const elems = ydoc.getXmlFragment()
|
||||||
|
const undoManager = new Y.UndoManager(elems)
|
||||||
|
const subdoc = new Y.Doc()
|
||||||
|
// @ts-ignore
|
||||||
|
elems.insert(0, [subdoc])
|
||||||
|
undoManager.undo()
|
||||||
|
undoManager.redo()
|
||||||
|
t.assert(elems.length === 1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import * as Y from '../src/internals'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Y.YText} ytext
|
* @param {Y.Text} ytext
|
||||||
*/
|
*/
|
||||||
const checkRelativePositions = ytext => {
|
const checkRelativePositions = ytext => {
|
||||||
// test if all positions are encoded and restored correctly
|
// test if all positions are encoded and restored correctly
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { createDocFromSnapshot, Doc, snapshot, YMap } from '../src/internals'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
import { init } from './testHelper'
|
import { init } from './testHelper.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testBasicRestoreSnapshot = tc => {
|
export const testBasicRestoreSnapshot = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, ['hello'])
|
doc.getArray('array').insert(0, ['hello'])
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
doc.getArray('array').insert(1, ['world'])
|
doc.getArray('array').insert(1, ['world'])
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toArray(), ['hello'])
|
t.compare(docRestored.getArray('array').toArray(), ['hello'])
|
||||||
t.compare(doc.getArray('array').toArray(), ['hello', 'world'])
|
t.compare(doc.getArray('array').toArray(), ['hello', 'world'])
|
||||||
@@ -21,19 +21,19 @@ export const testBasicRestoreSnapshot = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testEmptyRestoreSnapshot = tc => {
|
export const testEmptyRestoreSnapshot = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
snap.sv.set(9999, 0)
|
snap.sv.set(9999, 0)
|
||||||
doc.getArray().insert(0, ['world'])
|
doc.getArray().insert(0, ['world'])
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray().toArray(), [])
|
t.compare(docRestored.getArray().toArray(), [])
|
||||||
t.compare(doc.getArray().toArray(), ['world'])
|
t.compare(doc.getArray().toArray(), ['world'])
|
||||||
|
|
||||||
// now this snapshot reflects the latest state. It shoult still work.
|
// now this snapshot reflects the latest state. It shoult still work.
|
||||||
const snap2 = snapshot(doc)
|
const snap2 = Y.snapshot(doc)
|
||||||
const docRestored2 = createDocFromSnapshot(doc, snap2)
|
const docRestored2 = Y.createDocFromSnapshot(doc, snap2)
|
||||||
t.compare(docRestored2.getArray().toArray(), ['world'])
|
t.compare(docRestored2.getArray().toArray(), ['world'])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,15 +41,15 @@ export const testEmptyRestoreSnapshot = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testRestoreSnapshotWithSubType = tc => {
|
export const testRestoreSnapshotWithSubType = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, [new YMap()])
|
doc.getArray('array').insert(0, [new Y.Map()])
|
||||||
const subMap = doc.getArray('array').get(0)
|
const subMap = doc.getArray('array').get(0)
|
||||||
subMap.set('key1', 'value1')
|
subMap.set('key1', 'value1')
|
||||||
|
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
subMap.set('key2', 'value2')
|
subMap.set('key2', 'value2')
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toJSON(), [{
|
t.compare(docRestored.getArray('array').toJSON(), [{
|
||||||
key1: 'value1'
|
key1: 'value1'
|
||||||
@@ -64,13 +64,13 @@ export const testRestoreSnapshotWithSubType = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testRestoreDeletedItem1 = tc => {
|
export const testRestoreDeletedItem1 = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, ['item1', 'item2'])
|
doc.getArray('array').insert(0, ['item1', 'item2'])
|
||||||
|
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
doc.getArray('array').delete(0)
|
doc.getArray('array').delete(0)
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item2'])
|
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item2'])
|
||||||
t.compare(doc.getArray('array').toArray(), ['item2'])
|
t.compare(doc.getArray('array').toArray(), ['item2'])
|
||||||
@@ -80,15 +80,15 @@ export const testRestoreDeletedItem1 = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testRestoreLeftItem = tc => {
|
export const testRestoreLeftItem = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, ['item1'])
|
doc.getArray('array').insert(0, ['item1'])
|
||||||
doc.getMap('map').set('test', 1)
|
doc.getMap('map').set('test', 1)
|
||||||
doc.getArray('array').insert(0, ['item0'])
|
doc.getArray('array').insert(0, ['item0'])
|
||||||
|
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
doc.getArray('array').delete(1)
|
doc.getArray('array').delete(1)
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toArray(), ['item0', 'item1'])
|
t.compare(docRestored.getArray('array').toArray(), ['item0', 'item1'])
|
||||||
t.compare(doc.getArray('array').toArray(), ['item0'])
|
t.compare(doc.getArray('array').toArray(), ['item0'])
|
||||||
@@ -98,13 +98,13 @@ export const testRestoreLeftItem = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testDeletedItemsBase = tc => {
|
export const testDeletedItemsBase = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, ['item1'])
|
doc.getArray('array').insert(0, ['item1'])
|
||||||
doc.getArray('array').delete(0)
|
doc.getArray('array').delete(0)
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
doc.getArray('array').insert(0, ['item0'])
|
doc.getArray('array').insert(0, ['item0'])
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toArray(), [])
|
t.compare(docRestored.getArray('array').toArray(), [])
|
||||||
t.compare(doc.getArray('array').toArray(), ['item0'])
|
t.compare(doc.getArray('array').toArray(), ['item0'])
|
||||||
@@ -114,13 +114,13 @@ export const testDeletedItemsBase = tc => {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testDeletedItems2 = tc => {
|
export const testDeletedItems2 = tc => {
|
||||||
const doc = new Doc({ gc: false })
|
const doc = new Y.Doc({ gc: false })
|
||||||
doc.getArray('array').insert(0, ['item1', 'item2', 'item3'])
|
doc.getArray('array').insert(0, ['item1', 'item2', 'item3'])
|
||||||
doc.getArray('array').delete(1)
|
doc.getArray('array').delete(1)
|
||||||
const snap = snapshot(doc)
|
const snap = Y.snapshot(doc)
|
||||||
doc.getArray('array').insert(0, ['item0'])
|
doc.getArray('array').insert(0, ['item0'])
|
||||||
|
|
||||||
const docRestored = createDocFromSnapshot(doc, snap)
|
const docRestored = Y.createDocFromSnapshot(doc, snap)
|
||||||
|
|
||||||
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item3'])
|
t.compare(docRestored.getArray('array').toArray(), ['item1', 'item3'])
|
||||||
t.compare(doc.getArray('array').toArray(), ['item0', 'item1', 'item3'])
|
t.compare(doc.getArray('array').toArray(), ['item0', 'item1', 'item3'])
|
||||||
@@ -140,11 +140,11 @@ export const testDependentChanges = tc => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type Doc
|
* @type {Y.Doc}
|
||||||
*/
|
*/
|
||||||
const doc0 = array0.doc
|
const doc0 = array0.doc
|
||||||
/**
|
/**
|
||||||
* @type Doc
|
* @type {Y.Doc}
|
||||||
*/
|
*/
|
||||||
const doc1 = array1.doc
|
const doc1 = array1.doc
|
||||||
|
|
||||||
@@ -156,16 +156,16 @@ export const testDependentChanges = tc => {
|
|||||||
array1.insert(1, ['user2item1'])
|
array1.insert(1, ['user2item1'])
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
|
|
||||||
const snap = snapshot(array0.doc)
|
const snap = Y.snapshot(array0.doc)
|
||||||
|
|
||||||
array0.insert(2, ['user1item2'])
|
array0.insert(2, ['user1item2'])
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
array1.insert(3, ['user2item2'])
|
array1.insert(3, ['user2item2'])
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
|
|
||||||
const docRestored0 = createDocFromSnapshot(array0.doc, snap)
|
const docRestored0 = Y.createDocFromSnapshot(array0.doc, snap)
|
||||||
t.compare(docRestored0.getArray('array').toArray(), ['user1item1', 'user2item1'])
|
t.compare(docRestored0.getArray('array').toArray(), ['user1item1', 'user2item1'])
|
||||||
|
|
||||||
const docRestored1 = createDocFromSnapshot(array1.doc, snap)
|
const docRestored1 = Y.createDocFromSnapshot(array1.doc, snap)
|
||||||
t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1'])
|
t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1'])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import * as t from 'lib0/testing'
|
|||||||
import * as prng from 'lib0/prng'
|
import * as prng from 'lib0/prng'
|
||||||
import * as encoding from 'lib0/encoding'
|
import * as encoding from 'lib0/encoding'
|
||||||
import * as decoding from 'lib0/decoding'
|
import * as decoding from 'lib0/decoding'
|
||||||
import * as syncProtocol from 'y-protocols/sync.js'
|
import * as syncProtocol from 'y-protocols/sync'
|
||||||
import * as object from 'lib0/object'
|
import * as object from 'lib0/object'
|
||||||
import * as Y from '../src/internals.js'
|
import * as Y from '../src/index.js'
|
||||||
export * from '../src/internals.js'
|
export * from '../src/index.js'
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -279,7 +279,7 @@ export class TestConnector {
|
|||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
* @param {{users?:number}} conf
|
* @param {{users?:number}} conf
|
||||||
* @param {InitTestObjectCallback<T>} [initTestObject]
|
* @param {InitTestObjectCallback<T>} [initTestObject]
|
||||||
* @return {{testObjects:Array<any>,testConnector:TestConnector,users:Array<TestYInstance>,array0:Y.YArray<any>,array1:Y.YArray<any>,array2:Y.YArray<any>,map0:Y.YMap<any>,map1:Y.YMap<any>,map2:Y.YMap<any>,map3:Y.YMap<any>,text0:Y.YText,text1:Y.YText,text2:Y.YText,xml0:Y.YXmlElement,xml1:Y.YXmlElement,xml2:Y.YXmlElement}}
|
* @return {{testObjects:Array<any>,testConnector:TestConnector,users:Array<TestYInstance>,array0:Y.Array<any>,array1:Y.Array<any>,array2:Y.Array<any>,map0:Y.Map<any>,map1:Y.Map<any>,map2:Y.Map<any>,map3:Y.Map<any>,text0:Y.Text,text1:Y.Text,text2:Y.Text,xml0:Y.XmlElement,xml1:Y.XmlElement,xml2:Y.XmlElement}}
|
||||||
*/
|
*/
|
||||||
export const init = (tc, { users = 5 } = {}, initTestObject) => {
|
export const init = (tc, { users = 5 } = {}, initTestObject) => {
|
||||||
/**
|
/**
|
||||||
@@ -304,7 +304,7 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => {
|
|||||||
result.users.push(y)
|
result.users.push(y)
|
||||||
result['array' + i] = y.getArray('array')
|
result['array' + i] = y.getArray('array')
|
||||||
result['map' + i] = y.getMap('map')
|
result['map' + i] = y.getMap('map')
|
||||||
result['xml' + i] = y.get('xml', Y.YXmlElement)
|
result['xml' + i] = y.get('xml', Y.XmlElement)
|
||||||
result['text' + i] = y.getText('text')
|
result['text' + i] = y.getText('text')
|
||||||
}
|
}
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
@@ -335,7 +335,7 @@ export const compare = users => {
|
|||||||
users.push(.../** @type {any} */(mergedDocs))
|
users.push(.../** @type {any} */(mergedDocs))
|
||||||
const userArrayValues = users.map(u => u.getArray('array').toJSON())
|
const userArrayValues = users.map(u => u.getArray('array').toJSON())
|
||||||
const userMapValues = users.map(u => u.getMap('map').toJSON())
|
const userMapValues = users.map(u => u.getMap('map').toJSON())
|
||||||
const userXmlValues = users.map(u => u.get('xml', Y.YXmlElement).toString())
|
const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString())
|
||||||
const userTextValues = users.map(u => u.getText('text').toDelta())
|
const userTextValues = users.map(u => u.getText('text').toDelta())
|
||||||
for (const u of users) {
|
for (const u of users) {
|
||||||
t.assert(u.store.pendingDs === null)
|
t.assert(u.store.pendingDs === null)
|
||||||
@@ -370,7 +370,7 @@ export const compare = users => {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
t.compare(Y.getStateVector(users[i].store), Y.getStateVector(users[i + 1].store))
|
t.compare(Y.encodeStateVector(users[i]), Y.encodeStateVector(users[i + 1]))
|
||||||
compareDS(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store))
|
compareDS(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store))
|
||||||
compareStructStores(users[i].store, users[i + 1].store)
|
compareStructStores(users[i].store, users[i + 1].store)
|
||||||
}
|
}
|
||||||
@@ -385,8 +385,8 @@ export const compare = users => {
|
|||||||
export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y.compareIDs(a.id, b.id))
|
export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y.compareIDs(a.id, b.id))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Y.StructStore} ss1
|
* @param {import('../src/internals').StructStore} ss1
|
||||||
* @param {Y.StructStore} ss2
|
* @param {import('../src/internals').StructStore} ss2
|
||||||
*/
|
*/
|
||||||
export const compareStructStores = (ss1, ss2) => {
|
export const compareStructStores = (ss1, ss2) => {
|
||||||
t.assert(ss1.clients.size === ss2.clients.size)
|
t.assert(ss1.clients.size === ss2.clients.size)
|
||||||
@@ -428,13 +428,13 @@ export const compareStructStores = (ss1, ss2) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Y.DeleteSet} ds1
|
* @param {import('../src/internals').DeleteSet} ds1
|
||||||
* @param {Y.DeleteSet} ds2
|
* @param {import('../src/internals').DeleteSet} ds2
|
||||||
*/
|
*/
|
||||||
export const compareDS = (ds1, ds2) => {
|
export const compareDS = (ds1, ds2) => {
|
||||||
t.assert(ds1.clients.size === ds2.clients.size)
|
t.assert(ds1.clients.size === ds2.clients.size)
|
||||||
ds1.clients.forEach((deleteItems1, client) => {
|
ds1.clients.forEach((deleteItems1, client) => {
|
||||||
const deleteItems2 = /** @type {Array<Y.DeleteItem>} */ (ds2.clients.get(client))
|
const deleteItems2 = /** @type {Array<import('../src/internals').DeleteItem>} */ (ds2.clients.get(client))
|
||||||
t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length)
|
t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length)
|
||||||
for (let i = 0; i < deleteItems1.length; i++) {
|
for (let i = 0; i < deleteItems1.length; i++) {
|
||||||
const di1 = deleteItems1[i]
|
const di1 = deleteItems1[i]
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
||||||
|
|
||||||
import {
|
|
||||||
UndoManager
|
|
||||||
} from '../src/internals.js'
|
|
||||||
|
|
||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
@@ -12,7 +8,7 @@ import * as t from 'lib0/testing'
|
|||||||
*/
|
*/
|
||||||
export const testUndoText = tc => {
|
export const testUndoText = tc => {
|
||||||
const { testConnector, text0, text1 } = init(tc, { users: 3 })
|
const { testConnector, text0, text1 } = init(tc, { users: 3 })
|
||||||
const undoManager = new UndoManager(text0)
|
const undoManager = new Y.UndoManager(text0)
|
||||||
|
|
||||||
// items that are added & deleted in the same transaction won't be undo
|
// items that are added & deleted in the same transaction won't be undo
|
||||||
text0.insert(0, 'test')
|
text0.insert(0, 'test')
|
||||||
@@ -81,7 +77,7 @@ export const testDoubleUndo = tc => {
|
|||||||
export const testUndoMap = tc => {
|
export const testUndoMap = tc => {
|
||||||
const { testConnector, map0, map1 } = init(tc, { users: 2 })
|
const { testConnector, map0, map1 } = init(tc, { users: 2 })
|
||||||
map0.set('a', 0)
|
map0.set('a', 0)
|
||||||
const undoManager = new UndoManager(map0)
|
const undoManager = new Y.UndoManager(map0)
|
||||||
map0.set('a', 1)
|
map0.set('a', 1)
|
||||||
undoManager.undo()
|
undoManager.undo()
|
||||||
t.assert(map0.get('a') === 0)
|
t.assert(map0.get('a') === 0)
|
||||||
@@ -120,7 +116,7 @@ export const testUndoMap = tc => {
|
|||||||
*/
|
*/
|
||||||
export const testUndoArray = tc => {
|
export const testUndoArray = tc => {
|
||||||
const { testConnector, array0, array1 } = init(tc, { users: 3 })
|
const { testConnector, array0, array1 } = init(tc, { users: 3 })
|
||||||
const undoManager = new UndoManager(array0)
|
const undoManager = new Y.UndoManager(array0)
|
||||||
array0.insert(0, [1, 2, 3])
|
array0.insert(0, [1, 2, 3])
|
||||||
array1.insert(0, [4, 5, 6])
|
array1.insert(0, [4, 5, 6])
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
@@ -171,7 +167,7 @@ export const testUndoArray = tc => {
|
|||||||
*/
|
*/
|
||||||
export const testUndoXml = tc => {
|
export const testUndoXml = tc => {
|
||||||
const { xml0 } = init(tc, { users: 3 })
|
const { xml0 } = init(tc, { users: 3 })
|
||||||
const undoManager = new UndoManager(xml0)
|
const undoManager = new Y.UndoManager(xml0)
|
||||||
const child = new Y.XmlElement('p')
|
const child = new Y.XmlElement('p')
|
||||||
xml0.insert(0, [child])
|
xml0.insert(0, [child])
|
||||||
const textchild = new Y.XmlText('content')
|
const textchild = new Y.XmlText('content')
|
||||||
@@ -196,7 +192,7 @@ export const testUndoXml = tc => {
|
|||||||
*/
|
*/
|
||||||
export const testUndoEvents = tc => {
|
export const testUndoEvents = tc => {
|
||||||
const { text0 } = init(tc, { users: 3 })
|
const { text0 } = init(tc, { users: 3 })
|
||||||
const undoManager = new UndoManager(text0)
|
const undoManager = new Y.UndoManager(text0)
|
||||||
let counter = 0
|
let counter = 0
|
||||||
let receivedMetadata = -1
|
let receivedMetadata = -1
|
||||||
undoManager.on('stack-item-added', /** @param {any} event */ event => {
|
undoManager.on('stack-item-added', /** @param {any} event */ event => {
|
||||||
@@ -222,7 +218,7 @@ export const testUndoEvents = tc => {
|
|||||||
export const testTrackClass = tc => {
|
export const testTrackClass = tc => {
|
||||||
const { users, text0 } = init(tc, { users: 3 })
|
const { users, text0 } = init(tc, { users: 3 })
|
||||||
// only track origins that are numbers
|
// only track origins that are numbers
|
||||||
const undoManager = new UndoManager(text0, { trackedOrigins: new Set([Number]) })
|
const undoManager = new Y.UndoManager(text0, { trackedOrigins: new Set([Number]) })
|
||||||
users[0].transact(() => {
|
users[0].transact(() => {
|
||||||
text0.insert(0, 'abc')
|
text0.insert(0, 'abc')
|
||||||
}, 42)
|
}, 42)
|
||||||
@@ -240,8 +236,8 @@ export const testTypeScope = tc => {
|
|||||||
const text0 = new Y.Text()
|
const text0 = new Y.Text()
|
||||||
const text1 = new Y.Text()
|
const text1 = new Y.Text()
|
||||||
array0.insert(0, [text0, text1])
|
array0.insert(0, [text0, text1])
|
||||||
const undoManager = new UndoManager(text0)
|
const undoManager = new Y.UndoManager(text0)
|
||||||
const undoManagerBoth = new UndoManager([text0, text1])
|
const undoManagerBoth = new Y.UndoManager([text0, text1])
|
||||||
text1.insert(0, 'abc')
|
text1.insert(0, 'abc')
|
||||||
t.assert(undoManager.undoStack.length === 0)
|
t.assert(undoManager.undoStack.length === 0)
|
||||||
t.assert(undoManagerBoth.undoStack.length === 1)
|
t.assert(undoManagerBoth.undoStack.length === 1)
|
||||||
@@ -260,7 +256,7 @@ export const testUndoDeleteFilter = tc => {
|
|||||||
* @type {Array<Y.Map<any>>}
|
* @type {Array<Y.Map<any>>}
|
||||||
*/
|
*/
|
||||||
const array0 = /** @type {any} */ (init(tc, { users: 3 }).array0)
|
const array0 = /** @type {any} */ (init(tc, { users: 3 }).array0)
|
||||||
const undoManager = new UndoManager(array0, { deleteFilter: item => !(item instanceof Y.Item) || (item.content instanceof Y.ContentType && item.content.type._map.size === 0) })
|
const undoManager = new Y.UndoManager(array0, { deleteFilter: item => !(item instanceof Y.Item) || (item.content instanceof Y.ContentType && item.content.type._map.size === 0) })
|
||||||
const map0 = new Y.Map()
|
const map0 = new Y.Map()
|
||||||
map0.set('hi', 1)
|
map0.set('hi', 1)
|
||||||
const map1 = new Y.Map()
|
const map1 = new Y.Map()
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export const testGetDeltaWithEmbeds = tc => {
|
|||||||
export const testTypesAsEmbed = tc => {
|
export const testTypesAsEmbed = tc => {
|
||||||
const { text0, text1, testConnector } = init(tc, { users: 2 })
|
const { text0, text1, testConnector } = init(tc, { users: 2 })
|
||||||
text0.applyDelta([{
|
text0.applyDelta([{
|
||||||
insert: new Y.YMap([['key', 'val']])
|
insert: new Y.Map([['key', 'val']])
|
||||||
}])
|
}])
|
||||||
t.compare(text0.toDelta()[0].insert.toJSON(), { key: 'val' })
|
t.compare(text0.toDelta()[0].insert.toJSON(), { key: 'val' })
|
||||||
let firedEvent = false
|
let firedEvent = false
|
||||||
@@ -288,6 +288,41 @@ export const testFormattingRemovedInMidText = tc => {
|
|||||||
t.assert(Y.getTypeChildren(text0).length === 3)
|
t.assert(Y.getTypeChildren(text0).length === 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reported in https://github.com/yjs/yjs/issues/344
|
||||||
|
*
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testFormattingDeltaUnnecessaryAttributeChange = tc => {
|
||||||
|
const { text0, text1, testConnector } = init(tc, { users: 2 })
|
||||||
|
text0.insert(0, '\n', {
|
||||||
|
PARAGRAPH_STYLES: 'normal',
|
||||||
|
LIST_STYLES: 'bullet'
|
||||||
|
})
|
||||||
|
text0.insert(1, 'abc', {
|
||||||
|
PARAGRAPH_STYLES: 'normal'
|
||||||
|
})
|
||||||
|
testConnector.flushAllMessages()
|
||||||
|
/**
|
||||||
|
* @type {Array<any>}
|
||||||
|
*/
|
||||||
|
const deltas = []
|
||||||
|
text0.observe(event => {
|
||||||
|
deltas.push(event.delta)
|
||||||
|
})
|
||||||
|
text1.observe(event => {
|
||||||
|
deltas.push(event.delta)
|
||||||
|
})
|
||||||
|
text1.format(0, 1, { LIST_STYLES: 'number' })
|
||||||
|
testConnector.flushAllMessages()
|
||||||
|
const filteredDeltas = deltas.filter(d => d.length > 0)
|
||||||
|
t.assert(filteredDeltas.length === 2)
|
||||||
|
t.compare(filteredDeltas[0], [
|
||||||
|
{ retain: 1, attributes: { LIST_STYLES: 'number' } }
|
||||||
|
])
|
||||||
|
t.compare(filteredDeltas[0], filteredDeltas[1])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
@@ -679,7 +714,7 @@ const qChanges = [
|
|||||||
if (prng.bool(gen)) {
|
if (prng.bool(gen)) {
|
||||||
ytext.insertEmbed(insertPos, { image: 'https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png' })
|
ytext.insertEmbed(insertPos, { image: 'https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png' })
|
||||||
} else {
|
} else {
|
||||||
ytext.insertEmbed(insertPos, new Y.YMap([[prng.word(gen), prng.word(gen)]]))
|
ytext.insertEmbed(insertPos, new Y.Map([[prng.word(gen), prng.word(gen)]]))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user