first test case for attributions

This commit is contained in:
Kevin Jahns
2025-04-21 02:00:14 +02:00
parent 2e2968e71b
commit 6671071213
3 changed files with 86 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
import * as object from 'lib0/object'
import * as fun from 'lib0/function'
/**
* @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp
@@ -71,6 +72,46 @@ export class Delta {
this.ops = []
}
/**
* @param {Delta} d
* @return {boolean}
*/
equals (d) {
return this.ops.length === d.ops.length && this.ops.every((op, i) => {
const dop = d.ops[i]
if (op.constructor !== dop.constructor) return false
switch (op.constructor) {
case DeleteOp: {
if (/** @type {DeleteOp} */ (op).delete !== /** @type {DeleteOp} */ (dop).delete) {
return false
}
break
}
case InsertOp: {
if (
!fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert)
|| !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes)
|| !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution)
) {
return false
}
break
}
case RetainOp: {
if (
/** @type {RetainOp} */ (op).retain !== /** @type {RetainOp} */ (dop).retain
|| !fun.equalityDeep(/** @type {RetainOp} */ (op).attributes, /** @type {RetainOp} */ (dop).attributes)
|| !fun.equalityDeep(/** @type {RetainOp} */ (op).attribution, /** @type {RetainOp} */ (dop).attribution)
) {
return false
}
break
}
}
return true
})
}
toJSON () {
return { ops: this.ops.map(o => o.toJSON()) }
}

View File

@@ -267,6 +267,32 @@ export const mergeIdMaps = ams => {
return merged
}
/**
* @param {IdSet} idset
* @param {Array<Attribution<any>>} attrs
*/
export const createIdMapFromIdSet = (idset, attrs) => {
const idmap = createIdMap()
// map attrs to idmap
attrs = _ensureAttrs(idmap, attrs)
// filter out duplicates
/**
* @type {Array<Attribution<any>>}
*/
const checkedAttrs = []
attrs.forEach(attr => {
if (!idmapAttrsHas(checkedAttrs, attr)) {
checkedAttrs.push(attr)
}
})
idset.clients.forEach((ranges, client) => {
const attrRanges = new AttrRanges(ranges.getIds().map(range => new AttrRange(range.clock, range.len, checkedAttrs)))
attrRanges.sorted = true // is sorted because idset is sorted
idmap.clients.set(client, attrRanges)
})
return idmap
}
/**
* @template Attrs
*/

View File

@@ -2,6 +2,8 @@ import * as Y from './testHelper.js'
import * as t from 'lib0/testing'
import * as prng from 'lib0/prng'
import * as math from 'lib0/math'
import * as delta from '../src/utils/Delta.js'
import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager } from 'yjs/internals'
const { init, compare } = Y
@@ -2299,6 +2301,23 @@ export const testDeleteFormatting = _tc => {
t.compare(text2.toDelta(), expected)
}
/**
* @param {t.TestCase} tc
*/
export const testAttributedContent = tc => {
const ydoc = new Y.Doc()
const ytext = ydoc.getText()
ytext.insert(0, 'Hello World!')
let am = noAttributionsManager
ydoc.on('afterTransaction', tr => {
am = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, []))
})
ytext.applyDelta([{ retain: 6 }, { delete: 5 }, { insert: 'attributions' }])
const attributedContent = ytext.getContent(am)
t.assert(attributedContent.equals(delta.create().retain(6).insert('World', {}, { type: 'delete' }).insert('attributions', {}, { type: 'insert' })))
debugger
}
// RANDOM TESTS
let charCounter = 0