2019-03-01 23:26:40 +01:00
|
|
|
import * as Y from '../src/index.js'
|
2025-03-25 11:14:55 +01:00
|
|
|
import { init, compare } from './testHelper.js'
|
2021-05-14 18:53:24 +02:00
|
|
|
import * as t from 'lib0/testing'
|
2025-10-20 02:14:02 +02:00
|
|
|
import * as delta from 'lib0/delta'
|
2017-08-24 14:44:23 +02:00
|
|
|
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testCustomTypings = () => {
|
|
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const ymap = ydoc.getMap()
|
|
|
|
|
/**
|
|
|
|
|
* @type {Y.XmlElement<{ num: number, str: string, [k:string]: object|number|string }>}
|
|
|
|
|
*/
|
|
|
|
|
const yxml = ymap.set('yxml', new Y.XmlElement('test'))
|
|
|
|
|
/**
|
|
|
|
|
* @type {number|undefined}
|
|
|
|
|
*/
|
|
|
|
|
const num = yxml.getAttribute('num')
|
|
|
|
|
/**
|
|
|
|
|
* @type {string|undefined}
|
|
|
|
|
*/
|
|
|
|
|
const str = yxml.getAttribute('str')
|
|
|
|
|
/**
|
|
|
|
|
* @type {object|number|string|undefined}
|
|
|
|
|
*/
|
|
|
|
|
const dtrn = yxml.getAttribute('dtrn')
|
|
|
|
|
const attrs = yxml.getAttributes()
|
|
|
|
|
/**
|
|
|
|
|
* @type {object|number|string|undefined}
|
|
|
|
|
*/
|
|
|
|
|
const any = attrs.shouldBeAny
|
|
|
|
|
console.log({ num, str, dtrn, attrs, any })
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-12 01:42:51 +01:00
|
|
|
export const testSetProperty = tc => {
|
|
|
|
|
const { testConnector, users, xml0, xml1 } = init(tc, { users: 2 })
|
2018-03-23 01:55:47 +01:00
|
|
|
xml0.setAttribute('height', '10')
|
|
|
|
|
t.assert(xml0.getAttribute('height') === '10', 'Simple set+get works')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2018-03-23 01:55:47 +01:00
|
|
|
t.assert(xml1.getAttribute('height') === '10', 'Simple set+get works (remote)')
|
2019-03-12 01:42:51 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-24 14:44:23 +02:00
|
|
|
|
2021-06-15 16:36:22 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2021-06-24 16:50:25 +02:00
|
|
|
export const testHasProperty = tc => {
|
2021-06-15 16:36:22 +02:00
|
|
|
const { testConnector, users, xml0, xml1 } = init(tc, { users: 2 })
|
|
|
|
|
xml0.setAttribute('height', '10')
|
|
|
|
|
t.assert(xml0.hasAttribute('height'), 'Simple set+has works')
|
|
|
|
|
testConnector.flushAllMessages()
|
|
|
|
|
t.assert(xml1.hasAttribute('height'), 'Simple set+has works (remote)')
|
|
|
|
|
|
|
|
|
|
xml0.removeAttribute('height')
|
|
|
|
|
t.assert(!xml0.hasAttribute('height'), 'Simple set+remove+has works')
|
|
|
|
|
testConnector.flushAllMessages()
|
|
|
|
|
t.assert(!xml1.hasAttribute('height'), 'Simple set+remove+has works (remote)')
|
|
|
|
|
compare(users)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-12 01:42:51 +01:00
|
|
|
export const testEvents = tc => {
|
|
|
|
|
const { testConnector, users, xml0, xml1 } = init(tc, { users: 2 })
|
2019-04-03 13:23:27 +02:00
|
|
|
/**
|
|
|
|
|
* @type {any}
|
|
|
|
|
*/
|
|
|
|
|
let event
|
|
|
|
|
/**
|
|
|
|
|
* @type {any}
|
|
|
|
|
*/
|
|
|
|
|
let remoteEvent
|
2018-11-25 03:17:00 +01:00
|
|
|
xml0.observe(e => {
|
2017-08-24 14:44:23 +02:00
|
|
|
event = e
|
|
|
|
|
})
|
2018-11-25 03:17:00 +01:00
|
|
|
xml1.observe(e => {
|
2017-08-24 14:44:23 +02:00
|
|
|
remoteEvent = e
|
|
|
|
|
})
|
|
|
|
|
xml0.setAttribute('key', 'value')
|
2018-03-23 01:55:47 +01:00
|
|
|
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2018-03-29 11:58:02 +02:00
|
|
|
t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key (remote)')
|
2017-08-24 14:44:23 +02:00
|
|
|
// check attributeRemoved
|
|
|
|
|
xml0.removeAttribute('key')
|
2018-03-23 01:55:47 +01:00
|
|
|
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2018-03-29 11:58:02 +02:00
|
|
|
t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute (remote)')
|
2017-10-19 17:36:28 +02:00
|
|
|
xml0.insert(0, [new Y.XmlText('some text')])
|
2018-03-23 01:55:47 +01:00
|
|
|
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2018-03-29 11:58:02 +02:00
|
|
|
t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on inserted element (remote)')
|
2017-08-24 14:44:23 +02:00
|
|
|
// test childRemoved
|
|
|
|
|
xml0.delete(0)
|
2018-03-23 01:55:47 +01:00
|
|
|
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2018-03-29 11:58:02 +02:00
|
|
|
t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on deleted element (remote)')
|
2019-03-12 01:42:51 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-24 14:44:23 +02:00
|
|
|
|
2020-11-13 12:40:53 +01:00
|
|
|
/**
|
2023-05-04 11:26:11 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2020-11-13 12:40:53 +01:00
|
|
|
*/
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testYtextAttributes = _tc => {
|
2020-11-13 12:40:53 +01:00
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const ytext = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText))
|
|
|
|
|
ytext.observe(event => {
|
2025-10-20 02:14:02 +02:00
|
|
|
t.assert(event.delta.attrs.get('test')?.type === 'insert')
|
2020-11-13 12:40:53 +01:00
|
|
|
})
|
|
|
|
|
ytext.setAttribute('test', 42)
|
|
|
|
|
t.compare(ytext.getAttribute('test'), 42)
|
|
|
|
|
t.compare(ytext.getAttributes(), { test: 42 })
|
|
|
|
|
}
|
2020-11-14 13:33:43 +01:00
|
|
|
|
|
|
|
|
/**
|
2023-05-04 11:26:11 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2020-11-14 13:33:43 +01:00
|
|
|
*/
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testSiblings = _tc => {
|
2020-11-14 13:33:43 +01:00
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const yxml = ydoc.getXmlFragment()
|
|
|
|
|
const first = new Y.XmlText()
|
|
|
|
|
const second = new Y.XmlElement('p')
|
|
|
|
|
yxml.insert(0, [first, second])
|
|
|
|
|
t.assert(first.nextSibling === second)
|
|
|
|
|
t.assert(second.prevSibling === first)
|
|
|
|
|
t.assert(first.parent === yxml)
|
|
|
|
|
t.assert(yxml.parent === null)
|
|
|
|
|
t.assert(yxml.firstChild === first)
|
|
|
|
|
}
|
2020-11-15 14:57:45 +01:00
|
|
|
|
|
|
|
|
/**
|
2023-05-04 11:26:11 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2020-11-15 14:57:45 +01:00
|
|
|
*/
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testInsertafter = _tc => {
|
2020-11-15 14:57:45 +01:00
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const yxml = ydoc.getXmlFragment()
|
|
|
|
|
const first = new Y.XmlText()
|
|
|
|
|
const second = new Y.XmlElement('p')
|
|
|
|
|
const third = new Y.XmlElement('p')
|
|
|
|
|
|
|
|
|
|
const deepsecond1 = new Y.XmlElement('span')
|
|
|
|
|
const deepsecond2 = new Y.XmlText()
|
|
|
|
|
second.insertAfter(null, [deepsecond1])
|
|
|
|
|
second.insertAfter(deepsecond1, [deepsecond2])
|
|
|
|
|
|
|
|
|
|
yxml.insertAfter(null, [first, second])
|
|
|
|
|
yxml.insertAfter(second, [third])
|
|
|
|
|
|
|
|
|
|
t.assert(yxml.length === 3)
|
|
|
|
|
t.assert(second.get(0) === deepsecond1)
|
|
|
|
|
t.assert(second.get(1) === deepsecond2)
|
|
|
|
|
|
|
|
|
|
t.compareArrays(yxml.toArray(), [first, second, third])
|
|
|
|
|
|
|
|
|
|
t.fails(() => {
|
|
|
|
|
const el = new Y.XmlElement('p')
|
|
|
|
|
el.insertAfter(deepsecond1, [new Y.XmlText()])
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-06-08 21:30:08 -04:00
|
|
|
|
|
|
|
|
/**
|
2023-05-04 11:26:11 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2021-06-08 21:30:08 -04:00
|
|
|
*/
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testClone = _tc => {
|
2021-06-08 21:30:08 -04:00
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const yxml = ydoc.getXmlFragment()
|
|
|
|
|
const first = new Y.XmlText('text')
|
|
|
|
|
const second = new Y.XmlElement('p')
|
|
|
|
|
const third = new Y.XmlElement('p')
|
|
|
|
|
yxml.push([first, second, third])
|
|
|
|
|
t.compareArrays(yxml.toArray(), [first, second, third])
|
|
|
|
|
const cloneYxml = yxml.clone()
|
2021-08-02 16:20:01 +02:00
|
|
|
ydoc.getArray('copyarr').insert(0, [cloneYxml])
|
2021-06-08 21:30:08 -04:00
|
|
|
t.assert(cloneYxml.length === 3)
|
2021-08-02 16:20:01 +02:00
|
|
|
t.compare(cloneYxml.toJSON(), yxml.toJSON())
|
2021-06-08 21:30:08 -04:00
|
|
|
}
|
2021-08-02 16:43:25 +02:00
|
|
|
|
|
|
|
|
/**
|
2023-05-04 11:26:11 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2021-08-02 16:43:25 +02:00
|
|
|
*/
|
2023-05-04 11:26:11 +02:00
|
|
|
export const testFormattingBug = _tc => {
|
2021-08-02 16:43:25 +02:00
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const yxml = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText))
|
2025-10-20 02:14:02 +02:00
|
|
|
const q = delta.create()
|
|
|
|
|
.insert('A', { em: {}, strong: {} })
|
|
|
|
|
.insert('B', { em: {} })
|
|
|
|
|
.insert('C', { em: {}, strong: {} })
|
|
|
|
|
yxml.applyDelta(q)
|
|
|
|
|
t.compare(yxml.getContent(), q)
|
2021-08-02 16:43:25 +02:00
|
|
|
}
|
2024-02-05 13:24:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testElement = _tc => {
|
|
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
const yxmlel = ydoc.getXmlElement()
|
|
|
|
|
const text1 = new Y.XmlText('text1')
|
|
|
|
|
const text2 = new Y.XmlText('text2')
|
|
|
|
|
yxmlel.insert(0, [text1, text2])
|
|
|
|
|
t.compareArrays(yxmlel.toArray(), [text1, text2])
|
|
|
|
|
}
|
2025-04-28 17:06:32 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testFragmentAttributedContent = _tc => {
|
|
|
|
|
const ydoc = new Y.Doc({ gc: false })
|
|
|
|
|
const yfragment = new Y.XmlFragment()
|
|
|
|
|
const elem1 = new Y.XmlText('hello')
|
|
|
|
|
const elem2 = new Y.XmlElement()
|
|
|
|
|
const elem3 = new Y.XmlText('world')
|
|
|
|
|
yfragment.insert(0, [elem1, elem2])
|
|
|
|
|
ydoc.getArray().insert(0, [yfragment])
|
|
|
|
|
let attributionManager = Y.noAttributionsManager
|
|
|
|
|
ydoc.on('afterTransaction', tr => {
|
|
|
|
|
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
|
|
|
|
attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, []))
|
|
|
|
|
})
|
|
|
|
|
t.group('insert / delete', () => {
|
|
|
|
|
ydoc.transact(() => {
|
|
|
|
|
yfragment.delete(0, 1)
|
|
|
|
|
yfragment.insert(1, [elem3])
|
|
|
|
|
})
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
2025-07-19 16:17:05 +02:00
|
|
|
const attributedContent = yfragment.getContent(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log(attributedContent.toJSON())
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(elem1.getContent(attributionManager).toJSON(), delta.create().insert('hello', null, { delete: [] }).toJSON())
|
2025-04-28 17:06:32 +02:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testElementAttributedContent = _tc => {
|
|
|
|
|
const ydoc = new Y.Doc({ gc: false })
|
|
|
|
|
const yelement = ydoc.getXmlElement('p')
|
2025-04-29 18:02:15 +02:00
|
|
|
const elem1 = new Y.XmlText('hello')
|
|
|
|
|
const elem2 = new Y.XmlElement('span')
|
2025-04-28 17:06:32 +02:00
|
|
|
const elem3 = new Y.XmlText('world')
|
|
|
|
|
yelement.insert(0, [elem1, elem2])
|
|
|
|
|
let attributionManager = Y.noAttributionsManager
|
|
|
|
|
ydoc.on('afterTransaction', tr => {
|
|
|
|
|
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
|
|
|
|
attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, []))
|
|
|
|
|
})
|
|
|
|
|
t.group('insert / delete', () => {
|
|
|
|
|
ydoc.transact(() => {
|
|
|
|
|
yelement.delete(0, 1)
|
|
|
|
|
yelement.insert(1, [elem3])
|
|
|
|
|
yelement.setAttribute('key', '42')
|
|
|
|
|
})
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
2025-07-19 16:17:05 +02:00
|
|
|
const attributedContent = yelement.getContent(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', attributedContent.toJSON())
|
|
|
|
|
console.log('attributes', attributedContent)
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
2025-04-29 18:02:15 +02:00
|
|
|
t.group('test getContentDeep', () => {
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert(
|
|
|
|
|
[delta.text().insert('hello', null, { delete: [] })],
|
2025-04-29 18:02:15 +02:00
|
|
|
null,
|
|
|
|
|
{ delete: [] }
|
2025-10-20 02:14:02 +02:00
|
|
|
).insert([delta.create('span')])
|
2025-04-29 22:42:56 +02:00
|
|
|
.insert([
|
2025-10-20 02:14:02 +02:00
|
|
|
delta.text().insert('world', null, { insert: [] })
|
2025-04-29 22:42:56 +02:00
|
|
|
], null, { insert: [] })
|
|
|
|
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
|
2025-05-07 22:43:23 +02:00
|
|
|
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('attributes', attributedContent.toJSON().attrs)
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent, /** @type {delta.MapDelta<any>} */ (delta.map()).set('key', '42', { insert: [] }))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
|
|
|
|
t.assert(attributedContent.name === 'UNDEFINED')
|
2025-04-29 22:42:56 +02:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testElementAttributedContentViaDiffer = _tc => {
|
|
|
|
|
const ydocV1 = new Y.Doc()
|
|
|
|
|
ydocV1.getXmlElement('p').insert(0, [new Y.XmlText('hello'), new Y.XmlElement('span')])
|
|
|
|
|
const ydoc = new Y.Doc()
|
|
|
|
|
Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocV1))
|
|
|
|
|
const yelement = ydoc.getXmlElement('p')
|
|
|
|
|
const elem2 = yelement.get(1) // new Y.XmlElement('span')
|
|
|
|
|
const elem3 = new Y.XmlText('world')
|
2025-04-30 23:17:15 +02:00
|
|
|
ydoc.transact(() => {
|
|
|
|
|
yelement.delete(0, 1)
|
|
|
|
|
yelement.insert(1, [elem3])
|
|
|
|
|
yelement.setAttribute('key', '42')
|
|
|
|
|
})
|
|
|
|
|
const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc)
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] })
|
2025-04-30 23:17:15 +02:00
|
|
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', attributedContent.toJSON().children)
|
|
|
|
|
console.log('attributes', attributedContent.toJSON().attrs)
|
|
|
|
|
t.compare(attributedContent.toJSON(), expectedContent.toJSON())
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
2025-04-30 23:17:15 +02:00
|
|
|
t.group('test getContentDeep', () => {
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert(
|
|
|
|
|
[delta.create().insert('hello')],
|
2025-04-30 23:17:15 +02:00
|
|
|
null,
|
|
|
|
|
{ delete: [] }
|
2025-10-20 02:14:02 +02:00
|
|
|
).insert([delta.create('span')])
|
2025-04-30 23:17:15 +02:00
|
|
|
.insert([
|
2025-10-20 02:14:02 +02:00
|
|
|
delta.create().insert('world', null, { insert: [] })
|
2025-04-30 23:17:15 +02:00
|
|
|
], null, { insert: [] })
|
2025-04-29 22:42:56 +02:00
|
|
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
|
2025-05-07 22:43:23 +02:00
|
|
|
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('attributes', attributedContent.toJSON().attrs)
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
|
|
|
|
t.assert(attributedContent.name === 'UNDEFINED')
|
2025-04-30 23:17:15 +02:00
|
|
|
})
|
|
|
|
|
ydoc.transact(() => {
|
|
|
|
|
elem3.insert(0, 'big')
|
|
|
|
|
})
|
|
|
|
|
t.group('test getContentDeep after some more updates', () => {
|
|
|
|
|
t.info('expecting diffingAttributionManager to auto update itself')
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert(
|
|
|
|
|
[delta.create().insert('hello')],
|
2025-04-30 23:17:15 +02:00
|
|
|
null,
|
|
|
|
|
{ delete: [] }
|
2025-10-20 02:14:02 +02:00
|
|
|
).insert([delta.create('span')])
|
2025-04-30 23:17:15 +02:00
|
|
|
.insert([
|
2025-10-20 02:14:02 +02:00
|
|
|
delta.create().insert('bigworld', null, { insert: [] })
|
2025-04-30 23:17:15 +02:00
|
|
|
], null, { insert: [] })
|
|
|
|
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
|
2025-05-07 22:43:23 +02:00
|
|
|
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('attributes', attributedContent.toJSON().attrs)
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
|
|
|
|
t.assert(attributedContent.name === 'UNDEFINED')
|
2025-04-30 23:17:15 +02:00
|
|
|
})
|
|
|
|
|
Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc))
|
|
|
|
|
t.group('test getContentDeep both docs synced', () => {
|
|
|
|
|
t.info('expecting diffingAttributionManager to auto update itself')
|
2025-10-20 02:14:02 +02:00
|
|
|
const expectedContent = delta.create().insert([delta.create('span')]).insert([
|
|
|
|
|
delta.create().insert('bigworld')
|
2025-06-05 14:52:55 +02:00
|
|
|
])
|
2025-04-30 23:17:15 +02:00
|
|
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
|
2025-05-07 00:35:57 +02:00
|
|
|
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
|
2025-10-20 02:14:02 +02:00
|
|
|
console.log('attributes', attributedContent.toJSON().attrs)
|
|
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42' } })
|
|
|
|
|
t.assert(attributedContent.name === 'UNDEFINED')
|
2025-04-28 17:06:32 +02:00
|
|
|
})
|
|
|
|
|
}
|
2025-04-29 23:29:02 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testAttributionManagerSimpleExample = _tc => {
|
2025-04-30 18:13:54 +02:00
|
|
|
const ydoc = new Y.Doc()
|
2025-05-01 14:44:24 +02:00
|
|
|
ydoc.clientID = 0
|
2025-04-30 18:13:54 +02:00
|
|
|
// create some initial content
|
|
|
|
|
ydoc.getXmlFragment().insert(0, [new Y.XmlText('hello world')])
|
|
|
|
|
const ydocFork = new Y.Doc()
|
2025-05-01 14:44:24 +02:00
|
|
|
ydocFork.clientID = 1
|
2025-04-30 18:13:54 +02:00
|
|
|
Y.applyUpdate(ydocFork, Y.encodeStateAsUpdate(ydoc))
|
|
|
|
|
// modify the fork
|
|
|
|
|
// append a span element
|
|
|
|
|
ydocFork.getXmlFragment().insert(1, [new Y.XmlElement('span')])
|
|
|
|
|
const ytext = /** @type {Y.XmlText} */ (ydocFork.getXmlFragment().get(0))
|
|
|
|
|
// make "hello" italic
|
|
|
|
|
ytext.format(0, 5, { italic: true })
|
2025-05-01 14:44:24 +02:00
|
|
|
ytext.insert(11, 'deleteme')
|
|
|
|
|
ytext.delete(11, 8)
|
2025-04-30 18:13:54 +02:00
|
|
|
ytext.insert(11, '!')
|
|
|
|
|
// highlight the changes
|
|
|
|
|
console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttributionManagerFromDiff(ydoc, ydocFork)), null, 2))
|
2025-04-29 23:29:02 +02:00
|
|
|
/* =>
|
|
|
|
|
{
|
|
|
|
|
"children": {
|
|
|
|
|
"ops": [
|
|
|
|
|
{
|
|
|
|
|
"insert": [
|
|
|
|
|
{
|
|
|
|
|
"ops": [
|
|
|
|
|
{
|
|
|
|
|
"insert": "hello",
|
|
|
|
|
"attributes": {
|
|
|
|
|
"italic": true
|
|
|
|
|
},
|
|
|
|
|
"attribution": {
|
|
|
|
|
"attributes": {
|
|
|
|
|
"italic": [] -- the attribute "italic" was changed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"insert": " world" -- "world" remains unchanged
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"insert": "!",
|
|
|
|
|
"attribution": {
|
|
|
|
|
"insert": [] -- "!" was inserted
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"insert": [
|
|
|
|
|
{
|
|
|
|
|
"nodeName": "span",
|
|
|
|
|
"children": {
|
|
|
|
|
"ops": []
|
|
|
|
|
},
|
|
|
|
|
"attributes": {}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"attribution": {
|
|
|
|
|
"insert": [] -- A <span/> tag was inserted
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|