fix all tests

This commit is contained in:
Kevin Jahns
2025-11-03 18:13:34 +01:00
parent 2f7895e5ce
commit 19b5134604
8 changed files with 83 additions and 85 deletions

View File

@@ -571,7 +571,7 @@ export class AbstractType {
if (c.deleted ? retainDeletes : retainInserts) {
d.retain(c.content.getLength(), null, attribution ?? {})
} else if (deep && c.content.constructor === ContentType) {
d.insert([/** @type {any} */(c.content).type.getContent(am, opts)], null, attribution)
d.insert([/** @type {any} */(c.content).type.getContent(am, { ...opts, renderChildren: true, renderAttrs: null })], null, attribution)
} else {
d.insert(c.content.getContent(), null, attribution)
}
@@ -579,6 +579,7 @@ export class AbstractType {
d.delete(1)
} else if (retainContent) {
if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) {
// @todo use current transaction instead
d.modify(/** @type {any} */ (c.content).type.getContent(am, opts))
} else {
d.usedAttributes = changedAttributes

View File

@@ -3,7 +3,6 @@
*/
import {
YEvent,
AbstractType,
typeListGet,
typeListToArray,

View File

@@ -3,7 +3,6 @@
*/
import {
YEvent,
AbstractType,
typeListMap,
typeListForEach,

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
import * as Y from '../src/index.js'
import * as delta from 'lib0/delta'
import * as t from 'lib0/testing'
import * as s from 'lib0/schema'
/**
* Delta is a versatyle format enabling you to efficiently describe changes. It is part of lib0, so
@@ -20,7 +21,7 @@ import * as t from 'lib0/testing'
* The "|" describes the current cursor position.
*
* - d.retain(5) - "|hello world" => "hello| world" - jump over the next five characters
* - d.delete(6) - "hello| world" => "hello|" - delete the next 6 characres
* - d.delete(6) - "hello| world" => "hello|" - delete the next 6 characres
* - d.insert('!') - "hello!|" - insert "!" at the current position
* => compact form: d.retain(5).delete(6).insert('!')
*
@@ -44,6 +45,34 @@ export const testDeltaBasics = _tc => {
t.assert(state.equals(delta.create().insert('hello!')))
}
/**
* lib0 also ships a schema library that can be used to validate JSON objects and custom data types,
* like Yjs types.
*
* As a convention, schemas are usually prefixed with a $ sign. This clarifies the difference
* between a schema, and an instance of a schema.
*
* const $myobj = s.$object({ key: s.$number })
* let inputValue: any
* if ($myobj.check(inputValue)) {
* inputValue // is validated and of type $myobj
* }
*
* We can also define the expected values on a delta.
*
* @param {t.TestCase} _tc
*/
export const testDeltaBasicSchema = _tc => {
const $d = delta.$delta({ attrs: s.$object({ key: s.$string }), children: s.$number, hasText: false })
const d = delta.create($d)
// @ts-expect-error
d.set('key', false) // invalid change: will throw a type error
t.fails(() => {
// @ts-expect-error
d.apply(delta.create().set('key', false)) // invalid delta: will throw a type error
})
}
/**
* Deltas can describe changes on attributes and children. Textual insertions are children. But we
* may also insert json-objects and other deltas as children.
@@ -71,9 +100,9 @@ export const testDeltaValues = _tc => {
} else if (delta.$deleteOp.check(childChange)) {
console.log(`delete ${childChange.delete} child items`)
} else if (delta.$insertOp.check(childChange)) {
console.log(`insert child items:`, childChange.insert)
console.log('insert child items:', childChange.insert)
} else if (delta.$textOp.check(childChange)) {
console.log(`insert textual content`, childChange.insert)
console.log('insert textual content', childChange.insert)
}
}
}

View File

@@ -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.create().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 }).done())
}
const initializeYDoc = () => {
const yDoc = new Y.Doc({ gc: false })
@@ -298,17 +298,18 @@ export const testUndoXml = tc => {
t.assert(xml0.toString() === '<undefined><p>content</p></undefined>')
// format textchild and revert that change
undoManager.stopCapturing()
textchild.format(3, 4, { bold: {} })
t.compare(xml0.getContentDeep(), delta.create('UNDEFINED').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done())
t.assert(xml0.toString() === '<undefined><p>con<bold>tent</bold></p></undefined>')
textchild.format(3, 4, { bold: true })
const v1 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()]).done()
const v2 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('content').done()]).done()]).done()
t.compare(xml0.getContentDeep(), v1)
undoManager.undo()
t.assert(xml0.toString() === '<undefined><p>content</p></undefined>')
t.compare(xml0.getContentDeep(), v2)
undoManager.redo()
t.assert(xml0.toString() === '<undefined><p>con<bold>tent</bold></p></undefined>')
t.compare(xml0.getContentDeep(), v1)
xml0.delete(0, 1)
t.assert(xml0.toString() === '<undefined></undefined>')
t.compare(xml0.getContentDeep(), delta.create('UNDEFINED'))
undoManager.undo()
t.assert(xml0.toString() === '<undefined><p>con<bold>tent</bold></p></undefined>')
t.compare(xml0.getContentDeep(), v1)
}
/**

View File

@@ -1439,18 +1439,18 @@ export const testTypesAsEmbed = tc => {
text0.applyDelta(delta.create()
.insert([new Y.Map([['key', 'val']])])
)
t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ attrs: { key: { type: 'insert', value: 'val' } } }] }])
t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } }] }])
let firedEvent = false
text1.observe(event => {
const d = event.deltaDeep
t.assert(d.children.len === 1)
t.compare(d.toJSON().children?.map(x => /** @type {any} */ (x).insert), [[{ key: { type: 'insert', value: 'val' } }]])
t.compare(d.toJSON().children?.map(x => /** @type {any} */ (x).insert), [[{ type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } }]])
firedEvent = true
})
testConnector.flushAllMessages()
const dd = text1.getContent().toJSON().children
const dd = text1.getContentDeep().toJSON().children
t.assert(dd?.length === 1)
t.compare(/** @type {any} */ (dd?.[0]).insert, { key: 'val' })
t.compare(/** @type {any} */ (dd?.[0]).insert[0], { type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } })
t.assert(firedEvent, 'fired the event observer containing a Type-Embed')
}

