fix all tests
This commit is contained in:
parent
f94653424a
commit
52abcdd043
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { createMutex } from '../../lib/mutex.js'
|
import { createMutex } from '../lib/mutex.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for bindings.
|
* Abstract class for bindings.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global MutationObserver, getSelection */
|
/* global MutationObserver, getSelection */
|
||||||
|
|
||||||
import { fromRelativePosition } from '../../Util/relativePosition.js'
|
import { fromRelativePosition } from '../../src/Util/relativePosition.js'
|
||||||
import Binding from '../Binding.js'
|
import Binding from '../Binding.js'
|
||||||
import { createAssociation, removeAssociation } from './util.js'
|
import { createAssociation, removeAssociation } from './util.js'
|
||||||
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer, getCurrentRelativeSelection } from './selection.js'
|
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer, getCurrentRelativeSelection } from './selection.js'
|
||||||
@ -121,8 +121,15 @@ export default class DomBinding extends Binding {
|
|||||||
createAssociation(this, target, type)
|
createAssociation(this, target, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flushDomChanges () {
|
||||||
|
this._domObserver(this._mutationObserver.takeRecords())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: currently does not apply filter to existing elements!
|
* NOTE:
|
||||||
|
* * does not apply filter to existing elements!
|
||||||
|
* * only guarantees that changes are filtered locally. Remote sites may see different content.
|
||||||
|
*
|
||||||
* @param {DomFilter} filter The filter function to use from now on.
|
* @param {DomFilter} filter The filter function to use from now on.
|
||||||
*/
|
*/
|
||||||
setFilter (filter) {
|
setFilter (filter) {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
import YXmlHook from '../../src/Types/YXml/YXmlHook.js'
|
||||||
import {
|
import {
|
||||||
iterateUntilUndeleted,
|
iterateUntilUndeleted,
|
||||||
removeAssociation,
|
removeAssociation,
|
||||||
insertNodeHelper } from './util.js'
|
insertNodeHelper } from './util.js'
|
||||||
import diff from '../../../lib/simpleDiff.js'
|
import diff from '../../lib/simpleDiff.js'
|
||||||
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
|
import YXmlFragment from '../../src/Types/YXml/YXmlFragment.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Check if any of the nodes was deleted
|
* 1. Check if any of the nodes was deleted
|
||||||
|
@ -47,10 +47,12 @@ export function splitHelper (y, a, b, diff) {
|
|||||||
o = o._right
|
o = o._right
|
||||||
}
|
}
|
||||||
y.os.put(b)
|
y.os.put(b)
|
||||||
if (y._transaction.newTypes.has(a)) {
|
if (y._transaction !== null) {
|
||||||
y._transaction.newTypes.add(b)
|
if (y._transaction.newTypes.has(a)) {
|
||||||
} else if (y._transaction.deletedStructs.has(a)) {
|
y._transaction.newTypes.add(b)
|
||||||
y._transaction.deletedStructs.add(b)
|
} else if (y._transaction.deletedStructs.has(a)) {
|
||||||
|
y._transaction.deletedStructs.add(b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +119,7 @@ export default class Item {
|
|||||||
*/
|
*/
|
||||||
_copy () {
|
_copy () {
|
||||||
const C = this.constructor
|
const C = this.constructor
|
||||||
return C()
|
return new C()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,7 @@ export const stringifyDeleteSet = (decoder) => {
|
|||||||
const dsLength = decoding.readUint32(decoder)
|
const dsLength = decoding.readUint32(decoder)
|
||||||
for (let i = 0; i < dsLength; i++) {
|
for (let i = 0; i < dsLength; i++) {
|
||||||
str += ' -' + decoding.readVarUint(decoder) + ':\n' // decodes user
|
str += ' -' + decoding.readVarUint(decoder) + ':\n' // decodes user
|
||||||
const dvLength = decoding.readVarUint(decoder)
|
const dvLength = decoding.readUint32(decoder)
|
||||||
for (let j = 0; j < dvLength; j++) {
|
for (let j = 0; j < dvLength; j++) {
|
||||||
str += `clock: ${decoding.readVarUint(decoder)}, length: ${decoding.readVarUint(decoder)}, gc: ${decoding.readUint8(decoder) === 1}\n`
|
str += `clock: ${decoding.readVarUint(decoder)}, length: ${decoding.readVarUint(decoder)}, gc: ${decoding.readUint8(decoder) === 1}\n`
|
||||||
}
|
}
|
||||||
@ -192,8 +192,8 @@ export const readDeleteSet = (decoder, y) => {
|
|||||||
*/
|
*/
|
||||||
export const stringifyStateSet = decoder => {
|
export const stringifyStateSet = decoder => {
|
||||||
let s = 'State Set: '
|
let s = 'State Set: '
|
||||||
readStateSet(decoder).forEach((user, userState) => {
|
readStateSet(decoder).forEach((clock, user) => {
|
||||||
s += `(${user}: ${userState}), `
|
s += `(${user}: ${clock}), `
|
||||||
})
|
})
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ export const writeStateSet = (encoder, y) => {
|
|||||||
// write as fixed-size number to stay consistent with the other encode functions.
|
// write as fixed-size number to stay consistent with the other encode functions.
|
||||||
// => anytime we write the number of objects that follow, encode as fixed-size number.
|
// => anytime we write the number of objects that follow, encode as fixed-size number.
|
||||||
encoding.writeUint32(encoder, state.size)
|
encoding.writeUint32(encoder, state.size)
|
||||||
state.forEach((user, clock) => {
|
state.forEach((clock, user) => {
|
||||||
encoding.writeVarUint(encoder, user)
|
encoding.writeVarUint(encoder, user)
|
||||||
encoding.writeVarUint(encoder, clock)
|
encoding.writeVarUint(encoder, clock)
|
||||||
})
|
})
|
||||||
@ -317,7 +317,9 @@ export const writeStructs = (encoder, y, ss) => {
|
|||||||
const overlappingLeft = y.os.findPrev(minBound)
|
const overlappingLeft = y.os.findPrev(minBound)
|
||||||
const rightID = overlappingLeft === null ? null : overlappingLeft._id
|
const rightID = overlappingLeft === null ? null : overlappingLeft._id
|
||||||
if (rightID !== null && rightID.user === user && rightID.clock + overlappingLeft._length > clock) {
|
if (rightID !== null && rightID.user === user && rightID.clock + overlappingLeft._length > clock) {
|
||||||
const struct = overlappingLeft._clonePartial(clock - rightID.clock)
|
// TODO: only write partial content (only missing content)
|
||||||
|
// const struct = overlappingLeft._clonePartial(clock - rightID.clock)
|
||||||
|
const struct = overlappingLeft
|
||||||
struct._toBinary(encoder)
|
struct._toBinary(encoder)
|
||||||
len++
|
len++
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ function testEncoding (t, write, read, val) {
|
|||||||
t.compare(val, result, 'Compare results')
|
t.compare(val, result, 'Compare results')
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeVarUint = (encoder, val) => encoder.writeVarUint(val)
|
const writeVarUint = (encoder, val) => encoding.writeVarUint(encoder, val)
|
||||||
const readVarUint = decoder => decoder.readVarUint()
|
const readVarUint = decoder => decoding.readVarUint(decoder)
|
||||||
|
|
||||||
test('varUint 1 byte', async function varUint1 (t) {
|
test('varUint 1 byte', async function varUint1 (t) {
|
||||||
testEncoding(t, writeVarUint, readVarUint, 42)
|
testEncoding(t, writeVarUint, readVarUint, 42)
|
||||||
@ -46,8 +46,8 @@ test('varUint random user id', async function varUintRandomUserId (t) {
|
|||||||
testEncoding(t, writeVarUint, readVarUint, generateRandomUint32())
|
testEncoding(t, writeVarUint, readVarUint, generateRandomUint32())
|
||||||
})
|
})
|
||||||
|
|
||||||
const writeVarString = (encoder, val) => encoder.writeVarString(val)
|
const writeVarString = (encoder, val) => encoding.writeVarString(encoder, val)
|
||||||
const readVarString = decoder => decoder.readVarString()
|
const readVarString = decoder => decoding.readVarString(decoder)
|
||||||
|
|
||||||
test('varString', async function varString (t) {
|
test('varString', async function varString (t) {
|
||||||
testEncoding(t, writeVarString, readVarString, 'hello')
|
testEncoding(t, writeVarString, readVarString, 'hello')
|
||||||
|
@ -65,6 +65,7 @@ test('insertions work in late sync', async function array4 (t) {
|
|||||||
array2.insert(1, ['user2'])
|
array2.insert(1, ['user2'])
|
||||||
await users[1].connect()
|
await users[1].connect()
|
||||||
await users[2].connect()
|
await users[2].connect()
|
||||||
|
testConnector.flushAllMessages()
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ function getUniqueNumber () {
|
|||||||
|
|
||||||
var arrayTransactions = [
|
var arrayTransactions = [
|
||||||
function insert (t, user, prng) {
|
function insert (t, user, prng) {
|
||||||
const yarray = user.get('array', Y.Array)
|
const yarray = user.define('array', Y.Array)
|
||||||
var uniqueNumber = getUniqueNumber()
|
var uniqueNumber = getUniqueNumber()
|
||||||
var content = []
|
var content = []
|
||||||
var len = random.int32(prng, 1, 4)
|
var len = random.int32(prng, 1, 4)
|
||||||
@ -226,14 +227,14 @@ var arrayTransactions = [
|
|||||||
yarray.insert(pos, content)
|
yarray.insert(pos, content)
|
||||||
},
|
},
|
||||||
function insertTypeArray (t, user, prng) {
|
function insertTypeArray (t, user, prng) {
|
||||||
const yarray = user.get('array', Y.Array)
|
const yarray = user.define('array', Y.Array)
|
||||||
var pos = random.int32(prng, 0, yarray.length)
|
var pos = random.int32(prng, 0, yarray.length)
|
||||||
yarray.insert(pos, [Y.Array])
|
yarray.insert(pos, [Y.Array])
|
||||||
var array2 = yarray.get(pos)
|
var array2 = yarray.get(pos)
|
||||||
array2.insert(0, [1, 2, 3, 4])
|
array2.insert(0, [1, 2, 3, 4])
|
||||||
},
|
},
|
||||||
function insertTypeMap (t, user, prng) {
|
function insertTypeMap (t, user, prng) {
|
||||||
const yarray = user.get('array', Y.Array)
|
const yarray = user.define('array', Y.Array)
|
||||||
var pos = random.int32(prng, 0, yarray.length)
|
var pos = random.int32(prng, 0, yarray.length)
|
||||||
yarray.insert(pos, [Y.Map])
|
yarray.insert(pos, [Y.Map])
|
||||||
var map = yarray.get(pos)
|
var map = yarray.get(pos)
|
||||||
@ -242,7 +243,7 @@ var arrayTransactions = [
|
|||||||
map.set('someprop', 44)
|
map.set('someprop', 44)
|
||||||
},
|
},
|
||||||
function _delete (t, user, prng) {
|
function _delete (t, user, prng) {
|
||||||
const yarray = user.get('array', Y.Array)
|
const yarray = user.define('array', Y.Array)
|
||||||
var length = yarray.length
|
var length = yarray.length
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
var somePos = random.int32(prng, 0, length - 1)
|
var somePos = random.int32(prng, 0, length - 1)
|
||||||
|
@ -50,7 +50,7 @@ test('Basic get&set of Map property (converge via sync)', async function map1 (t
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.compare(u.get('stuff'), 'stuffy')
|
t.compare(u.get('stuff'), 'stuffy')
|
||||||
t.assert(u.get('undefined') === undefined, 'undefined')
|
t.assert(u.get('undefined') === undefined, 'undefined')
|
||||||
t.compare(u.get('null'), null, 'null')
|
t.compare(u.get('null'), null, 'null')
|
||||||
@ -94,7 +94,7 @@ test('Basic get&set of Map property (converge via update)', async function map5
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.compare(u.get('stuff'), 'stuffy')
|
t.compare(u.get('stuff'), 'stuffy')
|
||||||
}
|
}
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -108,7 +108,7 @@ test('Basic get&set of Map property (handle conflict)', async function map6 (t)
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.compare(u.get('stuff'), 'c0')
|
t.compare(u.get('stuff'), 'c0')
|
||||||
}
|
}
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -121,7 +121,7 @@ test('Basic get&set&delete of Map property (handle conflict)', async function ma
|
|||||||
map1.set('stuff', 'c1')
|
map1.set('stuff', 'c1')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.assert(u.get('stuff') === undefined)
|
t.assert(u.get('stuff') === undefined)
|
||||||
}
|
}
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -135,7 +135,7 @@ test('Basic get&set of Map property (handle three conflicts)', async function ma
|
|||||||
map2.set('stuff', 'c3')
|
map2.set('stuff', 'c3')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.compare(u.get('stuff'), 'c0')
|
t.compare(u.get('stuff'), 'c0')
|
||||||
}
|
}
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -155,7 +155,7 @@ test('Basic get&set&delete of Map property (handle three conflicts)', async func
|
|||||||
map3.set('stuff', 'c3')
|
map3.set('stuff', 'c3')
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
var u = user.get('map', Y.Map)
|
var u = user.define('map', Y.Map)
|
||||||
t.assert(u.get('stuff') === undefined)
|
t.assert(u.get('stuff') === undefined)
|
||||||
}
|
}
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -303,12 +303,12 @@ var mapTransactions = [
|
|||||||
function set (t, user, prng) {
|
function set (t, user, prng) {
|
||||||
let key = random.oneOf(prng, ['one', 'two'])
|
let key = random.oneOf(prng, ['one', 'two'])
|
||||||
var value = random.utf16String(prng)
|
var value = random.utf16String(prng)
|
||||||
user.get('map', Y.Map).set(key, value)
|
user.define('map', Y.Map).set(key, value)
|
||||||
},
|
},
|
||||||
function setType (t, user, prng) {
|
function setType (t, user, prng) {
|
||||||
let key = random.oneOf(prng, ['one', 'two'])
|
let key = random.oneOf(prng, ['one', 'two'])
|
||||||
var type = random.oneOf(prng, [new Y.Array(), new Y.Map()])
|
var type = random.oneOf(prng, [new Y.Array(), new Y.Map()])
|
||||||
user.get('map', Y.Map).set(key, type)
|
user.define('map', Y.Map).set(key, type)
|
||||||
if (type instanceof Y.Array) {
|
if (type instanceof Y.Array) {
|
||||||
type.insert(0, [1, 2, 3, 4])
|
type.insert(0, [1, 2, 3, 4])
|
||||||
} else {
|
} else {
|
||||||
@ -317,7 +317,7 @@ var mapTransactions = [
|
|||||||
},
|
},
|
||||||
function _delete (t, user, prng) {
|
function _delete (t, user, prng) {
|
||||||
let key = random.oneOf(prng, ['one', 'two'])
|
let key = random.oneOf(prng, ['one', 'two'])
|
||||||
user.get('map', Y.Map).delete(key)
|
user.define('map', Y.Map).delete(key)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -61,20 +61,24 @@ test('attribute modifications (y -> dom)', async function xml2 (t) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('attribute modifications (dom -> y)', async function xml3 (t) {
|
test('attribute modifications (dom -> y)', async function xml3 (t) {
|
||||||
var { users, xml0, dom0 } = await initArrays(t, { users: 3 })
|
var { users, xml0, dom0, domBinding0 } = await initArrays(t, { users: 3 })
|
||||||
dom0.setAttribute('height', '100px')
|
dom0.setAttribute('height', '100px')
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.getAttribute('height') === '100px', 'setAttribute')
|
t.assert(xml0.getAttribute('height') === '100px', 'setAttribute')
|
||||||
dom0.removeAttribute('height')
|
dom0.removeAttribute('height')
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.getAttribute('height') == null, 'removeAttribute')
|
t.assert(xml0.getAttribute('height') == null, 'removeAttribute')
|
||||||
dom0.setAttribute('class', 'stuffy stuff')
|
dom0.setAttribute('class', 'stuffy stuff')
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
|
t.assert(xml0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('element insert (dom -> y)', async function xml4 (t) {
|
test('element insert (dom -> y)', async function xml4 (t) {
|
||||||
var { users, xml0, dom0 } = await initArrays(t, { users: 3 })
|
var { users, xml0, dom0, domBinding0 } = await initArrays(t, { users: 3 })
|
||||||
dom0.insertBefore(document.createTextNode('some text'), null)
|
dom0.insertBefore(document.createTextNode('some text'), null)
|
||||||
dom0.insertBefore(document.createElement('p'), null)
|
dom0.insertBefore(document.createElement('p'), null)
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.get(0).toString() === 'some text', 'Retrieve Text Node')
|
t.assert(xml0.get(0).toString() === 'some text', 'Retrieve Text Node')
|
||||||
t.assert(xml0.get(1).nodeName === 'P', 'Retrieve Element node')
|
t.assert(xml0.get(1).nodeName === 'P', 'Retrieve Element node')
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -90,10 +94,12 @@ test('element insert (y -> dom)', async function xml5 (t) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('y on insert, then delete (dom -> y)', async function xml6 (t) {
|
test('y on insert, then delete (dom -> y)', async function xml6 (t) {
|
||||||
var { users, xml0, dom0 } = await initArrays(t, { users: 3 })
|
var { users, xml0, dom0, domBinding0 } = await initArrays(t, { users: 3 })
|
||||||
dom0.insertBefore(document.createElement('p'), null)
|
dom0.insertBefore(document.createElement('p'), null)
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.length === 1, 'one node present')
|
t.assert(xml0.length === 1, 'one node present')
|
||||||
dom0.childNodes[0].remove()
|
dom0.childNodes[0].remove()
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
t.assert(xml0.length === 0, 'no node present after delete')
|
t.assert(xml0.length === 0, 'no node present after delete')
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
})
|
})
|
||||||
@ -164,11 +170,13 @@ test('Receive a bunch of elements (with disconnect)', async function xml12 (t) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('move element to a different position', async function xml13 (t) {
|
test('move element to a different position', async function xml13 (t) {
|
||||||
var { testConnector, users, dom0, dom1 } = await initArrays(t, { users: 3 })
|
var { testConnector, users, dom0, dom1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 })
|
||||||
dom0.append(document.createElement('div'))
|
dom0.append(document.createElement('div'))
|
||||||
dom0.append(document.createElement('h1'))
|
dom0.append(document.createElement('h1'))
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
dom1.insertBefore(dom1.childNodes[0], null)
|
dom1.insertBefore(dom1.childNodes[0], null)
|
||||||
|
domBinding1.flushDomChanges()
|
||||||
t.assert(dom1.childNodes[0].nodeName === 'H1', 'div was deleted (user 0)')
|
t.assert(dom1.childNodes[0].nodeName === 'H1', 'div was deleted (user 0)')
|
||||||
t.assert(dom1.childNodes[1].nodeName === 'DIV', 'div was moved to the correct position (user 0)')
|
t.assert(dom1.childNodes[1].nodeName === 'DIV', 'div was moved to the correct position (user 0)')
|
||||||
t.assert(dom1.childNodes[0].nodeName === 'H1', 'div was deleted (user 1)')
|
t.assert(dom1.childNodes[0].nodeName === 'H1', 'div was deleted (user 1)')
|
||||||
@ -189,6 +197,7 @@ test('filter node', async function xml14 (t) {
|
|||||||
domBinding1.setFilter(domFilter)
|
domBinding1.setFilter(domFilter)
|
||||||
dom0.append(document.createElement('div'))
|
dom0.append(document.createElement('div'))
|
||||||
dom0.append(document.createElement('h1'))
|
dom0.append(document.createElement('h1'))
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
t.assert(dom1.childNodes.length === 1, 'Only one node was not transmitted')
|
t.assert(dom1.childNodes.length === 1, 'Only one node was not transmitted')
|
||||||
t.assert(dom1.childNodes[0].nodeName === 'DIV', 'div node was transmitted')
|
t.assert(dom1.childNodes[0].nodeName === 'DIV', 'div node was transmitted')
|
||||||
@ -206,6 +215,7 @@ test('filter attribute', async function xml15 (t) {
|
|||||||
dom0.setAttribute('hidden', 'true')
|
dom0.setAttribute('hidden', 'true')
|
||||||
dom0.setAttribute('style', 'height: 30px')
|
dom0.setAttribute('style', 'height: 30px')
|
||||||
dom0.setAttribute('data-me', '77')
|
dom0.setAttribute('data-me', '77')
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
t.assert(dom0.getAttribute('hidden') === 'true', 'User 0 still has the attribute')
|
t.assert(dom0.getAttribute('hidden') === 'true', 'User 0 still has the attribute')
|
||||||
t.assert(dom1.getAttribute('hidden') == null, 'User 1 did not receive update')
|
t.assert(dom1.getAttribute('hidden') == null, 'User 1 did not receive update')
|
||||||
@ -215,7 +225,7 @@ test('filter attribute', async function xml15 (t) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('deep element insert', async function xml16 (t) {
|
test('deep element insert', async function xml16 (t) {
|
||||||
var { testConnector, users, dom0, dom1 } = await initArrays(t, { users: 3 })
|
var { testConnector, users, dom0, dom1, domBinding0 } = await initArrays(t, { users: 3 })
|
||||||
let deepElement = document.createElement('p')
|
let deepElement = document.createElement('p')
|
||||||
let boldElement = document.createElement('b')
|
let boldElement = document.createElement('b')
|
||||||
let attrElement = document.createElement('img')
|
let attrElement = document.createElement('img')
|
||||||
@ -225,6 +235,7 @@ test('deep element insert', async function xml16 (t) {
|
|||||||
deepElement.append(attrElement)
|
deepElement.append(attrElement)
|
||||||
dom0.append(deepElement)
|
dom0.append(deepElement)
|
||||||
let str0 = dom0.outerHTML
|
let str0 = dom0.outerHTML
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
let str1 = dom1.outerHTML
|
let str1 = dom1.outerHTML
|
||||||
t.compare(str0, str1, 'Dom string representation matches')
|
t.compare(str0, str1, 'Dom string representation matches')
|
||||||
@ -253,8 +264,8 @@ test('treeWalker', async function xml17 (t) {
|
|||||||
* Incoming changes that contain malicious attributes should be deleted.
|
* Incoming changes that contain malicious attributes should be deleted.
|
||||||
*/
|
*/
|
||||||
test('Filtering remote changes', async function xmlFilteringRemote (t) {
|
test('Filtering remote changes', async function xmlFilteringRemote (t) {
|
||||||
var { testConnector, users, xml0, xml1, domBinding0 } = await initArrays(t, { users: 3 })
|
var { testConnector, users, xml0, xml1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 })
|
||||||
domBinding0.setFilter(function (nodeName, attributes) {
|
const filter = (nodeName, attributes) => {
|
||||||
attributes.delete('malicious')
|
attributes.delete('malicious')
|
||||||
if (nodeName === 'HIDEME') {
|
if (nodeName === 'HIDEME') {
|
||||||
return null
|
return null
|
||||||
@ -263,7 +274,9 @@ test('Filtering remote changes', async function xmlFilteringRemote (t) {
|
|||||||
} else {
|
} else {
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
domBinding0.setFilter(filter)
|
||||||
|
domBinding1.setFilter(filter)
|
||||||
let paragraph = new Y.XmlElement('p')
|
let paragraph = new Y.XmlElement('p')
|
||||||
let hideMe = new Y.XmlElement('hideMe')
|
let hideMe = new Y.XmlElement('hideMe')
|
||||||
let span = new Y.XmlElement('span')
|
let span = new Y.XmlElement('span')
|
||||||
@ -275,13 +288,16 @@ test('Filtering remote changes', async function xmlFilteringRemote (t) {
|
|||||||
let tag2 = new Y.XmlElement('tag')
|
let tag2 = new Y.XmlElement('tag')
|
||||||
tag2.setAttribute('isHidden', 'true')
|
tag2.setAttribute('isHidden', 'true')
|
||||||
paragraph.insert(0, [tag2])
|
paragraph.insert(0, [tag2])
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
// check dom
|
// check dom
|
||||||
domBinding0.typeToDom.get(paragraph).setAttribute('malicious', 'true')
|
domBinding0.typeToDom.get(paragraph).setAttribute('malicious', 'true')
|
||||||
domBinding0.typeToDom.get(span).setAttribute('malicious', 'true')
|
domBinding0.typeToDom.get(span).setAttribute('malicious', 'true')
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
// check incoming attributes
|
// check incoming attributes
|
||||||
xml1.get(0).get(0).setAttribute('malicious', 'true')
|
xml1.get(0).get(0).setAttribute('malicious', 'true')
|
||||||
xml1.insert(0, [new Y.XmlElement('hideMe')])
|
xml1.insert(0, [new Y.XmlElement('hideMe')])
|
||||||
|
domBinding0.flushDomChanges()
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
await compareUsers(t, users)
|
await compareUsers(t, users)
|
||||||
@ -292,30 +308,36 @@ var xmlTransactions = [
|
|||||||
function attributeChange (t, user, prng) {
|
function attributeChange (t, user, prng) {
|
||||||
// random.word generates non-empty words. prepend something
|
// random.word generates non-empty words. prepend something
|
||||||
user.dom.setAttribute('_' + random.word(prng), random.word(prng))
|
user.dom.setAttribute('_' + random.word(prng), random.word(prng))
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
},
|
},
|
||||||
function attributeChangeHidden (t, user, prng) {
|
function attributeChangeHidden (t, user, prng) {
|
||||||
user.dom.setAttribute('hidden', random.word(prng))
|
user.dom.setAttribute('hidden', random.word(prng))
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
},
|
},
|
||||||
function insertText (t, user, prng) {
|
function insertText (t, user, prng) {
|
||||||
let dom = user.dom
|
let dom = user.dom
|
||||||
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
||||||
dom.insertBefore(document.createTextNode(random.word(prng)), succ)
|
dom.insertBefore(document.createTextNode(random.word(prng)), succ)
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
},
|
},
|
||||||
function insertHiddenDom (t, user, prng) {
|
function insertHiddenDom (t, user, prng) {
|
||||||
let dom = user.dom
|
let dom = user.dom
|
||||||
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
||||||
dom.insertBefore(document.createElement('hidden'), succ)
|
dom.insertBefore(document.createElement('hidden'), succ)
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
},
|
},
|
||||||
function insertDom (t, user, prng) {
|
function insertDom (t, user, prng) {
|
||||||
let dom = user.dom
|
let dom = user.dom
|
||||||
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
var succ = dom.children.length > 0 ? random.oneOf(prng, dom.children) : null
|
||||||
dom.insertBefore(document.createElement('my-' + random.word(prng)), succ)
|
dom.insertBefore(document.createElement('my-' + random.word(prng)), succ)
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
},
|
},
|
||||||
function deleteChild (t, user, prng) {
|
function deleteChild (t, user, prng) {
|
||||||
let dom = user.dom
|
let dom = user.dom
|
||||||
if (dom.childNodes.length > 0) {
|
if (dom.childNodes.length > 0) {
|
||||||
var d = random.oneOf(prng, dom.childNodes)
|
var d = random.oneOf(prng, dom.childNodes)
|
||||||
d.remove()
|
d.remove()
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function insertTextSecondLayer (t, user, prng) {
|
function insertTextSecondLayer (t, user, prng) {
|
||||||
@ -324,6 +346,7 @@ var xmlTransactions = [
|
|||||||
let dom2 = random.oneOf(prng, dom.children)
|
let dom2 = random.oneOf(prng, dom.children)
|
||||||
let succ = dom2.childNodes.length > 0 ? random.oneOf(prng, dom2.childNodes) : null
|
let succ = dom2.childNodes.length > 0 ? random.oneOf(prng, dom2.childNodes) : null
|
||||||
dom2.insertBefore(document.createTextNode(random.word(prng)), succ)
|
dom2.insertBefore(document.createTextNode(random.word(prng)), succ)
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function insertDomSecondLayer (t, user, prng) {
|
function insertDomSecondLayer (t, user, prng) {
|
||||||
@ -332,6 +355,7 @@ var xmlTransactions = [
|
|||||||
let dom2 = random.oneOf(prng, dom.children)
|
let dom2 = random.oneOf(prng, dom.children)
|
||||||
let succ = dom2.childNodes.length > 0 ? random.oneOf(prng, dom2.childNodes) : null
|
let succ = dom2.childNodes.length > 0 ? random.oneOf(prng, dom2.childNodes) : null
|
||||||
dom2.insertBefore(document.createElement('my-' + random.word(prng)), succ)
|
dom2.insertBefore(document.createElement('my-' + random.word(prng)), succ)
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function deleteChildSecondLayer (t, user, prng) {
|
function deleteChildSecondLayer (t, user, prng) {
|
||||||
@ -342,6 +366,7 @@ var xmlTransactions = [
|
|||||||
let d = random.oneOf(prng, dom2.childNodes)
|
let d = random.oneOf(prng, dom2.childNodes)
|
||||||
d.remove()
|
d.remove()
|
||||||
}
|
}
|
||||||
|
user.domBinding.flushDomChanges()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -6,10 +6,12 @@ import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
|||||||
import Quill from 'quill'
|
import Quill from 'quill'
|
||||||
import GC from '../src/Struct/GC.js'
|
import GC from '../src/Struct/GC.js'
|
||||||
import * as random from '../lib/random/random.js'
|
import * as random from '../lib/random/random.js'
|
||||||
import * as message from '../src/message.js'
|
import * as syncProtocol from '../src/protocols/syncProtocol.js'
|
||||||
import * as encoding from '../lib/encoding.js'
|
import * as encoding from '../lib/encoding.js'
|
||||||
import * as decoding from '../lib/decoding.js'
|
import * as decoding from '../lib/decoding.js'
|
||||||
import { createMutex } from '../lib/mutex.js'
|
import { createMutex } from '../lib/mutex.js'
|
||||||
|
import QuillBinding from '../bindings/QuillBinding/QuillBinding.js'
|
||||||
|
import DomBinding from '../bindings/DomBinding/DomBinding.js'
|
||||||
|
|
||||||
export * from '../src/index.js'
|
export * from '../src/index.js'
|
||||||
|
|
||||||
@ -21,7 +23,7 @@ const afterTransaction = (y, transaction) => {
|
|||||||
y.mMux(() => {
|
y.mMux(() => {
|
||||||
if (transaction.encodedStructsLen > 0) {
|
if (transaction.encodedStructsLen > 0) {
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = encoding.createEncoder()
|
||||||
message.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
syncProtocol.writeUpdate(encoder, transaction.encodedStructsLen, transaction.encodedStructs)
|
||||||
broadcastMessage(y, encoding.toBuffer(encoder))
|
broadcastMessage(y, encoding.toBuffer(encoder))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -31,8 +33,9 @@ export class TestYInstance extends Y.Y {
|
|||||||
/**
|
/**
|
||||||
* @param {TestConnector} testConnector
|
* @param {TestConnector} testConnector
|
||||||
*/
|
*/
|
||||||
constructor (testConnector) {
|
constructor (testConnector, clientID) {
|
||||||
super()
|
super()
|
||||||
|
this.userID = clientID // overwriting clientID
|
||||||
/**
|
/**
|
||||||
* @type {TestConnector}
|
* @type {TestConnector}
|
||||||
*/
|
*/
|
||||||
@ -64,17 +67,19 @@ export class TestYInstance extends Y.Y {
|
|||||||
*/
|
*/
|
||||||
connect () {
|
connect () {
|
||||||
if (!this.tc.onlineConns.has(this)) {
|
if (!this.tc.onlineConns.has(this)) {
|
||||||
|
this.tc.onlineConns.add(this)
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = encoding.createEncoder()
|
||||||
message.writeSyncStep1(encoder, this)
|
syncProtocol.writeSyncStep1(encoder, this)
|
||||||
// publish SyncStep1
|
// publish SyncStep1
|
||||||
broadcastMessage(this, encoding.toBuffer(encoder))
|
broadcastMessage(this, encoding.toBuffer(encoder))
|
||||||
this.tc.onlineConns.forEach(remoteYInstance => {
|
this.tc.onlineConns.forEach(remoteYInstance => {
|
||||||
// remote instance sends instance to this instance
|
if (remoteYInstance !== this) {
|
||||||
const encoder = encoding.createEncoder()
|
// remote instance sends instance to this instance
|
||||||
message.writeSyncStep1(encoder, remoteYInstance)
|
const encoder = encoding.createEncoder()
|
||||||
this._receive(encoding.toBuffer(encoder), remoteYInstance)
|
syncProtocol.writeSyncStep1(encoder, remoteYInstance)
|
||||||
|
this._receive(encoding.toBuffer(encoder), remoteYInstance)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
this.tc.onlineConns.add(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -117,9 +122,10 @@ export class TestConnector {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Create a new Y instance and add it to the list of connections
|
* Create a new Y instance and add it to the list of connections
|
||||||
|
* @param {number} clientID
|
||||||
*/
|
*/
|
||||||
createY () {
|
createY (clientID) {
|
||||||
return new TestYInstance(this)
|
return new TestYInstance(this, clientID)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Choose random connection and flush a random message from a random sender.
|
* Choose random connection and flush a random message from a random sender.
|
||||||
@ -139,8 +145,9 @@ export class TestConnector {
|
|||||||
}
|
}
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = encoding.createEncoder()
|
||||||
receiver.mMux(() => {
|
receiver.mMux(() => {
|
||||||
|
console.log('receive (' + sender.userID + '->' + receiver.userID + '):\n', syncProtocol.stringifySyncMessage(decoding.createDecoder(m), receiver))
|
||||||
// do not publish data created when this function is executed (could be ss2 or update message)
|
// do not publish data created when this function is executed (could be ss2 or update message)
|
||||||
message.readMessage(decoding.createDecoder(m), encoder, receiver)
|
syncProtocol.readSyncMessage(decoding.createDecoder(m), encoder, receiver)
|
||||||
})
|
})
|
||||||
if (encoding.length(encoder) > 0) {
|
if (encoding.length(encoder) > 0) {
|
||||||
// send reply message
|
// send reply message
|
||||||
@ -202,12 +209,15 @@ export class TestConnector {
|
|||||||
* @param {TestYInstance} y // publish message created by `y` to all other online clients
|
* @param {TestYInstance} y // publish message created by `y` to all other online clients
|
||||||
* @param {ArrayBuffer} m
|
* @param {ArrayBuffer} m
|
||||||
*/
|
*/
|
||||||
const broadcastMessage = (y, m) =>
|
const broadcastMessage = (y, m) => {
|
||||||
y.tc.onlineConns.forEach(remoteYInstance => {
|
if (y.tc.onlineConns.has(y)) {
|
||||||
if (remoteYInstance !== y) {
|
y.tc.onlineConns.forEach(remoteYInstance => {
|
||||||
remoteYInstance._receive(m, y)
|
if (remoteYInstance !== y) {
|
||||||
}
|
remoteYInstance._receive(m, y)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert DS to a proper DeleteSet of Map.
|
* Convert DS to a proper DeleteSet of Map.
|
||||||
@ -295,7 +305,7 @@ export function compareUsers (t, users) {
|
|||||||
data.os = ops
|
data.os = ops
|
||||||
data.ds = getDeleteSet(u)
|
data.ds = getDeleteSet(u)
|
||||||
const ss = {}
|
const ss = {}
|
||||||
u.ss.state.forEach((user, clock) => {
|
u.ss.state.forEach((clock, user) => {
|
||||||
ss[user] = clock
|
ss[user] = clock
|
||||||
})
|
})
|
||||||
data.ss = ss
|
data.ss = ss
|
||||||
@ -347,20 +357,20 @@ export function initArrays (t, opts) {
|
|||||||
const testConnector = new TestConnector(prng)
|
const testConnector = new TestConnector(prng)
|
||||||
result.testConnector = testConnector
|
result.testConnector = testConnector
|
||||||
for (let i = 0; i < opts.users; i++) {
|
for (let i = 0; i < opts.users; i++) {
|
||||||
let y = testConnector.createY()
|
let y = testConnector.createY(i)
|
||||||
result.users.push(y)
|
result.users.push(y)
|
||||||
result['array' + i] = y.define('array', Y.Array)
|
result['array' + i] = y.define('array', Y.Array)
|
||||||
result['map' + i] = y.define('map', Y.Map)
|
result['map' + i] = y.define('map', Y.Map)
|
||||||
const yxml = y.define('xml', Y.XmlElement)
|
const yxml = y.define('xml', Y.XmlElement)
|
||||||
result['xml' + i] = yxml
|
result['xml' + i] = yxml
|
||||||
const dom = document.createElement('my-dom')
|
const dom = document.createElement('my-dom')
|
||||||
const domBinding = new Y.DomBinding(yxml, dom, { filter })
|
const domBinding = new DomBinding(yxml, dom, { filter })
|
||||||
result['domBinding' + i] = domBinding
|
result['domBinding' + i] = domBinding
|
||||||
result['dom' + i] = dom
|
result['dom' + i] = dom
|
||||||
const textType = y.define('text', Y.Text)
|
const textType = y.define('text', Y.Text)
|
||||||
result['text' + i] = textType
|
result['text' + i] = textType
|
||||||
const quill = new Quill(document.createElement('div'))
|
const quill = new Quill(document.createElement('div'))
|
||||||
result['quillBinding' + i] = new Y.QuillBinding(textType, quill)
|
result['quillBinding' + i] = new QuillBinding(textType, quill)
|
||||||
result['quill' + i] = quill
|
result['quill' + i] = quill
|
||||||
y.quill = quill // put quill on the y object (so we can use it later)
|
y.quill = quill // put quill on the y object (so we can use it later)
|
||||||
y.dom = dom
|
y.dom = dom
|
||||||
|
Loading…
x
Reference in New Issue
Block a user