From bbaec17bf3056a86fb8268599a8d5dc17939c999 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 10 Jan 2026 13:28:41 +0100 Subject: [PATCH] [delta integration] fixed all tests --- src/utils/AttributionManager.js | 11 +++++------ src/utils/Transaction.js | 16 ---------------- src/ytype.js | 8 ++++---- tests/doc.tests.js | 14 ++++++++------ tests/undo-redo.tests.js | 23 +++++++++++------------ tests/y-map.tests.js | 2 +- tests/y-text.tests.js | 8 ++++---- tests/y-xml.tests.js | 12 +++++------- 8 files changed, 38 insertions(+), 56 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index b4cce013..c9fe9854 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -24,6 +24,7 @@ import { getItemCleanStart, intersectSets, ContentFormat, + createAttributionItem, StructStore, Transaction, ID, IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line encodeStateAsUpdate } from '../internals.js' @@ -574,16 +575,16 @@ export class SnapshotAttributionManager extends ObservableV2 { * @param {Object} [options] - options for the attribution manager * @param {Array>} [options.attrs] - the attributes to apply to the diff */ - constructor (prevSnapshot, nextSnapshot, { attrs = [] } = {}) { + constructor (prevSnapshot, nextSnapshot) { super() this.prevSnapshot = prevSnapshot this.nextSnapshot = nextSnapshot const inserts = createIdMap() - const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), attrs) + const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), [createAttributionItem('change', '')]) nextSnapshot.sv.forEach((clock, client) => { const prevClock = prevSnapshot.sv.get(client) || 0 inserts.add(client, 0, prevClock, []) // content is included in prevSnapshot is rendered without attributes - inserts.add(client, prevClock, clock - prevClock, attrs) // content is rendered as "inserted" + inserts.add(client, prevClock, clock - prevClock, [createAttributionItem('change', '')]) // content is rendered as "inserted" }) this.attrs = mergeIdMaps([diffIdMap(inserts, prevSnapshot.ds), deletes]) } @@ -635,7 +636,5 @@ export class SnapshotAttributionManager extends ObservableV2 { /** * @param {Snapshot} prevSnapshot * @param {Snapshot} nextSnapshot - * @param {Object} [options] - options for the attribution manager - * @param {Array>} [options.attrs] - the attributes to apply to the diff */ -export const createAttributionManagerFromSnapshots = (prevSnapshot, nextSnapshot = prevSnapshot, options) => new SnapshotAttributionManager(prevSnapshot, nextSnapshot, options) +export const createAttributionManagerFromSnapshots = (prevSnapshot, nextSnapshot = prevSnapshot) => new SnapshotAttributionManager(prevSnapshot, nextSnapshot) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 9b2e7b8a..d1a3a4e7 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -530,22 +530,6 @@ const cleanupTransactions = (transactionCleanups, i) => { // We need to think about the possibility that the user transforms the // Y.Doc in the event. if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) { - events = events - .filter(event => - event.target._item === null || !event.target._item.deleted - ) - events - .forEach(event => { - event.currentTarget = type - // path is relative to the current target - event._path = null - }) - // sort events by path length so that top-level events are fired first. - events - .sort((event1, event2) => event1.path.length - event2.path.length) - // We don't need to check for events.length - // because we know it has at least one element - /** * @type {YEvent} */ diff --git a/src/ytype.js b/src/ytype.js index ad216545..1c4391d0 100644 --- a/src/ytype.js +++ b/src/ytype.js @@ -101,8 +101,6 @@ export class ItemTextListPosition { * @function */ formatText (transaction, parent, length, attributes) { - const doc = transaction.doc - const ownClientId = doc.clientID minimizeAttributeChanges(this, attributes) const negatedAttributes = insertAttributes(transaction, parent, this, attributes) // iterate until first non-format or null is found @@ -1312,7 +1310,7 @@ export class YType { * @type {{[K:string]:any}} */ const attrs = this.getAttrs() - for (let k in attrs) { + for (const k in attrs) { const attr = attrs[k] attrs[k] = attr instanceof YType ? attr.toJSON() : attr } @@ -1343,7 +1341,7 @@ export class YType { */ const attrs = [] this.forEachAttr((attr, key) => { - attrs.push([(key), attr.toString({ forceTag: true })]) + attrs.push([(key), /** @type {any} */ (attr) instanceof YType ? attr.toString({ forceTag: true }) : JSON.stringify(attr)]) }) const attrsString = (attrs.length > 0 ? ' ' : '') + attrs.sort((a, b) => a[0].toString() < b[0].toString() ? -1 : 1).map(attr => attr[0] + '=' + attr[1]).join(' ') /** @@ -1561,6 +1559,8 @@ export const typeListGet = (type, index) => { } /** + * @todo this is a duplicate. use the unified insert function and remove this. + * * @param {Transaction} transaction * @param {YType} parent * @param {Item?} referenceItem diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 1d09765b..6b9eb7a6 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -112,14 +112,16 @@ export const testToJSON = _tc => { map2.setAttr('m2k1', 'm2v1') t.compare(doc.toJSON(), { array: { children: ['test1'] }, - map: { attrs: { - k1: 'v1', - k2: { - attrs: { - m2k1: 'm2v1' + map: { + attrs: { + k1: 'v1', + k2: { + attrs: { + m2k1: 'm2v1' + } } } - }} + } }, 'doc.toJSON has array and recursive map') } diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 39a9452a..fb88b9b3 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -262,7 +262,7 @@ export const testUndoArray = tc => { t.compare(array0.toJSON().children, [{}]) undoManager.stopCapturing() ymap.setAttr('a', 1) - t.compare(array0.toJSON().children, [{ a: 1 }]) + t.compare(array0.toJSON().children, [{ attrs: { a: 1 } }]) undoManager.undo() t.compare(array0.toJSON().children, [{}]) undoManager.undo() @@ -270,19 +270,19 @@ export const testUndoArray = tc => { undoManager.redo() t.compare(array0.toJSON().children, [{}]) undoManager.redo() - t.compare(array0.toJSON().children, [{ a: 1 }]) + t.compare(array0.toJSON().children, [{ attrs: { a: 1 } }]) testConnector.syncAll() array1.get(0).setAttr('b', 2) testConnector.syncAll() - t.compare(array0.toJSON().children, [{ a: 1, b: 2 }]) + t.compare(array0.toJSON().children, [{ attrs: { a: 1, b: 2 } }]) undoManager.undo() - t.compare(array0.toJSON().children, [{ b: 2 }]) + t.compare(array0.toJSON().children, [{ attrs: { b: 2 } }]) undoManager.undo() t.compare(array0.toJSON().children, [2, 3, 4, 5, 6]) undoManager.redo() - t.compare(array0.toJSON().children, [{ b: 2 }]) + t.compare(array0.toJSON().children, [{ attrs: { b: 2 } }]) undoManager.redo() - t.compare(array0.toJSON().children, [{ a: 1, b: 2 }]) + t.compare(array0.toJSON().children, [{ attrs: { a: 1, b: 2 } }]) } /** @@ -311,7 +311,6 @@ export const testUndoXml = tc => { t.compare(xml0.getContentDeep(), v1) } - /** * @param {t.TestCase} tc */ @@ -584,7 +583,7 @@ export const testUndoXmlBug = _tc => { undoManager.redo() undoManager.redo() undoManager.redo() - t.compare(fragment.toString(), '') + t.compare(fragment.toString(), '') } /** @@ -643,7 +642,7 @@ export const testUndoBlockBug = _tc => { undoManager.redo() // {"text":{"blocks":{"text":"2"}}} undoManager.redo() // {"text":{"blocks":{"text":"3"}}} undoManager.redo() // {"text":{}} - t.compare(design.toJSON().attrs, { text: { blocks: { text: '4' } } }) + t.compare(design.toJSON().attrs, { text: { attrs: { blocks: { attrs: { text: '4' } } } } }) } /** @@ -720,16 +719,16 @@ export const testSpecialDeletionCase = _tc => { e.setAttr('b', '2') fragment.insert(0, [e]) }) - t.compareStrings(fragment.toString(), '') + t.compareStrings(fragment.toString(), '') doc.transact(() => { // change attribute "b" and delete test-node const e = fragment.get(0) - e.setAttribute('b', '3') + e.setAttr('b', '3') fragment.delete(0) }, origin) t.compareStrings(fragment.toString(), '') undoManager.undo() - t.compareStrings(fragment.toString(), '') + t.compareStrings(fragment.toString(), '') } /** diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 412a6659..111ba5f1 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -203,7 +203,7 @@ export const testYmapSetsYarray = tc => { t.assert(array === map0.getAttr('Array')) array.insert(0, [1, 2, 3]) // @ts-ignore - t.compare(map0.toJSON(), { Array: [1, 2, 3] }) + t.compare(map0.toJSON().attrs, { Array: { children: [1, 2, 3] } }) compare(users) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index b4569ad9..dac37516 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2096,7 +2096,7 @@ const qChanges = [ */ (y, gen) => { // delete text const ytext = y.get('text') - const contentLen = ytext.toString().length + const contentLen = ytext.length const insertPos = prng.int32(gen, 0, contentLen) const overwrite = math.min(prng.int32(gen, 0, contentLen - insertPos), 2) ytext.delete(insertPos, overwrite) @@ -2107,7 +2107,7 @@ const qChanges = [ */ (y, gen) => { // format text const ytext = y.get('text') - const contentLen = ytext.toString().length + const contentLen = ytext.length const insertPos = prng.int32(gen, 0, contentLen) const overwrite = math.min(prng.int32(gen, 0, contentLen - insertPos), 2) const format = prng.oneOf(gen, marks) @@ -2119,7 +2119,7 @@ const qChanges = [ */ (y, gen) => { // insert codeblock const ytext = y.get('text') - const insertPos = prng.int32(gen, 0, ytext.toString().length) + const insertPos = prng.int32(gen, 0, ytext.length) const text = charCounter++ + prng.word(gen) const d = delta.create() d.retain(insertPos).insert(text).insert('\n', { 'code-block': true }) @@ -2131,7 +2131,7 @@ const qChanges = [ */ (y, gen) => { // complex delta op const ytext = y.get('text') - const contentLen = ytext.toString().length + const contentLen = ytext.length let currentPos = math.max(0, prng.int32(gen, 0, contentLen - 1)) const d = delta.create().retain(currentPos) // create max 3 ops diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 1347f9ea..021720c1 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -94,14 +94,12 @@ export const testInsertafter = _tc => { const first = new Y.Type() const second = new Y.Type('p') const third = new Y.Type('p') - const deepsecond1 = new Y.Type('span') const deepsecond2 = new Y.Type() - second.insertAfter(null, [deepsecond1]) - second.insertAfter(deepsecond1, [deepsecond2]) - yxml.insertAfter(null, [first, second]) yxml.insertAfter(second, [third]) + second.insertAfter(null, [deepsecond1]) + second.insertAfter(deepsecond1, [deepsecond2]) t.assert(yxml.length === 3) t.assert(second.get(0) === deepsecond1) @@ -164,9 +162,9 @@ export const testElement = _tc => { export const testFragmentAttributedContent = _tc => { const ydoc = new Y.Doc({ gc: false }) const yfragment = new Y.Type() - const elem1 = new Y.Type('hello') + const elem1 = Y.Type.from(delta.create().insert('hello')) const elem2 = new Y.Type() - const elem3 = new Y.Type('world') + const elem3 = Y.Type.from(delta.create().insert('world')) yfragment.insert(0, [elem1, elem2]) ydoc.get().insert(0, [yfragment]) let attributionManager = Y.noAttributionsManager @@ -230,7 +228,7 @@ export const testElementAttributedContentViaDiffer = _tc => { Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocV1)) const yelement = ydoc.get('p') const elem2 = yelement.get(1) // new Y.XmlElement('span') - const elem3 = new Y.Type('world') + const elem3 = Y.Type.from(delta.create().insert('world')) ydoc.transact(() => { yelement.delete(0, 1) yelement.insert(1, [elem3])