View File

@@ -59,46 +59,6 @@ export const testHasProperty = tc => {
compare(users)
}
/**
* @param {t.TestCase} tc
*/
export const testEvents = tc => {
const { testConnector, users, xml0, xml1 } = init(tc, { users: 2 })
/**
* @type {any}
*/
let event
/**
* @type {any}
*/
let remoteEvent
xml0.observe(e => {
event = e
})
xml1.observe(e => {
remoteEvent = e
})
xml0.setAttribute('key', 'value')
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key')
testConnector.flushAllMessages()
t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key (remote)')
// check attributeRemoved
xml0.removeAttribute('key')
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute')
testConnector.flushAllMessages()
t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute (remote)')
xml0.insert(0, [new Y.XmlText('some text')])
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element')
testConnector.flushAllMessages()
t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on inserted element (remote)')
// test childRemoved
xml0.delete(0)
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element')
testConnector.flushAllMessages()
t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on deleted element (remote)')
compare(users)
}
/**
* @param {t.TestCase} _tc
*/
@@ -252,28 +212,31 @@ export const testElementAttributedContent = _tc => {
yelement.insert(1, [elem3])
yelement.setAttribute('key', '42')
})
const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
const expectedContent = delta.create('UNDEFINED').insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }).set('key', '42', { insert: [] })
const attributedContent = yelement.getContent(attributionManager)
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: [] } } })
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } })
t.group('test getContentDeep', () => {
const expectedContent = delta.create().insert(
[delta.text().insert('hello', null, { delete: [] })],
null,
{ delete: [] }
).insert([delta.create('span')])
const expectedContent = delta.create('UNDEFINED')
.insert(
[delta.text().insert('hello', null, { delete: [] })],
null,
{ delete: [] }
)
.insert([delta.create('span')])
.insert([
delta.text().insert('world', null, { insert: [] })
], null, { insert: [] })
.set('key', '42', { insert: [] })
.done()
const attributedContent = yelement.getContentDeep(attributionManager)
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
console.log('attributes', attributedContent.toJSON().attrs)
t.assert(attributedContent.equals(expectedContent))
t.compare(attributedContent, delta.map().set('key', '42', { insert: [] }))
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } })
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } })
t.assert(attributedContent.name === 'UNDEFINED')
})
})
@@ -296,28 +259,31 @@ export const testElementAttributedContentViaDiffer = _tc => {
yelement.setAttribute('key', '42')
})
const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc)
const expectedContent = delta.create().insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] })
const expectedContent = delta.create('UNDEFINED').insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] }).set('key', '42', { insert: [] })
const attributedContent = yelement.getContentDeep(attributionManager)
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: [] } } })
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } })
t.group('test getContentDeep', () => {
const expectedContent = delta.create().insert(
[delta.create().insert('hello')],
null,
{ delete: [] }
).insert([delta.create('span')])
const expectedContent = delta.create('UNDEFINED')
.insert(
[delta.create().insert('hello')],
null,
{ delete: [] }
)
.insert([delta.create('span')])
.insert([
delta.create().insert('world', null, { insert: [] })
], null, { insert: [] })
.set('key', '42', { insert: [] })
const attributedContent = yelement.getContentDeep(attributionManager)
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
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.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } })
t.assert(attributedContent.name === 'UNDEFINED')
})
ydoc.transact(() => {
@@ -325,34 +291,37 @@ export const testElementAttributedContentViaDiffer = _tc => {
})
t.group('test getContentDeep after some more updates', () => {
t.info('expecting diffingAttributionManager to auto update itself')
const expectedContent = delta.create().insert(
[delta.create().insert('hello')],
null,
{ delete: [] }
).insert([delta.create('span')])
const expectedContent = delta.create('UNDEFINED')
.insert(
[delta.create().insert('hello')],
null,
{ delete: [] }
)
.insert([delta.create('span')])
.insert([
delta.create().insert('bigworld', null, { insert: [] })
], null, { insert: [] })
.set('key', '42', { insert: [] })
const attributedContent = yelement.getContentDeep(attributionManager)
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
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.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } })
t.assert(attributedContent.name === 'UNDEFINED')
})
Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc))
t.group('test getContentDeep both docs synced', () => {
t.info('expecting diffingAttributionManager to auto update itself')
const expectedContent = delta.create().insert([delta.create('span')]).insert([
const expectedContent = delta.create('UNDEFINED').insert([delta.create('span')]).insert([
delta.create().insert('bigworld')
])
]).set('key', '42')
const attributedContent = yelement.getContentDeep(attributionManager)
console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2))
console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2))
console.log('attributes', attributedContent.toJSON().attrs)
t.assert(attributedContent.equals(expectedContent))
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42' } })
t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42' } })
t.assert(attributedContent.name === 'UNDEFINED')
})
}