2019-05-28 14:18:20 +02:00
|
|
|
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
2019-03-01 23:26:40 +01:00
|
|
|
import * as Y from '../src/index.js'
|
2021-05-14 18:53:24 +02:00
|
|
|
import * as t from 'lib0/testing'
|
|
|
|
|
import * as prng from 'lib0/prng'
|
|
|
|
|
import * as math from 'lib0/math'
|
2024-10-04 21:23:59 +02:00
|
|
|
import * as env from 'lib0/environment'
|
2025-04-28 17:06:32 +02:00
|
|
|
import * as delta from '../src/utils/Delta.js'
|
2024-10-04 21:23:59 +02:00
|
|
|
|
|
|
|
|
const isDevMode = env.getVariable('node_env') === 'development'
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2020-07-12 18:25:45 +02:00
|
|
|
/**
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2020-07-12 18:25:45 +02:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testBasicUpdate = _tc => {
|
2020-07-12 18:25:45 +02:00
|
|
|
const doc1 = new Y.Doc()
|
|
|
|
|
const doc2 = new Y.Doc()
|
|
|
|
|
doc1.getArray('array').insert(0, ['hi'])
|
|
|
|
|
const update = Y.encodeStateAsUpdate(doc1)
|
|
|
|
|
Y.applyUpdate(doc2, update)
|
|
|
|
|
t.compare(doc2.getArray('array').toArray(), ['hi'])
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 21:23:59 +02:00
|
|
|
/**
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2024-10-04 21:23:59 +02:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testFailsObjectManipulationInDevMode = _tc => {
|
2024-10-04 21:23:59 +02:00
|
|
|
if (isDevMode) {
|
|
|
|
|
t.info('running in dev mode')
|
|
|
|
|
const doc = new Y.Doc()
|
|
|
|
|
const a = [1, 2, 3]
|
|
|
|
|
const b = { o: 1 }
|
|
|
|
|
doc.getArray('test').insert(0, [a])
|
|
|
|
|
doc.getMap('map').set('k', b)
|
|
|
|
|
t.fails(() => {
|
|
|
|
|
a[0] = 42
|
|
|
|
|
})
|
|
|
|
|
t.fails(() => {
|
|
|
|
|
b.o = 42
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
t.info('not in dev mode')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-08 01:51:39 +01:00
|
|
|
/**
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2020-11-08 01:51:39 +01:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testSlice = _tc => {
|
2020-11-08 01:51:39 +01:00
|
|
|
const doc1 = new Y.Doc()
|
|
|
|
|
const arr = doc1.getArray('array')
|
|
|
|
|
arr.insert(0, [1, 2, 3])
|
|
|
|
|
t.compareArrays(arr.slice(0), [1, 2, 3])
|
|
|
|
|
t.compareArrays(arr.slice(1), [2, 3])
|
|
|
|
|
t.compareArrays(arr.slice(0, -1), [1, 2])
|
|
|
|
|
arr.insert(0, [0])
|
|
|
|
|
t.compareArrays(arr.slice(0), [0, 1, 2, 3])
|
|
|
|
|
t.compareArrays(arr.slice(0, 2), [0, 1])
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-23 06:41:57 +00:00
|
|
|
/**
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2023-01-23 06:41:57 +00:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testArrayFrom = _tc => {
|
2023-01-23 06:41:57 +00:00
|
|
|
const doc1 = new Y.Doc()
|
|
|
|
|
const db1 = doc1.getMap('root')
|
|
|
|
|
const nestedArray1 = Y.Array.from([0, 1, 2])
|
|
|
|
|
db1.set('array', nestedArray1)
|
|
|
|
|
t.compare(nestedArray1.toArray(), [0, 1, 2])
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-25 21:17:01 +02:00
|
|
|
/**
|
|
|
|
|
* Debugging yjs#297 - a critical bug connected to the search-marker approach
|
|
|
|
|
*
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2021-05-25 21:17:01 +02:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testLengthIssue = _tc => {
|
2021-05-25 21:17:01 +02:00
|
|
|
const doc1 = new Y.Doc()
|
|
|
|
|
const arr = doc1.getArray('array')
|
|
|
|
|
arr.push([0, 1, 2, 3])
|
|
|
|
|
arr.delete(0)
|
|
|
|
|
arr.insert(0, [0])
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
doc1.transact(() => {
|
|
|
|
|
arr.delete(1)
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
arr.insert(1, [1])
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
arr.delete(2)
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
arr.insert(2, [2])
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
})
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
arr.delete(1)
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
arr.insert(1, [1])
|
|
|
|
|
t.assert(arr.length === arr.toArray().length)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 16:50:25 +02:00
|
|
|
/**
|
|
|
|
|
* Debugging yjs#314
|
|
|
|
|
*
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2021-06-24 16:50:25 +02:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testLengthIssue2 = _tc => {
|
2021-06-24 16:50:25 +02:00
|
|
|
const doc = new Y.Doc()
|
|
|
|
|
const next = doc.getArray()
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.insert(0, ['group2'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.insert(1, ['rectangle3'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.delete(0)
|
|
|
|
|
next.insert(0, ['rectangle3'])
|
|
|
|
|
})
|
|
|
|
|
next.delete(1)
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.insert(1, ['ellipse4'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.insert(2, ['ellipse3'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
next.insert(3, ['ellipse2'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
t.fails(() => {
|
|
|
|
|
next.insert(5, ['rectangle2'])
|
|
|
|
|
})
|
|
|
|
|
next.insert(4, ['rectangle2'])
|
|
|
|
|
})
|
|
|
|
|
doc.transact(() => {
|
|
|
|
|
// this should not throw an error message
|
|
|
|
|
next.delete(4)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
console.log(next.toArray())
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-04 14:28:18 +01:00
|
|
|
export const testDeleteInsert = tc => {
|
2019-03-05 14:00:31 +01:00
|
|
|
const { users, array0 } = init(tc, { users: 2 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.delete(0, 0)
|
2019-03-10 23:26:53 +01:00
|
|
|
t.describe('Does not throw when deleting zero elements with position 0')
|
2019-03-05 14:00:31 +01:00
|
|
|
t.fails(() => {
|
2017-10-16 04:53:12 +02:00
|
|
|
array0.delete(1, 1)
|
2019-03-10 23:26:53 +01:00
|
|
|
})
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['A'])
|
|
|
|
|
array0.delete(1, 0)
|
2019-03-10 23:26:53 +01:00
|
|
|
t.describe('Does not throw when deleting zero elements with valid position 1')
|
2019-03-04 14:28:18 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertThreeElementsTryRegetProperty = tc => {
|
|
|
|
|
const { testConnector, users, array0, array1 } = init(tc, { users: 2 })
|
2019-05-25 03:12:56 +02:00
|
|
|
array0.insert(0, [1, true, false])
|
|
|
|
|
t.compare(array0.toJSON(), [1, true, false], '.toJSON() works')
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2019-05-25 03:12:56 +02:00
|
|
|
t.compare(array1.toJSON(), [1, true, false], '.toJSON() works after sync')
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testConcurrentInsertWithThreeConflicts = tc => {
|
2020-12-19 16:29:17 +01:00
|
|
|
const { users, array0, array1, array2 } = init(tc, { users: 3 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, [0])
|
|
|
|
|
array1.insert(0, [1])
|
|
|
|
|
array2.insert(0, [2])
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testConcurrentInsertDeleteWithThreeConflicts = tc => {
|
|
|
|
|
const { testConnector, users, array0, array1, array2 } = init(tc, { users: 3 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y', 'z'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(1, [0])
|
|
|
|
|
array1.delete(0)
|
|
|
|
|
array1.delete(1, 1)
|
|
|
|
|
array2.insert(1, [2])
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertionsInLateSync = tc => {
|
|
|
|
|
const { testConnector, users, array0, array1, array2 } = init(tc, { users: 3 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2017-08-09 02:21:17 +02:00
|
|
|
users[1].disconnect()
|
|
|
|
|
users[2].disconnect()
|
|
|
|
|
array0.insert(1, ['user0'])
|
|
|
|
|
array1.insert(1, ['user1'])
|
|
|
|
|
array2.insert(1, ['user2'])
|
2019-03-05 14:00:31 +01:00
|
|
|
users[1].connect()
|
|
|
|
|
users[2].connect()
|
2018-11-16 12:33:20 +01:00
|
|
|
testConnector.flushAllMessages()
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testDisconnectReallyPreventsSendingMessages = tc => {
|
2020-12-19 16:29:17 +01:00
|
|
|
const { testConnector, users, array0, array1 } = init(tc, { users: 3 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2017-08-09 02:21:17 +02:00
|
|
|
users[1].disconnect()
|
|
|
|
|
users[2].disconnect()
|
|
|
|
|
array0.insert(1, ['user0'])
|
|
|
|
|
array1.insert(1, ['user1'])
|
2017-10-19 17:36:28 +02:00
|
|
|
t.compare(array0.toJSON(), ['x', 'user0', 'y'])
|
|
|
|
|
t.compare(array1.toJSON(), ['x', 'user1', 'y'])
|
2019-03-05 14:00:31 +01:00
|
|
|
users[1].connect()
|
|
|
|
|
users[2].connect()
|
|
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testDeletionsInLateSync = tc => {
|
|
|
|
|
const { testConnector, users, array0, array1 } = init(tc, { users: 2 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2019-03-05 14:00:31 +01:00
|
|
|
users[1].disconnect()
|
2017-08-09 02:21:17 +02:00
|
|
|
array1.delete(1, 1)
|
|
|
|
|
array0.delete(0, 2)
|
2019-03-05 14:00:31 +01:00
|
|
|
users[1].connect()
|
|
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertThenMergeDeleteOnSync = tc => {
|
|
|
|
|
const { testConnector, users, array0, array1 } = init(tc, { users: 2 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y', 'z'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
|
|
|
|
users[0].disconnect()
|
2017-08-09 02:21:17 +02:00
|
|
|
array1.delete(0, 3)
|
2018-10-29 21:58:21 +01:00
|
|
|
users[0].connect()
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertAndDeleteEvents = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
2019-04-03 02:30:44 +02:00
|
|
|
/**
|
2019-04-06 13:00:32 +02:00
|
|
|
* @type {Object<string,any>?}
|
2019-04-03 02:30:44 +02:00
|
|
|
*/
|
2019-04-06 13:00:32 +02:00
|
|
|
let event = null
|
2018-11-25 03:17:00 +01:00
|
|
|
array0.observe(e => {
|
2017-08-09 02:21:17 +02:00
|
|
|
event = e
|
|
|
|
|
})
|
|
|
|
|
array0.insert(0, [0, 1, 2])
|
2019-04-06 13:00:32 +02:00
|
|
|
t.assert(event !== null)
|
|
|
|
|
event = null
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.delete(0)
|
2019-04-06 13:00:32 +02:00
|
|
|
t.assert(event !== null)
|
|
|
|
|
event = null
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.delete(0, 2)
|
2019-04-06 13:00:32 +02:00
|
|
|
t.assert(event !== null)
|
|
|
|
|
event = null
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-26 13:31:00 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testNestedObserverEvents = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
|
|
|
|
/**
|
|
|
|
|
* @type {Array<number>}
|
|
|
|
|
*/
|
|
|
|
|
const vals = []
|
2025-04-28 17:06:32 +02:00
|
|
|
array0.observe(() => {
|
2019-04-26 13:31:00 +02:00
|
|
|
if (array0.length === 1) {
|
|
|
|
|
// inserting, will call this observer again
|
|
|
|
|
// we expect that this observer is called after this event handler finishedn
|
|
|
|
|
array0.insert(1, [1])
|
|
|
|
|
vals.push(0)
|
|
|
|
|
} else {
|
|
|
|
|
// this should be called the second time an element is inserted (above case)
|
|
|
|
|
vals.push(1)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
array0.insert(0, [0])
|
|
|
|
|
t.compareArrays(vals, [0, 1])
|
|
|
|
|
t.compareArrays(array0.toArray(), [0, 1])
|
|
|
|
|
compare(users)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertAndDeleteEventsForTypes = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
2019-04-03 02:30:44 +02:00
|
|
|
/**
|
2019-04-06 13:00:32 +02:00
|
|
|
* @type {Object<string,any>|null}
|
2019-04-03 02:30:44 +02:00
|
|
|
*/
|
2019-04-06 13:00:32 +02:00
|
|
|
let event = null
|
2018-11-25 03:17:00 +01:00
|
|
|
array0.observe(e => {
|
2017-08-09 02:21:17 +02:00
|
|
|
event = e
|
|
|
|
|
})
|
2019-04-03 02:30:44 +02:00
|
|
|
array0.insert(0, [new Y.Array()])
|
2019-04-06 13:00:32 +02:00
|
|
|
t.assert(event !== null)
|
|
|
|
|
event = null
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.delete(0)
|
2019-04-06 13:00:32 +02:00
|
|
|
t.assert(event !== null)
|
|
|
|
|
event = null
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2020-10-29 12:40:39 +01:00
|
|
|
/**
|
|
|
|
|
* This issue has been reported in https://discuss.yjs.dev/t/order-in-which-events-yielded-by-observedeep-should-be-applied/261/2
|
|
|
|
|
*
|
|
|
|
|
* Deep observers generate multiple events. When an array added at item at, say, position 0,
|
|
|
|
|
* and item 1 changed then the array-add event should fire first so that the change event
|
|
|
|
|
* path is correct. A array binding might lead to an inconsistent state otherwise.
|
|
|
|
|
*
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testObserveDeepEventOrder = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
|
|
|
|
/**
|
|
|
|
|
* @type {Array<any>}
|
|
|
|
|
*/
|
|
|
|
|
let events = []
|
|
|
|
|
array0.observeDeep(e => {
|
|
|
|
|
events = e
|
|
|
|
|
})
|
|
|
|
|
array0.insert(0, [new Y.Map()])
|
|
|
|
|
users[0].transact(() => {
|
|
|
|
|
array0.get(0).set('a', 'a')
|
|
|
|
|
array0.insert(0, [0])
|
|
|
|
|
})
|
|
|
|
|
for (let i = 1; i < events.length; i++) {
|
|
|
|
|
t.assert(events[i - 1].path.length <= events[i].path.length, 'path size increases, fire top-level events first')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-10 12:18:16 +02:00
|
|
|
/**
|
|
|
|
|
* Correct index when computing event.path in observeDeep - https://github.com/yjs/yjs/issues/457
|
|
|
|
|
*
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testObservedeepIndexes = _tc => {
|
|
|
|
|
const doc = new Y.Doc()
|
|
|
|
|
const map = doc.getMap()
|
|
|
|
|
// Create a field with the array as value
|
|
|
|
|
map.set('my-array', new Y.Array())
|
|
|
|
|
// Fill the array with some strings and our Map
|
|
|
|
|
map.get('my-array').push(['a', 'b', 'c', new Y.Map()])
|
|
|
|
|
/**
|
|
|
|
|
* @type {Array<any>}
|
|
|
|
|
*/
|
|
|
|
|
let eventPath = []
|
|
|
|
|
map.observeDeep((events) => { eventPath = events[0].path })
|
|
|
|
|
// set a value on the map inside of our array
|
|
|
|
|
map.get('my-array').get(3).set('hello', 'world')
|
|
|
|
|
console.log(eventPath)
|
|
|
|
|
t.compare(eventPath, ['my-array', 3])
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 16:33:29 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testChangeEvent = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
|
|
|
|
/**
|
|
|
|
|
* @type {any}
|
|
|
|
|
*/
|
|
|
|
|
let changes = null
|
|
|
|
|
array0.observe(e => {
|
|
|
|
|
changes = e.changes
|
|
|
|
|
})
|
|
|
|
|
const newArr = new Y.Array()
|
|
|
|
|
array0.insert(0, [newArr, 4, 'dtrn'])
|
|
|
|
|
t.assert(changes !== null && changes.added.size === 2 && changes.deleted.size === 0)
|
2019-10-25 23:44:09 +02:00
|
|
|
t.compare(changes.delta, [{ insert: [newArr, 4, 'dtrn'] }])
|
2019-09-03 16:33:29 +02:00
|
|
|
changes = null
|
|
|
|
|
array0.delete(0, 2)
|
|
|
|
|
t.assert(changes !== null && changes.added.size === 0 && changes.deleted.size === 2)
|
|
|
|
|
t.compare(changes.delta, [{ delete: 2 }])
|
|
|
|
|
changes = null
|
|
|
|
|
array0.insert(1, [0.1])
|
|
|
|
|
t.assert(changes !== null && changes.added.size === 1 && changes.deleted.size === 0)
|
|
|
|
|
t.compare(changes.delta, [{ retain: 1 }, { insert: [0.1] }])
|
|
|
|
|
compare(users)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testInsertAndDeleteEventsForTypes2 = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
2019-04-03 02:30:44 +02:00
|
|
|
/**
|
|
|
|
|
* @type {Array<Object<string,any>>}
|
|
|
|
|
*/
|
2020-01-22 16:42:16 +01:00
|
|
|
const events = []
|
2018-11-25 03:17:00 +01:00
|
|
|
array0.observe(e => {
|
2017-08-09 02:21:17 +02:00
|
|
|
events.push(e)
|
|
|
|
|
})
|
2019-04-03 02:30:44 +02:00
|
|
|
array0.insert(0, ['hi', new Y.Map()])
|
2017-10-22 19:12:50 +02:00
|
|
|
t.assert(events.length === 1, 'Event is triggered exactly once for insertion of two elements')
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.delete(1)
|
2017-10-22 19:12:50 +02:00
|
|
|
t.assert(events.length === 2, 'Event is triggered exactly once for deletion')
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-06-18 17:41:19 +02:00
|
|
|
/**
|
2019-09-04 13:05:03 +02:00
|
|
|
* This issue has been reported here https://github.com/yjs/yjs/issues/155
|
2019-06-18 17:41:19 +02:00
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testNewChildDoesNotEmitEventInTransaction = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 2 })
|
|
|
|
|
let fired = false
|
|
|
|
|
users[0].transact(() => {
|
|
|
|
|
const newMap = new Y.Map()
|
|
|
|
|
newMap.observe(() => {
|
|
|
|
|
fired = true
|
|
|
|
|
})
|
|
|
|
|
array0.insert(0, [newMap])
|
|
|
|
|
newMap.set('tst', 42)
|
|
|
|
|
})
|
|
|
|
|
t.assert(!fired, 'Event does not trigger')
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testGarbageCollector = tc => {
|
|
|
|
|
const { testConnector, users, array0 } = init(tc, { users: 3 })
|
2017-08-09 02:21:17 +02:00
|
|
|
array0.insert(0, ['x', 'y', 'z'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2017-08-09 02:21:17 +02:00
|
|
|
users[0].disconnect()
|
|
|
|
|
array0.delete(0, 3)
|
2019-03-05 14:00:31 +01:00
|
|
|
users[0].connect()
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testEventTargetIsSetCorrectlyOnLocal = tc => {
|
|
|
|
|
const { array0, users } = init(tc, { users: 3 })
|
|
|
|
|
/**
|
|
|
|
|
* @type {any}
|
|
|
|
|
*/
|
|
|
|
|
let event
|
2018-11-25 03:17:00 +01:00
|
|
|
array0.observe(e => {
|
2017-08-09 02:21:17 +02:00
|
|
|
event = e
|
|
|
|
|
})
|
|
|
|
|
array0.insert(0, ['stuff'])
|
2017-10-22 19:12:50 +02:00
|
|
|
t.assert(event.target === array0, '"target" property is set correctly')
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-05 14:00:31 +01:00
|
|
|
export const testEventTargetIsSetCorrectlyOnRemote = tc => {
|
|
|
|
|
const { testConnector, array0, array1, users } = init(tc, { users: 3 })
|
|
|
|
|
/**
|
|
|
|
|
* @type {any}
|
|
|
|
|
*/
|
|
|
|
|
let event
|
2018-11-25 03:17:00 +01:00
|
|
|
array0.observe(e => {
|
2017-08-09 02:21:17 +02:00
|
|
|
event = e
|
|
|
|
|
})
|
|
|
|
|
array1.insert(0, ['stuff'])
|
2018-10-29 21:58:21 +01:00
|
|
|
testConnector.flushAllMessages()
|
2017-10-22 19:12:50 +02:00
|
|
|
t.assert(event.target === array0, '"target" property is set correctly')
|
2019-03-05 14:00:31 +01:00
|
|
|
compare(users)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
2025-04-28 17:06:32 +02:00
|
|
|
* @param {t.TestCase} _tc
|
2019-04-02 23:08:58 +02:00
|
|
|
*/
|
2025-04-28 17:06:32 +02:00
|
|
|
export const testIteratingArrayContainingTypes = _tc => {
|
2019-05-07 13:44:23 +02:00
|
|
|
const y = new Y.Doc()
|
2019-04-03 02:30:44 +02:00
|
|
|
const arr = y.getArray('arr')
|
2019-01-16 03:12:58 +03:00
|
|
|
const numItems = 10
|
2019-03-01 23:26:40 +01:00
|
|
|
for (let i = 0; i < numItems; i++) {
|
2019-01-16 03:12:58 +03:00
|
|
|
const map = new Y.Map()
|
|
|
|
|
map.set('value', i)
|
|
|
|
|
arr.push([map])
|
|
|
|
|
}
|
|
|
|
|
let cnt = 0
|
2020-01-22 16:42:16 +01:00
|
|
|
for (const item of arr) {
|
2019-01-16 03:12:58 +03:00
|
|
|
t.assert(item.get('value') === cnt++, 'value is correct')
|
|
|
|
|
}
|
|
|
|
|
y.destroy()
|
2017-08-09 02:21:17 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-28 17:06:32 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} _tc
|
|
|
|
|
*/
|
|
|
|
|
export const testAttributedContent = _tc => {
|
|
|
|
|
const ydoc = new Y.Doc({ gc: false })
|
|
|
|
|
const yarray = ydoc.getArray()
|
|
|
|
|
yarray.insert(0, [1, 2])
|
|
|
|
|
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(() => {
|
|
|
|
|
yarray.delete(0, 1)
|
|
|
|
|
yarray.insert(1, [42])
|
|
|
|
|
})
|
2025-04-29 18:02:15 +02:00
|
|
|
const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
|
|
|
|
const attributedContent = yarray.getContent(attributionManager)
|
2025-05-07 22:43:23 +02:00
|
|
|
console.log(attributedContent.toJSON())
|
2025-04-28 17:06:32 +02:00
|
|
|
t.assert(attributedContent.equals(expectedContent))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-05 14:00:31 +01:00
|
|
|
let _uniqueNumber = 0
|
|
|
|
|
const getUniqueNumber = () => _uniqueNumber++
|
|
|
|
|
|
2019-04-03 02:30:44 +02:00
|
|
|
/**
|
2019-05-28 14:18:20 +02:00
|
|
|
* @type {Array<function(Doc,prng.PRNG,any):void>}
|
2019-04-03 02:30:44 +02:00
|
|
|
*/
|
2019-03-11 12:31:37 +01:00
|
|
|
const arrayTransactions = [
|
2019-04-03 02:30:44 +02:00
|
|
|
function insert (user, gen) {
|
|
|
|
|
const yarray = user.getArray('array')
|
2020-12-19 16:29:17 +01:00
|
|
|
const uniqueNumber = getUniqueNumber()
|
|
|
|
|
const content = []
|
|
|
|
|
const len = prng.int32(gen, 1, 4)
|
|
|
|
|
for (let i = 0; i < len; i++) {
|
2017-08-09 02:21:17 +02:00
|
|
|
content.push(uniqueNumber)
|
|
|
|
|
}
|
2020-12-19 16:29:17 +01:00
|
|
|
const pos = prng.int32(gen, 0, yarray.length)
|
2020-07-15 22:03:02 +02:00
|
|
|
const oldContent = yarray.toArray()
|
2017-10-22 19:12:50 +02:00
|
|
|
yarray.insert(pos, content)
|
2020-07-15 22:03:02 +02:00
|
|
|
oldContent.splice(pos, 0, ...content)
|
|
|
|
|
t.compareArrays(yarray.toArray(), oldContent) // we want to make sure that fastSearch markers insert at the correct position
|
2017-08-09 02:21:17 +02:00
|
|
|
},
|
2019-04-03 02:30:44 +02:00
|
|
|
function insertTypeArray (user, gen) {
|
|
|
|
|
const yarray = user.getArray('array')
|
2020-12-19 16:29:17 +01:00
|
|
|
const pos = prng.int32(gen, 0, yarray.length)
|
2019-04-03 02:30:44 +02:00
|
|
|
yarray.insert(pos, [new Y.Array()])
|
2020-12-19 16:29:17 +01:00
|
|
|
const array2 = yarray.get(pos)
|
2017-08-09 02:21:17 +02:00
|
|
|
array2.insert(0, [1, 2, 3, 4])
|
|
|
|
|
},
|
2019-04-03 02:30:44 +02:00
|
|
|
function insertTypeMap (user, gen) {
|
|
|
|
|
const yarray = user.getArray('array')
|
2020-12-19 16:29:17 +01:00
|
|
|
const pos = prng.int32(gen, 0, yarray.length)
|
2019-04-03 02:30:44 +02:00
|
|
|
yarray.insert(pos, [new Y.Map()])
|
2020-12-19 16:29:17 +01:00
|
|
|
const map = yarray.get(pos)
|
2017-08-09 02:21:17 +02:00
|
|
|
map.set('someprop', 42)
|
|
|
|
|
map.set('someprop', 43)
|
|
|
|
|
map.set('someprop', 44)
|
2017-10-26 19:12:33 +02:00
|
|
|
},
|
2021-09-21 14:23:58 +02:00
|
|
|
function insertTypeNull (user, gen) {
|
|
|
|
|
const yarray = user.getArray('array')
|
|
|
|
|
const pos = prng.int32(gen, 0, yarray.length)
|
|
|
|
|
yarray.insert(pos, [null])
|
|
|
|
|
},
|
2019-04-03 02:30:44 +02:00
|
|
|
function _delete (user, gen) {
|
|
|
|
|
const yarray = user.getArray('array')
|
2020-12-19 16:29:17 +01:00
|
|
|
const length = yarray.length
|
2017-08-09 02:21:17 +02:00
|
|
|
if (length > 0) {
|
2020-12-19 16:29:17 +01:00
|
|
|
let somePos = prng.int32(gen, 0, length - 1)
|
|
|
|
|
let delLength = prng.int32(gen, 1, math.min(2, length - somePos))
|
2019-04-03 02:30:44 +02:00
|
|
|
if (prng.bool(gen)) {
|
2020-12-19 16:29:17 +01:00
|
|
|
const type = yarray.get(somePos)
|
2021-09-21 14:23:58 +02:00
|
|
|
if (type instanceof Y.Array && type.length > 0) {
|
2020-07-12 18:25:45 +02:00
|
|
|
somePos = prng.int32(gen, 0, type.length - 1)
|
|
|
|
|
delLength = prng.int32(gen, 0, math.min(2, type.length - somePos))
|
2019-04-03 02:30:44 +02:00
|
|
|
type.delete(somePos, delLength)
|
2017-08-09 02:21:17 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2020-07-15 22:03:02 +02:00
|
|
|
const oldContent = yarray.toArray()
|
2017-10-22 19:12:50 +02:00
|
|
|
yarray.delete(somePos, delLength)
|
2020-07-15 22:03:02 +02:00
|
|
|
oldContent.splice(somePos, delLength)
|
|
|
|
|
t.compareArrays(yarray.toArray(), oldContent)
|
2017-08-09 02:21:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2020-07-15 22:03:02 +02:00
|
|
|
export const testRepeatGeneratingYarrayTests6 = tc => {
|
2025-06-06 17:46:12 +02:00
|
|
|
applyRandomTests(tc, arrayTransactions, 6)
|
2019-03-06 13:29:16 +01:00
|
|
|
}
|
2017-10-22 19:12:50 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests40 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 40)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests42 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 42)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests43 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 43)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests44 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 44)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests45 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 45)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests46 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 46)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests300 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 300)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests400 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 400)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests500 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 500)
|
|
|
|
|
}
|
2017-08-09 02:21:17 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests600 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 600)
|
|
|
|
|
}
|
2017-10-26 14:40:21 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests1000 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 1000)
|
|
|
|
|
}
|
2017-10-26 14:40:21 +02:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-03-06 13:29:16 +01:00
|
|
|
export const testRepeatGeneratingYarrayTests1800 = tc => {
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 1800)
|
|
|
|
|
}
|
2019-03-07 18:57:39 +01:00
|
|
|
|
2019-04-02 23:08:58 +02:00
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
2019-04-09 00:48:24 +02:00
|
|
|
export const testRepeatGeneratingYarrayTests3000 = tc => {
|
|
|
|
|
t.skip(!t.production)
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 3000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testRepeatGeneratingYarrayTests5000 = tc => {
|
|
|
|
|
t.skip(!t.production)
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 5000)
|
2019-03-07 18:57:39 +01:00
|
|
|
}
|
2019-04-09 00:31:17 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {t.TestCase} tc
|
|
|
|
|
*/
|
|
|
|
|
export const testRepeatGeneratingYarrayTests30000 = tc => {
|
|
|
|
|
t.skip(!t.production)
|
|
|
|
|
applyRandomTests(tc, arrayTransactions, 30000)
|
|
|
|
|
}
|