mirror of
https://github.com/yjs/yjs.git
synced 2025-12-14 18:57:45 +01:00
fixed most tests for delta v2 migration
This commit is contained in:
@@ -233,7 +233,7 @@ export const updateMarkerChanges = (searchMarker, index, len) => {
|
||||
/**
|
||||
* Accumulate all (list) children of a type and return them as an Array.
|
||||
*
|
||||
* @param {AbstractType} t
|
||||
* @param {import('../utils/types.js').YType} t
|
||||
* @return {Array<Item>}
|
||||
*/
|
||||
export const getTypeChildren = t => {
|
||||
@@ -272,7 +272,7 @@ export const callTypeObservers = (type, transaction, event) => {
|
||||
/**
|
||||
* Abstract Yjs Type class
|
||||
* @template {delta.Delta<any,any,any,any,any>} [EventDelta=delta.Delta<any,any,any,any,any>]
|
||||
* @template {YType_} [Self=any]
|
||||
* @template {AbstractType<any,any>} [Self=any]
|
||||
*/
|
||||
export class AbstractType {
|
||||
constructor () {
|
||||
@@ -442,18 +442,19 @@ export class AbstractType {
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender]
|
||||
* @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions
|
||||
* @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only
|
||||
* @param {Set<string>?} [opts.renderAttrs] - if true, retain rendered+attributed deletes only
|
||||
* @param {Set<string>?} [opts.renderAttrs] - set of attrs to render. if null, render all attributes
|
||||
* @param {boolean} [opts.renderChildren] - if true, retain rendered+attributed deletes only
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [opts.deletedItems] - used for computing prevItem in attributes
|
||||
* @return {EventDelta} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true } = {}) {
|
||||
getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null } = {}) {
|
||||
/**
|
||||
* @type {EventDelta}
|
||||
*/
|
||||
const d = /** @type {any} */ (delta.create())
|
||||
typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am)
|
||||
typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deletedItems, itemsToRender)
|
||||
if (renderChildren) {
|
||||
/**
|
||||
* @type {delta.FormattingAttributes}
|
||||
@@ -625,7 +626,7 @@ export class AbstractType {
|
||||
* @type {import('../utils/AttributionManager.js').Attribution}
|
||||
*/
|
||||
const formattingAttribution = object.assign({}, d.usedAttribution)
|
||||
const changedAttributedAttributes = /** @type {{ [key: string]: Array<any> }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {}))
|
||||
const changedAttributedAttributes = /** @type {{ [key: string]: Array<any> }} */ (formattingAttribution.format = object.assign({}, formattingAttribution.format ?? {}))
|
||||
if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) {
|
||||
// an unattributed formatting attribute was found or an attributed formatting
|
||||
// attribute was found that resets to the previous status
|
||||
@@ -635,13 +636,13 @@ export class AbstractType {
|
||||
const by = changedAttributedAttributes[key] = (changedAttributedAttributes[key]?.slice() ?? [])
|
||||
by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? []))
|
||||
const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt)
|
||||
if (attributedAt) formattingAttribution.attributedAt = attributedAt
|
||||
if (attributedAt) formattingAttribution.formatAt = attributedAt
|
||||
}
|
||||
if (object.isEmpty(changedAttributedAttributes)) {
|
||||
d.useAttribution(null)
|
||||
} else if (attribution != null) {
|
||||
const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt)
|
||||
if (attributedAt != null) formattingAttribution.attributedAt = attributedAt
|
||||
if (attributedAt != null) formattingAttribution.formatAt = attributedAt
|
||||
d.useAttribution(formattingAttribution)
|
||||
}
|
||||
}
|
||||
@@ -651,7 +652,7 @@ export class AbstractType {
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
return /** @type {any} */ (d.done())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -678,7 +679,7 @@ export class AbstractType {
|
||||
op.value = op.value.getContentDeep(am)
|
||||
}
|
||||
})
|
||||
return /** @type {any} */ (d)
|
||||
return /** @type {any} */ (d.done())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,47 +769,6 @@ export const typeListToArray = type => {
|
||||
return cs
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo this can be removed as this can be replaced by a generic function
|
||||
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||
* attributions.
|
||||
*
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @template {delta.Delta<any,any,any,any>} TypeDelta
|
||||
* @param {TypeDelta} d
|
||||
* @param {import('../utils/types.js').YType} type
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeListGetContent = (d, type, am) => {
|
||||
type.doc ?? warnPrematureAccess()
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const cs = []
|
||||
for (let item = type._start; item !== null; cs.length = 0) {
|
||||
// populate cs
|
||||
for (; item !== null && cs.length < 50; item = item.right) {
|
||||
am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1)
|
||||
}
|
||||
for (let i = 0; i < cs.length; i++) {
|
||||
const c = cs[i]
|
||||
const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted)
|
||||
if (c.content.isCountable()) {
|
||||
if (c.render || attribution != null) {
|
||||
d.insert(c.content.getContent(), null, attribution)
|
||||
} else if (!c.deleted) {
|
||||
d.retain(c.content.getLength())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {Snapshot} snapshot
|
||||
@@ -1270,13 +1230,13 @@ export const typeMapGetAll = (parent) => {
|
||||
* @param {YType_} parent
|
||||
* @param {Set<string>?} attrsToRender
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [deletedItems]
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [itemsToRender]
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeMapGetDelta = (d, parent, attrsToRender, am) => {
|
||||
parent.doc ?? warnPrematureAccess()
|
||||
|
||||
export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, itemsToRender) => {
|
||||
/**
|
||||
* @param {Item} item
|
||||
* @param {string} key
|
||||
@@ -1291,28 +1251,17 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am) => {
|
||||
const c = array.last(content.getContent())
|
||||
const attribution = createAttributionFromAttributionItems(attrs, deleted)
|
||||
if (deleted) {
|
||||
d.unset(key, attribution, c)
|
||||
} else {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
let cs = []
|
||||
for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const tmpcs = []
|
||||
am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, 1)
|
||||
cs = tmpcs.concat(cs)
|
||||
if (cs.length === 0 || cs[0].attrs == null) {
|
||||
cs.splice(0, cs.findIndex(c => c.attrs != null))
|
||||
break
|
||||
}
|
||||
if (cs.length > 0) {
|
||||
cs.length = 1
|
||||
}
|
||||
if (itemsToRender == null || itemsToRender.hasId(item.lastId)) {
|
||||
d.unset(key, attribution, c)
|
||||
}
|
||||
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
||||
} else {
|
||||
// find prev content
|
||||
let prevContentItem = item
|
||||
// this algorithm is problematic. should check all previous content using am.readcontent
|
||||
for (; prevContentItem.left !== null && deletedItems?.hasId(prevContentItem.left.lastId); prevContentItem = prevContentItem.left) {
|
||||
// nop
|
||||
}
|
||||
const prevValue = (prevContentItem !== item && itemsToRender?.hasId(prevContentItem.lastId)) ? array.last(prevContentItem.content.getContent()) : undefined
|
||||
d.set(key, c, attribution, prevValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
callTypeObservers,
|
||||
transact,
|
||||
warnPrematureAccess,
|
||||
typeListGetContent,
|
||||
typeListSlice,
|
||||
noAttributionsManager,
|
||||
AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
@@ -108,7 +107,7 @@ export class YArray extends AbstractType {
|
||||
*/
|
||||
_callObserver (transaction, parentSubs) {
|
||||
super._callObserver(transaction, parentSubs)
|
||||
callTypeObservers(this, transaction, new YEvent(this, transaction, null))
|
||||
callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,24 +213,6 @@ export class YArray extends AbstractType {
|
||||
return super.getContentDeep(am)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||
* attributions.
|
||||
*
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {delta.ArrayDelta<T>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager) {
|
||||
const d = this.change
|
||||
typeListGetContent(d, this, am)
|
||||
return d
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a portion of this YArray into a JavaScript Array selected
|
||||
* from start to end (end not included).
|
||||
|
||||
@@ -758,9 +758,11 @@ export class YText extends AbstractType {
|
||||
transact(this.doc, transaction => {
|
||||
const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am)
|
||||
for (const op of d.children) {
|
||||
if (delta.$insertOp.check(op)) {
|
||||
if (op.insert.length > 0 || typeof op.insert !== 'string') {
|
||||
insertText(transaction, this, currPos, op.insert, op.format || {})
|
||||
if (delta.$textOp.check(op)) {
|
||||
insertText(transaction, this, currPos, op.insert, op.format || {})
|
||||
} else if (delta.$insertOp.check(op)) {
|
||||
for (let i = 0; i < op.insert.length; i++) {
|
||||
insertText(transaction, this, currPos, op.insert[i], op.format || {})
|
||||
}
|
||||
} else if (delta.$retainOp.check(op)) {
|
||||
currPos.formatText(transaction, this, op.retain, op.format || {})
|
||||
|
||||
@@ -38,8 +38,8 @@ export const attributionJsonSchema = s.$object({
|
||||
insertedAt: s.$number.optional,
|
||||
delete: s.$array(s.$string).optional,
|
||||
deletedAt: s.$number.optional,
|
||||
attributes: s.$record(s.$string, s.$array(s.$string)).optional,
|
||||
attributedAt: s.$number.optional
|
||||
format: s.$record(s.$string, s.$array(s.$string)).optional,
|
||||
formatAt: s.$number.optional
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,10 +41,6 @@ export class YEvent {
|
||||
* @type {Transaction}
|
||||
*/
|
||||
this.transaction = transaction
|
||||
/**
|
||||
* @type {Object|null}
|
||||
*/
|
||||
this._changes = null
|
||||
/**
|
||||
* @type {null | Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
|
||||
*/
|
||||
@@ -183,7 +179,7 @@ export class YEvent {
|
||||
*/
|
||||
getDelta (am = noAttributionsManager) {
|
||||
const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)])
|
||||
return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged }))
|
||||
return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged, deletedItems: this.transaction.deleteSet }))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
|
||||
/**
|
||||
* @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt
|
||||
* |import('../index.js').Array<any>
|
||||
* |import('../index.js').Map<any>
|
||||
* |import('../index.js').Text<any>
|
||||
* |import('../index.js').XmlElement<any>
|
||||
* |import('../index.js').XmlFragment<any>
|
||||
* |import('../index.js').XmlText
|
||||
* |import('../index.js').XmlHook} YValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../types/YArray.js').YArray<any>
|
||||
* | import('../types/YMap.js').YMap<any>
|
||||
@@ -17,15 +6,17 @@
|
||||
* | import('../types/YXmlFragment.js').YXmlFragment<any,any>
|
||||
* | import('../types/YXmlElement.js').YXmlElement<any,any>
|
||||
* | import('../types/YXmlHook.js').YXmlHook
|
||||
* | import('../types/YXmlText.js').YXmlText} YType
|
||||
* | import('../types/YXmlText.js').YXmlText} YValueType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {typeof import('../types/YArray.js').YArray<any>
|
||||
* | typeof import('../types/YMap.js').YMap<any>
|
||||
* | typeof import('../types/YText.js').YText<any>
|
||||
* | typeof import('../types/YXmlFragment.js').YXmlFragment<any,any>
|
||||
* | typeof import('../types/YXmlElement.js').YXmlElement<any,any>
|
||||
* | typeof import('../types/YXmlHook.js').YXmlHook
|
||||
* | typeof import('../types/YXmlText.js').YXmlText} YTypeConstructors
|
||||
* @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt|YValueType} YValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../types/AbstractType.js').AbstractType<any,any>} YType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {typeof import('../types/AbstractType.js').AbstractType<any,any>} YTypeConstructors
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"lib0/conditions": "./node_modules/lib0/conditions.js",
|
||||
"lib0/crypto/jwt": "./node_modules/lib0/crypto/jwt.js",
|
||||
"lib0/crypto/aes-gcm": "./node_modules/lib0/crypto/aes-gcm.js",
|
||||
"lib0/delta": "./node_modules/lib0/delta/d2.js",
|
||||
"lib0/crypto/ecdsa": "./node_modules/lib0/crypto/ecdsa.js",
|
||||
"lib0/crypto/rsa-oaep": "./node_modules/lib0/crypto/rsa-oaep.js",
|
||||
"lib0/hash/rabin": "./node_modules/lib0/hash/rabin.js",
|
||||
@@ -201,6 +202,7 @@
|
||||
"lib0/conditions": "./node_modules/lib0/conditions.js",
|
||||
"lib0/crypto/jwt": "./node_modules/lib0/crypto/jwt.js",
|
||||
"lib0/crypto/aes-gcm": "./node_modules/lib0/crypto/aes-gcm.js",
|
||||
"lib0/delta": "./node_modules/lib0/delta/d2.js",
|
||||
"lib0/crypto/ecdsa": "./node_modules/lib0/crypto/ecdsa.js",
|
||||
"lib0/crypto/rsa-oaep": "./node_modules/lib0/crypto/rsa-oaep.js",
|
||||
"lib0/hash/rabin": "./node_modules/lib0/hash/rabin.js",
|
||||
|
||||
@@ -29,7 +29,7 @@ export const testFindTypeInOtherDoc = _tc => {
|
||||
const ydocClone = new Y.Doc()
|
||||
Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc))
|
||||
/**
|
||||
* @template {Y.AbstractType<any>} Type
|
||||
* @template {import('../src/utils/types.js').YType} Type
|
||||
* @param {Type} ytype
|
||||
* @param {Y.Doc} otherYdoc
|
||||
* @return {Type}
|
||||
@@ -47,7 +47,7 @@ export const testFindTypeInOtherDoc = _tc => {
|
||||
if (rootKey == null) {
|
||||
throw new Error('type does not exist in other ydoc')
|
||||
}
|
||||
return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {typeof Y.AbstractType<any>} */ (ytype.constructor)))
|
||||
return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {import('../src/utils/types.js').YTypeConstructors} */ (ytype.constructor)))
|
||||
} else {
|
||||
/**
|
||||
* If it is a sub type, we use the item id to find the history type.
|
||||
|
||||
@@ -7,6 +7,8 @@ import * as object from 'lib0/object'
|
||||
import * as map from 'lib0/map'
|
||||
import * as Y from '../src/index.js'
|
||||
import * as math from 'lib0/math'
|
||||
import * as list from 'lib0/list'
|
||||
import * as delta from 'lib0/delta'
|
||||
import {
|
||||
createIdSet, createIdMap, addToIdSet, encodeIdMap
|
||||
} from '../src/internals.js'
|
||||
@@ -484,7 +486,7 @@ export const compare = users => {
|
||||
t.compare(userArrayValues[i], userArrayValues[i + 1])
|
||||
t.compare(userMapValues[i], userMapValues[i + 1])
|
||||
t.compare(userXmlValues[i], userXmlValues[i + 1])
|
||||
t.compare(userTextValues[i].ops.map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length)
|
||||
t.compare(list.toArray(userTextValues[i].children).map(a => delta.$textOp.check(a) ? a.insert : ' ').join('').length, users[i].getText('text').length)
|
||||
t.compare(userTextValues[i], userTextValues[i + 1], '', (_constructor, a, b) => {
|
||||
if (a instanceof Y.AbstractType) {
|
||||
t.compare(a.toJSON(), b.toJSON())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Y from '../src/index.js'
|
||||
import { init } from './testHelper.js' // eslint-disable-line
|
||||
import * as t from 'lib0/testing'
|
||||
import * as delta from '../src/utils/Delta.js'
|
||||
import * as delta from 'lib0/delta'
|
||||
|
||||
export const testInconsistentFormat = () => {
|
||||
/**
|
||||
@@ -11,7 +11,7 @@ export const testInconsistentFormat = () => {
|
||||
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
|
||||
content.format(0, 6, { bold: null })
|
||||
content.format(6, 4, { type: 'text' })
|
||||
t.compare(content.getContent(), delta.createTextDelta().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true }))
|
||||
t.compare(content.getContent(), delta.create().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true }))
|
||||
}
|
||||
const initializeYDoc = () => {
|
||||
const yDoc = new Y.Doc({ gc: false })
|
||||
@@ -85,11 +85,11 @@ export const testUndoText = tc => {
|
||||
t.assert(text0.toString() === 'bcxyz')
|
||||
// test marks
|
||||
text0.format(1, 3, { bold: true })
|
||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
||||
t.compare(text0.getContent(), delta.create().insert('b').insert('cxy', { bold: true }).insert('z'))
|
||||
undoManager.undo()
|
||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }]))
|
||||
t.compare(text0.getContent(), delta.create().insert('bcxyz'))
|
||||
undoManager.redo()
|
||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
||||
t.compare(text0.getContent(), delta.create().insert('b').insert('cxy', { bold: true }).insert('z'))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -677,14 +677,10 @@ export const testUndoDeleteTextFormat = _tc => {
|
||||
undoManager.undo()
|
||||
Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc))
|
||||
|
||||
const expect = delta.fromJSON([
|
||||
{ insert: 'Attack ships ' },
|
||||
{
|
||||
insert: 'on fire',
|
||||
attributes: { bold: true }
|
||||
},
|
||||
{ insert: ' off the shoulder of Orion.' }
|
||||
])
|
||||
const expect = delta.create()
|
||||
.insert('Attack ships ')
|
||||
.insert('on fire', { bold: true })
|
||||
.insert(' off the shoulder of Orion.')
|
||||
t.compare(text.getContent(), expect)
|
||||
t.compare(text2.getContent(), expect)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { readStructSet, readIdSet, UpdateDecoderV2, UpdateEncoderV2, writeIdSet
|
||||
import * as encoding from 'lib0/encoding'
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as object from 'lib0/object'
|
||||
import * as delta from 'lib0/delta'
|
||||
|
||||
/**
|
||||
* @typedef {Object} Enc
|
||||
@@ -126,7 +127,7 @@ export const testKeyEncoding = tc => {
|
||||
const update = Y.encodeStateAsUpdateV2(users[0])
|
||||
Y.applyUpdateV2(users[1], update)
|
||||
|
||||
t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }])
|
||||
t.compare(text1.getContent().toJSON().children, [{ insert: 'c', format: { italic: true } }, { insert: 'b' }, { insert: 'a', format: { italic: true } }])
|
||||
|
||||
compare(users)
|
||||
}
|
||||
@@ -207,7 +208,7 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => {
|
||||
}
|
||||
|
||||
const meta = enc.parseUpdateMeta(mergedUpdates)
|
||||
meta.from.forEach((clock, client) => t.assert(clock === 0))
|
||||
meta.from.forEach((clock, _client) => t.assert(clock === 0))
|
||||
meta.to.forEach((clock, client) => {
|
||||
const structs = /** @type {Array<Y.Item>} */ (merged.store.clients.get(client))
|
||||
const lastStruct = structs[structs.length - 1]
|
||||
@@ -237,10 +238,10 @@ export const testMergeUpdates1 = _tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testMergeUpdates2 = tc => {
|
||||
encoders.forEach((enc, i) => {
|
||||
export const testMergeUpdates2 = _tc => {
|
||||
encoders.forEach((enc, _i) => {
|
||||
t.info(`Using encoder: ${enc.description}`)
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const updates = /** @type {Array<Uint8Array>} */ ([])
|
||||
@@ -257,23 +258,23 @@ export const testMergeUpdates2 = tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testMergePendingUpdates = tc => {
|
||||
export const testMergePendingUpdates = _tc => {
|
||||
const yDoc = new Y.Doc()
|
||||
/**
|
||||
* @type {Array<Uint8Array>}
|
||||
*/
|
||||
const serverUpdates = []
|
||||
yDoc.on('update', (update, origin, c) => {
|
||||
yDoc.on('update', (update, _origin, _c) => {
|
||||
serverUpdates.splice(serverUpdates.length, 0, update)
|
||||
})
|
||||
const yText = yDoc.getText('textBlock')
|
||||
yText.applyDelta([{ insert: 'r' }])
|
||||
yText.applyDelta([{ insert: 'o' }])
|
||||
yText.applyDelta([{ insert: 'n' }])
|
||||
yText.applyDelta([{ insert: 'e' }])
|
||||
yText.applyDelta([{ insert: 'n' }])
|
||||
yText.applyDelta(delta.create().insert('r'))
|
||||
yText.applyDelta(delta.create().insert('o'))
|
||||
yText.applyDelta(delta.create().insert('n'))
|
||||
yText.applyDelta(delta.create().insert('e'))
|
||||
yText.applyDelta(delta.create().insert('n'))
|
||||
|
||||
const yDoc1 = new Y.Doc()
|
||||
Y.applyUpdate(yDoc1, serverUpdates[0])
|
||||
@@ -297,8 +298,7 @@ export const testMergePendingUpdates = tc => {
|
||||
const yDoc5 = new Y.Doc()
|
||||
Y.applyUpdate(yDoc5, update4)
|
||||
Y.applyUpdate(yDoc5, serverUpdates[4])
|
||||
// @ts-ignore
|
||||
const _update5 = Y.encodeStateAsUpdate(yDoc5) // eslint-disable-line
|
||||
Y.encodeStateAsUpdate(yDoc5)
|
||||
|
||||
const yText5 = yDoc5.getText('textBlock')
|
||||
t.compareStrings(yText5.toString(), 'nenor')
|
||||
@@ -313,7 +313,7 @@ export const testObfuscateUpdates = _tc => {
|
||||
const ymap = ydoc.getMap('map')
|
||||
const yarray = ydoc.getArray('array')
|
||||
// test ytext
|
||||
ytext.applyDelta([{ insert: 'text', attributes: { bold: true } }, { insert: { href: 'supersecreturl' } }])
|
||||
ytext.applyDelta(delta.create().insert('text', { bold: true }).insert([{ href: 'supersecreturl' }]))
|
||||
// test ymap
|
||||
ymap.set('key', 'secret1')
|
||||
ymap.set('key', 'secret2')
|
||||
@@ -330,13 +330,14 @@ export const testObfuscateUpdates = _tc => {
|
||||
const omap = odoc.getMap('map')
|
||||
const oarray = odoc.getArray('array')
|
||||
// test ytext
|
||||
const delta = /** @type {Array<any>} */ (otext.getContent().toJSON())
|
||||
t.assert(delta.length === 2)
|
||||
t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4)
|
||||
t.assert(object.length(delta[0].attributes) === 1)
|
||||
t.assert(!object.hasProperty(delta[0].attributes, 'bold'))
|
||||
t.assert(object.length(delta[1]) === 1)
|
||||
t.assert(object.hasProperty(delta[1], 'insert'))
|
||||
const d = /** @type {any} */ (otext.getContent().toJSON().children)
|
||||
t.assert(d.length === 2)
|
||||
const q = d[0]
|
||||
t.assert(d[0].insert !== 'text' && d[0].insert.length === 4)
|
||||
t.assert(object.length(d[0].format) === 1)
|
||||
t.assert(!object.hasProperty(d[0].format, 'bold'))
|
||||
t.assert(object.length(d[1]) === 1)
|
||||
t.assert(object.hasProperty(d[1], 'insert'))
|
||||
// test ymap
|
||||
t.assert(omap.size === 1)
|
||||
t.assert(!omap.has('key'))
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
import * as math from 'lib0/math'
|
||||
import * as env from 'lib0/environment'
|
||||
import * as delta from '../src/utils/Delta.js'
|
||||
import * as delta from 'lib0/delta'
|
||||
|
||||
const isDevMode = env.getVariable('node_env') === 'development'
|
||||
|
||||
@@ -384,24 +384,22 @@ export const testObservedeepIndexes = _tc => {
|
||||
export const testChangeEvent = tc => {
|
||||
const { array0, users } = init(tc, { users: 2 })
|
||||
/**
|
||||
* @type {any}
|
||||
* @type {delta.Delta<any,any,any,any,any>}
|
||||
*/
|
||||
let changes = null
|
||||
let d = delta.create()
|
||||
array0.observe(e => {
|
||||
changes = e.changes
|
||||
d = e.delta
|
||||
})
|
||||
const newArr = new Y.Array()
|
||||
array0.insert(0, [newArr, 4, 'dtrn'])
|
||||
t.assert(changes !== null && changes.added.size === 2 && changes.deleted.size === 0)
|
||||
t.compare(changes.delta, [{ insert: [newArr, 4, 'dtrn'] }])
|
||||
changes = null
|
||||
t.assert(d !== null && d.children.len === 1)
|
||||
t.compare(d.toJSON().children, [{ insert: [newArr, 4, 'dtrn'] }])
|
||||
array0.delete(0, 2)
|
||||
t.assert(changes !== null && changes.added.size === 0 && changes.deleted.size === 2)
|
||||
t.compare(changes.delta, [{ delete: 2 }])
|
||||
changes = null
|
||||
t.assert(d !== null && d.children.len === 1)
|
||||
t.compare(d.toJSON().children, [{ delete: 2 }])
|
||||
array0.insert(1, [0.1])
|
||||
t.assert(changes !== null && changes.added.size === 1 && changes.deleted.size === 0)
|
||||
t.compare(changes.delta, [{ retain: 1 }, { insert: [0.1] }])
|
||||
t.assert(d !== null && d.children.len === 2)
|
||||
t.compare(d.toJSON().children, [{ retain: 1 }, { insert: [0.1] }])
|
||||
compare(users)
|
||||
}
|
||||
|
||||
@@ -531,7 +529,7 @@ export const testAttributedContent = _tc => {
|
||||
yarray.delete(0, 1)
|
||||
yarray.insert(1, [42])
|
||||
})
|
||||
const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
||||
const expectedContent = delta.create().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
||||
const attributedContent = yarray.getContent(attributionManager)
|
||||
console.log(attributedContent.toJSON())
|
||||
t.assert(attributedContent.equals(expectedContent))
|
||||
|
||||
@@ -4,11 +4,12 @@ import {
|
||||
compareIDs,
|
||||
noAttributionsManager,
|
||||
TwosetAttributionManager,
|
||||
createIdMapFromIdSet,
|
||||
mapDeltaJsonSchema
|
||||
createIdMapFromIdSet
|
||||
} from '../src/internals.js'
|
||||
import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
import * as delta from 'lib0/delta'
|
||||
import * as s from 'lib0/schema'
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
@@ -490,45 +491,41 @@ export const testThrowsDeleteEventsOnClear = tc => {
|
||||
export const testChangeEvent = tc => {
|
||||
const { map0, users } = init(tc, { users: 2 })
|
||||
/**
|
||||
* @type {any}
|
||||
* @type {delta.Delta<any,any,any,any>?}
|
||||
*/
|
||||
let changes = null
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let keyChange = null
|
||||
let changes = delta.create()
|
||||
map0.observe(e => {
|
||||
changes = e.changes
|
||||
changes = e.delta
|
||||
})
|
||||
map0.set('a', 1)
|
||||
keyChange = changes.keys.get('a')
|
||||
t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined)
|
||||
let keyChange = changes.attrs.get('a')
|
||||
t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined)
|
||||
map0.set('a', 2)
|
||||
keyChange = changes.keys.get('a')
|
||||
t.assert(changes !== null && keyChange.action === 'update' && keyChange.oldValue === 1)
|
||||
keyChange = changes.attrs.get('a')
|
||||
t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 1)
|
||||
users[0].transact(() => {
|
||||
map0.set('a', 3)
|
||||
map0.set('a', 4)
|
||||
})
|
||||
keyChange = changes.keys.get('a')
|
||||
t.assert(changes !== null && keyChange.action === 'update' && keyChange.oldValue === 2)
|
||||
keyChange = changes.attrs.get('a')
|
||||
t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 2)
|
||||
users[0].transact(() => {
|
||||
map0.set('b', 1)
|
||||
map0.set('b', 2)
|
||||
})
|
||||
keyChange = changes.keys.get('b')
|
||||
t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined)
|
||||
keyChange = changes.attrs.get('b')
|
||||
t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined)
|
||||
users[0].transact(() => {
|
||||
map0.set('c', 1)
|
||||
map0.delete('c')
|
||||
})
|
||||
t.assert(changes !== null && changes.keys.size === 0)
|
||||
t.assert(changes !== null && changes.attrs.size === 0)
|
||||
users[0].transact(() => {
|
||||
map0.set('d', 1)
|
||||
map0.set('d', 2)
|
||||
})
|
||||
keyChange = changes.keys.get('d')
|
||||
t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined)
|
||||
keyChange = changes.attrs.get('d')
|
||||
t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined)
|
||||
compare(users)
|
||||
}
|
||||
|
||||
@@ -631,24 +628,24 @@ export const testAttributedContent = _tc => {
|
||||
})
|
||||
t.group('initial value', () => {
|
||||
ymap.set('test', 42)
|
||||
const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: undefined, value: 42, attribution: { insert: [] } } })
|
||||
const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', value: 42, attribution: { insert: [] } }) }
|
||||
const attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent.toJSON())
|
||||
t.compare(expectedContent, attributedContent.toJSON())
|
||||
t.compare(expectedContent, attributedContent.toJSON().attrs)
|
||||
})
|
||||
t.group('overwrite value', () => {
|
||||
ymap.set('test', 'fourtytwo')
|
||||
const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } })
|
||||
const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } }) }
|
||||
const attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent)
|
||||
t.compare(expectedContent, attributedContent.toJSON())
|
||||
t.compare(expectedContent, attributedContent.toJSON().attrs)
|
||||
})
|
||||
t.group('delete value', () => {
|
||||
ymap.delete('test')
|
||||
const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'delete', prevValue: 'fourtytwo', attribution: { delete: [] } } })
|
||||
const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'delete', prevValue: 'fourtytwo', attribution: { delete: [] } }) }
|
||||
const attributedContent = ymap.getContent(attributionManager)
|
||||
console.log(attributedContent)
|
||||
t.compare(expectedContent, attributedContent.toJSON())
|
||||
console.log(attributedContent.toJSON())
|
||||
t.compare(expectedContent, attributedContent.toJSON().attrs)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,5 +20,5 @@
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*.js", "./tests/**/*.js"],
|
||||
"exclude": ["../lib0/**"]
|
||||
"exclude": ["./node_modules/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user