2019-08-31 22:42:18 +02:00
import * as Y from './testHelper.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'
2025-04-21 02:00:14 +02:00
import * as delta from '../src/utils/Delta.js'
2025-05-07 00:35:57 +02:00
import { createIdMapFromIdSet , noAttributionsManager , TwosetAttributionManager , createAttributionManagerFromSnapshots } from 'yjs/internals'
2020-05-11 01:45:27 +02:00
2019-08-31 22:42:18 +02:00
const { init , compare } = Y
2018-02-26 02:18:39 +01:00
2023-02-13 14:27:57 +01:00
/ * *
* https : //github.com/yjs/yjs/issues/474
* @ todo Remove debug : 127.0 . 0.1 : 8080 / test . html ? filter = \ [ 88 /
* @ param { t . TestCase } _tc
* /
export const testDeltaBug = _tc => {
const initialDelta = [ {
attributes : {
'block-id' : 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087'
} ,
insert : '\n'
} ,
{
attributes : {
'table-col' : {
width : '150'
}
} ,
insert : '\n\n\n'
} ,
{
attributes : {
'block-id' : 'block-9144be72-e528-4f91-b0b2-82d20408e9ea' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-6kv2ls' ,
cell : 'cell-apba4k'
} ,
row : 'row-6kv2ls' ,
cell : 'cell-apba4k' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-639adacb-1516-43ed-b272-937c55669a1c' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-6kv2ls' ,
cell : 'cell-a8qf0r'
} ,
row : 'row-6kv2ls' ,
cell : 'cell-a8qf0r' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-6kv2ls' ,
cell : 'cell-oi9ikb'
} ,
row : 'row-6kv2ls' ,
cell : 'cell-oi9ikb' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-ceeddd05-330e-4f86-8017-4a3a060c4627' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-d1sv2g' ,
cell : 'cell-dt6ks2'
} ,
row : 'row-d1sv2g' ,
cell : 'cell-dt6ks2' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-d1sv2g' ,
cell : 'cell-qah2ay'
} ,
row : 'row-d1sv2g' ,
cell : 'cell-qah2ay' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-468a69b5-9332-450b-9107-381d593de249' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-d1sv2g' ,
cell : 'cell-fpcz5a'
} ,
row : 'row-d1sv2g' ,
cell : 'cell-fpcz5a' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-pflz90' ,
cell : 'cell-zrhylp'
} ,
row : 'row-pflz90' ,
cell : 'cell-zrhylp' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-6af97ba7-8cf9-497a-9365-7075b938837b' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-pflz90' ,
cell : 'cell-s1q9nt'
} ,
row : 'row-pflz90' ,
cell : 'cell-s1q9nt' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-107e273e-86bc-44fd-b0d7-41ab55aca484' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-pflz90' ,
cell : 'cell-20b0j9'
} ,
row : 'row-pflz90' ,
cell : 'cell-20b0j9' ,
rowspan : '1' ,
colspan : '1'
} ,
insert : '\n'
} ,
{
attributes : {
'block-id' : 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3'
} ,
insert : '\n'
} ,
{
insert : 'Content after table'
} ,
{
attributes : {
'block-id' : 'block-15630542-ef45-412d-9415-88f0052238ce'
} ,
insert : '\n'
}
]
const ydoc1 = new Y . Doc ( )
const ytext = ydoc1 . getText ( )
ytext . applyDelta ( initialDelta )
const addingDash = [
{
retain : 12
} ,
{
insert : '-'
}
]
ytext . applyDelta ( addingDash )
const addingSpace = [
{
retain : 13
} ,
{
insert : ' '
}
]
ytext . applyDelta ( addingSpace )
const addingList = [
{
retain : 12
} ,
{
delete : 2
} ,
{
retain : 1 ,
attributes : {
// Clear table line attribute
'table-cell-line' : null ,
// Add list attribute in place of table-cell-line
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-pflz90' ,
cell : 'cell-20b0j9' ,
list : 'bullet'
}
}
}
]
ytext . applyDelta ( addingList )
2025-07-07 20:23:50 +02:00
const result = ytext . getDelta ( )
2025-05-07 00:35:57 +02:00
const expectedResult = delta . createTextDelta ( )
. insert ( '\n' , { 'block-id' : 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' } )
. insert ( '\n\n\n' , { 'table-col' : { width : '150' } } )
. insert ( '\n' , {
'block-id' : 'block-9144be72-e528-4f91-b0b2-82d20408e9ea' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
2023-02-13 14:27:57 +01:00
row : 'row-6kv2ls' ,
2025-05-07 00:35:57 +02:00
cell : 'cell-apba4k'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-6kv2ls' ,
cell : 'cell-apba4k' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-639adacb-1516-43ed-b272-937c55669a1c' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
row : 'row-6kv2ls' ,
cell : 'cell-a8qf0r'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-6kv2ls' ,
cell : 'cell-a8qf0r' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
row : 'row-6kv2ls' ,
cell : 'cell-oi9ikb'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-6kv2ls' ,
cell : 'cell-oi9ikb' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-ceeddd05-330e-4f86-8017-4a3a060c4627' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
2023-02-13 14:27:57 +01:00
row : 'row-d1sv2g' ,
2025-05-07 00:35:57 +02:00
cell : 'cell-dt6ks2'
} ,
row : 'row-d1sv2g' ,
cell : 'cell-dt6ks2' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
row : 'row-d1sv2g' ,
cell : 'cell-qah2ay'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-d1sv2g' ,
cell : 'cell-qah2ay' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-468a69b5-9332-450b-9107-381d593de249' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
row : 'row-d1sv2g' ,
cell : 'cell-fpcz5a'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-d1sv2g' ,
cell : 'cell-fpcz5a' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
2023-02-13 14:27:57 +01:00
row : 'row-pflz90' ,
2025-05-07 00:35:57 +02:00
cell : 'cell-zrhylp'
} ,
row : 'row-pflz90' ,
cell : 'cell-zrhylp' ,
rowspan : '1' ,
colspan : '1'
} )
. insert ( '\n' , {
'block-id' : 'block-6af97ba7-8cf9-497a-9365-7075b938837b' ,
'table-cell-line' : {
2023-02-13 14:27:57 +01:00
rowspan : '1' ,
2025-05-07 00:35:57 +02:00
colspan : '1' ,
row : 'row-pflz90' ,
cell : 'cell-s1q9nt'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
row : 'row-pflz90' ,
cell : 'cell-s1q9nt' ,
rowspan : '1' ,
colspan : '1'
} )
// This attributes has only list and no table-cell-line
. insert ( '\n' , {
list : {
rowspan : '1' ,
colspan : '1' ,
2023-02-13 14:27:57 +01:00
row : 'row-pflz90' ,
cell : 'cell-20b0j9' ,
2025-05-07 00:35:57 +02:00
list : 'bullet'
2023-02-13 14:27:57 +01:00
} ,
2025-05-07 00:35:57 +02:00
'block-id' : 'block-107e273e-86bc-44fd-b0d7-41ab55aca484' ,
row : 'row-pflz90' ,
cell : 'cell-20b0j9' ,
rowspan : '1' ,
colspan : '1'
} )
// No table-cell-line below here
. insert ( '\n' , {
'block-id' : 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3'
} )
. insert ( 'Content after table' )
. insert ( '\n' , {
'block-id' : 'block-15630542-ef45-412d-9415-88f0052238ce'
} )
. done ( )
2023-02-13 14:27:57 +01:00
t . compare ( result , expectedResult )
}
2023-03-02 19:08:01 +01:00
/ * *
* https : //github.com/yjs/yjs/issues/503
* @ param { t . TestCase } _tc
* /
export const testDeltaBug2 = _tc => {
const initialContent = [
{ insert : "Thomas' section" } ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-61ae80ac-a469-4eae-bac9-3b6a2c380118' }
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-d265d93f-1cc7-40ee-bb58-8270fca2619f' }
} ,
{ insert : '123' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-592a7bee-76a3-4e28-9c25-7a84344f8813' ,
list : { list : 'toggled' , 'toggle-id' : 'list-66xfft' }
}
} ,
{ insert : '456' } ,
{
insert : '\n' ,
attributes : {
indent : 1 ,
'block-id' : 'block-3ee2bd70-b97f-45b2-9115-f1e8910235b1' ,
list : { list : 'toggled' , 'toggle-id' : 'list-6vh0t0' }
}
} ,
{ insert : '789' } ,
{
insert : '\n' ,
attributes : {
indent : 1 ,
'block-id' : 'block-78150cf3-9bb5-4dea-a6f5-0ce1d2a98b9c' ,
list : { list : 'toggled' , 'toggle-id' : 'list-7jr0l2' }
}
} ,
{ insert : '901' } ,
{
insert : '\n' ,
attributes : {
indent : 1 ,
'block-id' : 'block-13c6416f-f522-41d5-9fd4-ce4eb1cde5ba' ,
list : { list : 'toggled' , 'toggle-id' : 'list-7uk8qu' }
}
} ,
{
insert : {
slash _command : {
id : 'doc_94zq-2436' ,
sessionId : 'nkwc70p2j' ,
replace : '/'
}
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-8a1d2bb6-23c2-4bcf-af3c-3919ffea1697' }
} ,
2025-05-07 00:35:57 +02:00
{ insert : '\n\n\n' , attributes : { 'table-col' : { width : '150' } } } ,
2023-03-02 19:08:01 +01:00
{
insert : '\n' ,
attributes : {
'block-id' : 'block-84ec3ea4-da6a-4e03-b430-0e5f432936a9' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-blmd4s' ,
cell : 'cell-m0u5za'
} ,
row : 'row-blmd4s' ,
cell : 'cell-m0u5za' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-83144ca8-aace-401e-8aa5-c05928a8ccf0' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-blmd4s' ,
cell : 'cell-1v8s8t'
} ,
row : 'row-blmd4s' ,
cell : 'cell-1v8s8t' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-9a493387-d27f-4b58-b2f7-731dfafda32a' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-blmd4s' ,
cell : 'cell-126947'
} ,
row : 'row-blmd4s' ,
cell : 'cell-126947' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-3484f86e-ae42-440f-8de6-857f0d8011ea' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-hmmljo' ,
cell : 'cell-wvutl9'
} ,
row : 'row-hmmljo' ,
cell : 'cell-wvutl9' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d4e0b741-9dea-47a5-85e1-4ded0efbc89d' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-hmmljo' ,
cell : 'cell-nkablr'
} ,
row : 'row-hmmljo' ,
cell : 'cell-nkablr' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-352f0d5a-d1b9-422f-b136-4bacefd00b1a' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-hmmljo' ,
cell : 'cell-n8xtd0'
} ,
row : 'row-hmmljo' ,
cell : 'cell-n8xtd0' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-95823e57-f29c-44cf-a69d-2b4494b7144b' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ev4xwq' ,
cell : 'cell-ua9bvu'
} ,
row : 'row-ev4xwq' ,
cell : 'cell-ua9bvu' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-cde5c027-15d3-4780-9e76-1e1a9d97a8e8' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ev4xwq' ,
cell : 'cell-7bwuvk'
} ,
row : 'row-ev4xwq' ,
cell : 'cell-7bwuvk' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-11a23ed4-b04d-4e45-8065-8120889cd4a4' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ev4xwq' ,
cell : 'cell-aouka5'
} ,
row : 'row-ev4xwq' ,
cell : 'cell-aouka5' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-15b4483c-da98-4ded-91d3-c3d6ebc82582' }
} ,
{ insert : { divider : true } } ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-68552c8e-b57b-4f4a-9f36-6cc1ef6b3461' }
} ,
{ insert : 'jklasjdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-c8b2df7d-8ec5-4dd4-81f1-8d8efc40b1b4' ,
list : { list : 'toggled' , 'toggle-id' : 'list-9ss39s' }
}
} ,
{ insert : 'asdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4f252ceb-14da-49ae-8cbd-69a701d18e2a' ,
list : { list : 'toggled' , 'toggle-id' : 'list-uvo013' }
}
} ,
{ insert : 'adg' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533' ,
list : { list : 'toggled' , 'toggle-id' : 'list-k53iwe' }
}
} ,
{ insert : 'asdfasdfasdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533' ,
list : { list : 'none' } ,
indent : 1
}
} ,
{ insert : 'asdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-f406f76d-f338-4261-abe7-5c9131f7f1ad' ,
list : { list : 'toggled' , 'toggle-id' : 'list-en86ur' }
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-be18141c-9b6b-434e-8fd0-2c214437d560' }
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-36922db3-4af5-48a1-9ea4-0788b3b5d7cf' }
} ,
{ insert : { table _content : true } } ,
{ insert : ' ' } ,
{
insert : {
slash _command : {
id : 'doc_94zq-2436' ,
sessionId : 'hiyrt6fny' ,
replace : '/'
}
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-9d6566a1-be55-4e20-999a-b990bc15e143' }
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4b545085-114d-4d07-844c-789710ec3aab' ,
layout :
'12d887e1-d1a2-4814-a1a3-0c904e950b46_1185cd29-ef1b-45d5-8fda-51a70b704e64' ,
'layout-width' : '0.25'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4d3f2321-33d1-470e-9b7c-d5a683570148' ,
layout :
'12d887e1-d1a2-4814-a1a3-0c904e950b46_75523ea3-c67f-4f5f-a85f-ac7c8fc0a992' ,
'layout-width' : '0.5'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4c7ae1e6-758e-470f-8d7c-ae0325e4ee8a' ,
layout :
'12d887e1-d1a2-4814-a1a3-0c904e950b46_54c740ef-fd7b-48c6-85aa-c14e1bfc9297' ,
'layout-width' : '0.25'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-2d6ff0f4-ff00-42b7-a8e2-b816463d8fb5' }
} ,
{ insert : { divider : true } } ,
{
insert : '\n' ,
attributes : { 'table-col' : { width : '150' } }
} ,
{ insert : '\n' , attributes : { 'table-col' : { width : '154' } } } ,
{
insert : '\n' ,
attributes : { 'table-col' : { width : '150' } }
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-38545d56-224b-464c-b779-51fcec24dbbf' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-q0qfck' ,
cell : 'cell-hmapv4'
} ,
row : 'row-q0qfck' ,
cell : 'cell-hmapv4' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d413a094-5f52-4fd4-a4aa-00774f6fdb44' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-q0qfck' ,
cell : 'cell-c0czb2'
} ,
row : 'row-q0qfck' ,
cell : 'cell-c0czb2' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-ff855cbc-8871-4e0a-9ba7-de0c1c2aa585' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-q0qfck' ,
cell : 'cell-hcpqmm'
} ,
row : 'row-q0qfck' ,
cell : 'cell-hcpqmm' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4841e6ee-fef8-4473-bf04-f5ba62db17f0' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-etopyl' ,
cell : 'cell-0io73v'
} ,
row : 'row-etopyl' ,
cell : 'cell-0io73v' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-adeec631-d4fe-4f38-9d5e-e67ba068bd24' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-etopyl' ,
cell : 'cell-gt2waa'
} ,
row : 'row-etopyl' ,
cell : 'cell-gt2waa' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d38a7308-c858-4ce0-b1f3-0f9092384961' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-etopyl' ,
cell : 'cell-os9ksy'
} ,
row : 'row-etopyl' ,
cell : 'cell-os9ksy' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-a9df6568-1838-40d1-9d16-3c073b6ce169' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-hbx9ri'
} ,
row : 'row-0jwjg3' ,
cell : 'cell-hbx9ri' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-e26a0cf2-fe62-44a5-a4ca-8678a56d62f1' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-yg5m2w'
} ,
row : 'row-0jwjg3' ,
cell : 'cell-yg5m2w' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{ insert : 'a' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-bfbc5ac2-7417-44b9-9aa5-8e36e4095627' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'b' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-f011c089-6389-47c0-8396-7477a29aa56f' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'c' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4497788d-1e02-4fd5-a80a-48b61a6185cb' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'd' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-5d73a2c7-f98b-47c7-a3f5-0d8527962b02' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'e' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-bfda76ee-ffdd-45db-a22e-a6707e11cf68' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'd' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-35242e64-a69d-4cdb-bd85-2a93766bfab4' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{ insert : 'f' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-8baa22c8-491b-4f1b-9502-44179d5ae744' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2' ,
list : 'ordered'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-0jwjg3' ,
cell : 'cell-1azhl2'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-7fa64af0-6974-4205-8cee-529f8bd46852' }
} ,
{ insert : { divider : true } } ,
{ insert : "Brandon's Section" } ,
{
insert : '\n' ,
attributes : {
header : 2 ,
'block-id' : 'block-cf49462c-2370-48ff-969d-576cb32c39a1'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-30ef8361-0dd6-4eee-b4eb-c9012d0e9070' }
} ,
{
insert : {
slash _command : {
id : 'doc_94zq-2436' ,
sessionId : 'x9x08o916' ,
replace : '/'
}
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-166ed856-cf8c-486a-9365-f499b21d91b3' }
} ,
{ insert : { divider : true } } ,
{
insert : '\n' ,
attributes : {
row : 'row-kssn15' ,
rowspan : '1' ,
colspan : '1' ,
'block-id' : 'block-e8079594-4559-4259-98bb-da5280e2a692' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-kssn15' ,
cell : 'cell-qxbksf'
} ,
cell : 'cell-qxbksf'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-70132663-14cc-4701-b5c5-eb99e875e2bd' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-kssn15' ,
cell : 'cell-lsohbx'
} ,
cell : 'cell-lsohbx' ,
row : 'row-kssn15' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-47a3899c-e3c5-4a7a-a8c4-46e0ae73a4fa' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-kssn15' ,
cell : 'cell-hner9k'
} ,
cell : 'cell-hner9k' ,
row : 'row-kssn15' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-0f9e650a-7841-412e-b4f2-5571b6d352c2' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-juxwc0' ,
cell : 'cell-ei4yqp'
} ,
cell : 'cell-ei4yqp' ,
row : 'row-juxwc0' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-53a158a9-8c82-4c82-9d4e-f5298257ca43' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-juxwc0' ,
cell : 'cell-25pf5x'
} ,
cell : 'cell-25pf5x' ,
row : 'row-juxwc0' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-da8ba35e-ce6e-4518-8605-c51d781eb07a' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-juxwc0' ,
cell : 'cell-m8reor'
} ,
cell : 'cell-m8reor' ,
row : 'row-juxwc0' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-2dce37c7-2978-4127-bed0-9549781babcb' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ot4wy5' ,
cell : 'cell-dinh0i'
} ,
cell : 'cell-dinh0i' ,
row : 'row-ot4wy5' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-7b593f8c-4ea3-44b4-8ad9-4a0abffe759b' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ot4wy5' ,
cell : 'cell-d115b2'
} ,
cell : 'cell-d115b2' ,
row : 'row-ot4wy5' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-272c28e6-2bde-4477-9d99-ce35b3045895' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-ot4wy5' ,
cell : 'cell-fuapvo'
} ,
cell : 'cell-fuapvo' ,
row : 'row-ot4wy5' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-fbf23cab-1ce9-4ede-9953-f2f8250004cf' }
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-c3fbb8c9-495c-40b0-b0dd-f6e33dd64b1b' }
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-3417ad09-92a3-4a43-b5db-6dbcb0f16db4' }
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-b9eacdce-4ba3-4e66-8b69-3eace5656057' }
} ,
{ insert : 'Dan Gornstein' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d7c6ae0d-a17c-433e-85fd-5efc52b587fb' ,
header : 1
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-814521bd-0e14-4fbf-b332-799c6452a624' }
} ,
{ insert : 'aaa' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-6aaf4dcf-dc21-45c6-b723-afb25fe0f498' ,
list : { list : 'toggled' , 'toggle-id' : 'list-idl93b' }
}
} ,
{ insert : 'bb' } ,
{
insert : '\n' ,
attributes : {
indent : 1 ,
'block-id' : 'block-3dd75392-fa50-4bfb-ba6b-3b7d6bd3f1a1' ,
list : { list : 'toggled' , 'toggle-id' : 'list-mrq7j2' }
}
} ,
{ insert : 'ccc' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-2528578b-ecda-4f74-9fd7-8741d72dc8b3' ,
indent : 2 ,
list : { list : 'toggled' , 'toggle-id' : 'list-liu7dl' }
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-18bf68c3-9ef3-4874-929c-9b6bb1a00325' }
} ,
{
insert : '\n' ,
attributes : { 'table-col' : { width : '150' } }
} ,
{ insert : '\n' , attributes : { 'table-col' : { width : '150' } } } ,
{
insert : '\n' ,
attributes : { 'table-col' : { width : '150' } }
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d44e74b4-b37f-48e0-b319-6327a6295a57' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie'
} ,
row : 'row-si1nah' ,
cell : 'cell-cpybie' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{ insert : 'aaa' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-3e545ee9-0c9a-42d7-a4d0-833edb8087f3' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie' ,
list : 'toggled' ,
'toggle-id' : 'list-kjl2ik'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie'
}
} ,
{ insert : 'bb' } ,
{
insert : '\n' ,
attributes : {
indent : 1 ,
'block-id' : 'block-5f1225ad-370f-46ab-8f1e-18b277b5095f' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie' ,
list : 'toggled' ,
'toggle-id' : 'list-eei1x5'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie'
}
} ,
{ insert : 'ccc' } ,
{
insert : '\n' ,
attributes : {
indent : 2 ,
'block-id' : 'block-a77fdc11-ad24-431b-9ca2-09e32db94ac2' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie' ,
list : 'toggled' ,
'toggle-id' : 'list-30us3c'
} ,
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d44e74b4-b37f-48e0-b319-6327a6295a57' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-cpybie'
} ,
row : 'row-si1nah' ,
cell : 'cell-cpybie' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-2c274c8a-757d-4892-8db8-1a7999f7ab51' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-al1z64'
} ,
row : 'row-si1nah' ,
cell : 'cell-al1z64' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-85931afe-1879-471c-bb4b-89e7bd517fe9' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-si1nah' ,
cell : 'cell-q186pb'
} ,
row : 'row-si1nah' ,
cell : 'cell-q186pb' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{ insert : 'asdfasdfasdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-6e0522e8-c1eb-4c07-98df-2b07c533a139' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-7x2d1o' ,
cell : 'cell-6eid2t'
} ,
row : 'row-7x2d1o' ,
cell : 'cell-6eid2t' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-4b3d0bd0-9175-45e9-955c-e8164f4b5376' ,
row : 'row-7x2d1o' ,
cell : 'cell-m1alad' ,
rowspan : '1' ,
colspan : '1' ,
list : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-7x2d1o' ,
cell : 'cell-m1alad' ,
list : 'ordered'
}
}
} ,
{ insert : 'asdfasdfasdf' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-08610089-cb05-4366-bb1e-a0787d5b11bf' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-7x2d1o' ,
cell : 'cell-dm1l2p'
} ,
row : 'row-7x2d1o' ,
cell : 'cell-dm1l2p' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-c22b5125-8df3-432f-bd55-5ff456e41b4e' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-o0ujua' ,
cell : 'cell-82g0ca'
} ,
row : 'row-o0ujua' ,
cell : 'cell-82g0ca' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-7c6320e4-acaf-4ab4-8355-c9b00408c9c1' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-o0ujua' ,
cell : 'cell-wv6ozp'
} ,
row : 'row-o0ujua' ,
cell : 'cell-wv6ozp' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-d1bb7bed-e69e-4807-8d20-2d28fef8d08f' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-o0ujua' ,
cell : 'cell-ldt53x'
} ,
row : 'row-o0ujua' ,
cell : 'cell-ldt53x' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-28f28cb8-51a2-4156-acf9-2380e1349745' }
} ,
{ insert : { divider : true } } ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-a1193252-c0c8-47fe-b9f6-32c8b01a1619' }
} ,
{ insert : '\n' , attributes : { 'table-col' : { width : '150' } } } ,
{
insert : '\n\n' ,
attributes : { 'table-col' : { width : '150' } }
} ,
{ insert : '/This is a test.' } ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-14188df0-a63f-4317-9a6d-91b96a7ac9fe' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5ixdvv' ,
cell : 'cell-9tgyed'
} ,
row : 'row-5ixdvv' ,
cell : 'cell-9tgyed' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-7e5ba2af-9903-457d-adf4-2a79be81d823' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5ixdvv' ,
cell : 'cell-xc56e9'
} ,
row : 'row-5ixdvv' ,
cell : 'cell-xc56e9' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-eb6cad93-caf7-4848-8adf-415255139268' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5ixdvv' ,
cell : 'cell-xrze3u'
} ,
row : 'row-5ixdvv' ,
cell : 'cell-xrze3u' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-5bb547a2-6f71-4624-80c7-d0e1318c81a2' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-xbzv98' ,
cell : 'cell-lie0ng'
} ,
row : 'row-xbzv98' ,
cell : 'cell-lie0ng' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-b506de0d-efb6-4bd7-ba8e-2186cc57903e' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-xbzv98' ,
cell : 'cell-s9sow1'
} ,
row : 'row-xbzv98' ,
cell : 'cell-s9sow1' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-42d2ad20-5521-40e3-a88d-fe6906176e61' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-xbzv98' ,
cell : 'cell-nodtcj'
} ,
row : 'row-xbzv98' ,
cell : 'cell-nodtcj' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-7d3e4216-3f68-4dd6-bc77-4a9fad4ba008' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5bqfil' ,
cell : 'cell-c8c0f3'
} ,
row : 'row-5bqfil' ,
cell : 'cell-c8c0f3' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-6671f221-551e-47fb-9b7d-9043b6b12cdc' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5bqfil' ,
cell : 'cell-jvxxif'
} ,
row : 'row-5bqfil' ,
cell : 'cell-jvxxif' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : {
'block-id' : 'block-51e3161b-0437-4fe3-ac4f-129a93a93fc3' ,
'table-cell-line' : {
rowspan : '1' ,
colspan : '1' ,
row : 'row-5bqfil' ,
cell : 'cell-rmjpze'
} ,
row : 'row-5bqfil' ,
cell : 'cell-rmjpze' ,
rowspan : '1' ,
colspan : '1'
}
} ,
{
insert : '\n' ,
attributes : { 'block-id' : 'block-21099df0-afb2-4cd3-834d-bb37800eb06a' }
}
]
const ydoc = new Y . Doc ( )
const ytext = ydoc . getText ( 'id' )
ytext . applyDelta ( initialContent )
const changeEvent = [
{ retain : 90 } ,
{ delete : 4 } ,
{
retain : 1 ,
attributes : {
layout : null ,
'layout-width' : null ,
'block-id' : 'block-9d6566a1-be55-4e20-999a-b990bc15e143'
}
}
]
ytext . applyDelta ( changeEvent )
2025-07-07 20:23:50 +02:00
const delta = ytext . getDelta ( )
2025-05-07 00:35:57 +02:00
t . compare ( delta . ops [ 40 ] . toJSON ( ) , {
2023-03-02 19:08:01 +01:00
insert : '\n' ,
attributes : {
'block-id' : 'block-9d6566a1-be55-4e20-999a-b990bc15e143'
}
} )
}
2021-04-13 22:05:30 +02:00
/ * *
2022-02-04 12:41:13 +01:00
* In this test we are mainly interested in the cleanup behavior and whether the resulting delta makes sense .
* It is fine if the resulting delta is not minimal . But applying the delta to a rich - text editor should result in a
* synced document .
*
2021-04-13 22:05:30 +02:00
* @ param { t . TestCase } tc
* /
export const testDeltaAfterConcurrentFormatting = tc => {
const { text0 , text1 , testConnector } = init ( tc , { users : 2 } )
text0 . insert ( 0 , 'abcde' )
testConnector . flushAllMessages ( )
text0 . format ( 0 , 3 , { bold : true } )
text1 . format ( 2 , 2 , { bold : true } )
2022-02-04 12:41:13 +01:00
/ * *
* @ type { any }
* /
const deltas = [ ]
2021-04-13 22:05:30 +02:00
text1 . observe ( event => {
2025-05-07 00:35:57 +02:00
if ( event . delta . ops . length > 0 ) {
deltas . push ( event . delta . toJSON ( ) )
2022-02-04 12:41:13 +01:00
}
2021-04-13 22:05:30 +02:00
} )
testConnector . flushAllMessages ( )
2025-05-19 17:52:59 +02:00
t . compare ( deltas , [ [ { retain : 2 , attributes : { bold : true } } , { retain : 1 } , { retain : 1 , attributes : { bold : null } } ] ] )
2021-04-13 22:05:30 +02:00
}
2019-04-02 23:08:58 +02:00
/ * *
* @ param { t . TestCase } tc
* /
2019-03-12 01:22:06 +01:00
export const testBasicInsertAndDelete = tc => {
const { users , text0 } = init ( tc , { users : 2 } )
2025-05-07 00:35:57 +02:00
let eventDelta
2018-02-26 02:18:39 +01:00
2018-11-25 03:17:00 +01:00
text0 . observe ( event => {
2025-05-07 00:35:57 +02:00
eventDelta = event . delta
2018-02-26 02:18:39 +01:00
} )
text0 . delete ( 0 , 0 )
t . assert ( true , 'Does not throw when deleting zero elements with position 0' )
text0 . insert ( 0 , 'abc' )
t . assert ( text0 . toString ( ) === 'abc' , 'Basic insert works' )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . fromJSON ( [ { insert : 'abc' } ] ) )
2018-02-26 02:18:39 +01:00
text0 . delete ( 0 , 1 )
t . assert ( text0 . toString ( ) === 'bc' , 'Basic delete works (position 0)' )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . fromJSON ( [ { delete : 1 } ] ) )
2018-02-26 02:18:39 +01:00
text0 . delete ( 1 , 1 )
t . assert ( text0 . toString ( ) === 'b' , 'Basic delete works (position 1)' )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . fromJSON ( [ { retain : 1 } , { delete : 1 } ] ) )
2019-05-19 20:40:11 +02:00
users [ 0 ] . transact ( ( ) => {
text0 . insert ( 0 , '1' )
text0 . delete ( 0 , 1 )
} )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . fromJSON ( [ ] ) )
2019-05-19 20:40:11 +02:00
2019-03-12 01:22:06 +01:00
compare ( users )
}
2018-02-26 02:18:39 +01:00
2019-04-02 23:08:58 +02:00
/ * *
* @ param { t . TestCase } tc
* /
2019-03-12 01:22:06 +01:00
export const testBasicFormat = tc => {
const { users , text0 } = init ( tc , { users : 2 } )
2025-05-07 00:35:57 +02:00
let eventDelta
2018-11-25 03:17:00 +01:00
text0 . observe ( event => {
2025-05-07 00:35:57 +02:00
eventDelta = event . delta
2018-02-26 02:18:39 +01:00
} )
text0 . insert ( 0 , 'abc' , { bold : true } )
t . assert ( text0 . toString ( ) === 'abc' , 'Basic insert with attributes works' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'abc' , { bold : true } ) . done ( ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . insert ( 'abc' , { bold : true } ) )
2018-02-26 02:18:39 +01:00
text0 . delete ( 0 , 1 )
t . assert ( text0 . toString ( ) === 'bc' , 'Basic delete on formatted works (position 0)' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'bc' , { bold : true } ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . delete ( 1 ) )
2018-02-26 02:18:39 +01:00
text0 . delete ( 1 , 1 )
t . assert ( text0 . toString ( ) === 'b' , 'Basic delete works (position 1)' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'b' , { bold : true } ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . retain ( 1 ) . delete ( 1 ) )
2019-03-01 23:26:40 +01:00
text0 . insert ( 0 , 'z' , { bold : true } )
2018-02-26 02:18:39 +01:00
t . assert ( text0 . toString ( ) === 'zb' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'zb' , { bold : true } ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . insert ( 'z' , { bold : true } ) )
2019-04-03 03:08:10 +02:00
// @ts-ignore
2019-05-28 14:18:20 +02:00
t . assert ( text0 . _start . right . right . right . content . str === 'b' , 'Does not insert duplicate attribute marker' )
2018-02-26 02:18:39 +01:00
text0 . insert ( 0 , 'y' )
t . assert ( text0 . toString ( ) === 'yzb' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'y' ) . insert ( 'zb' , { bold : true } ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . insert ( 'y' ) )
2018-02-26 02:18:39 +01:00
text0 . format ( 0 , 2 , { bold : null } )
t . assert ( text0 . toString ( ) === 'yzb' )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) , delta . createTextDelta ( ) . insert ( 'yz' ) . insert ( 'b' , { bold : true } ) )
2025-05-07 00:35:57 +02:00
t . compare ( eventDelta , delta . createTextDelta ( ) . retain ( 1 ) . retain ( 1 , { bold : null } ) )
2019-03-12 01:22:06 +01:00
compare ( users )
}
2019-05-25 13:48:57 +03:00
2024-03-01 11:39:31 +01:00
/ * *
* @ param { t . TestCase } tc
* /
export const testFalsyFormats = tc => {
const { users , text0 } = init ( tc , { users : 2 } )
let delta
text0 . observe ( event => {
2025-05-07 00:35:57 +02:00
delta = event . delta . toJSON ( )
2024-03-01 11:39:31 +01:00
} )
text0 . insert ( 0 , 'abcde' , { falsy : false } )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) . toJSON ( ) , [ { insert : 'abcde' , attributes : { falsy : false } } ] )
2024-03-01 11:39:31 +01:00
t . compare ( delta , [ { insert : 'abcde' , attributes : { falsy : false } } ] )
text0 . format ( 1 , 3 , { falsy : true } )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) . toJSON ( ) , [ { insert : 'a' , attributes : { falsy : false } } , { insert : 'bcd' , attributes : { falsy : true } } , { insert : 'e' , attributes : { falsy : false } } ] )
2024-03-01 11:39:31 +01:00
t . compare ( delta , [ { retain : 1 } , { retain : 3 , attributes : { falsy : true } } ] )
text0 . format ( 2 , 1 , { falsy : false } )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) . toJSON ( ) , [ { insert : 'a' , attributes : { falsy : false } } , { insert : 'b' , attributes : { falsy : true } } , { insert : 'c' , attributes : { falsy : false } } , { insert : 'd' , attributes : { falsy : true } } , { insert : 'e' , attributes : { falsy : false } } ] )
2024-03-01 11:39:31 +01:00
t . compare ( delta , [ { retain : 2 } , { retain : 1 , attributes : { falsy : false } } ] )
compare ( users )
}
2021-01-09 14:45:51 +01:00
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2021-01-09 14:45:51 +01:00
* /
2023-03-10 12:53:48 +01:00
export const testMultilineFormat = _tc => {
2021-01-09 14:45:51 +01:00
const ydoc = new Y . Doc ( )
const testText = ydoc . getText ( 'test' )
testText . insert ( 0 , 'Test\nMulti-line\nFormatting' )
testText . applyDelta ( [
{ retain : 4 , attributes : { bold : true } } ,
{ retain : 1 } , // newline character
{ retain : 10 , attributes : { bold : true } } ,
{ retain : 1 } , // newline character
{ retain : 10 , attributes : { bold : true } }
] )
2025-07-07 20:23:50 +02:00
t . compare ( testText . getDelta ( ) . toJSON ( ) , [
2021-01-09 14:45:51 +01:00
{ insert : 'Test' , attributes : { bold : true } } ,
{ insert : '\n' } ,
{ insert : 'Multi-line' , attributes : { bold : true } } ,
{ insert : '\n' } ,
{ insert : 'Formatting' , attributes : { bold : true } }
] )
}
2021-01-31 18:17:10 +03:00
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2021-01-31 18:17:10 +03:00
* /
2023-03-10 12:53:48 +01:00
export const testNotMergeEmptyLinesFormat = _tc => {
2021-01-31 18:17:10 +03:00
const ydoc = new Y . Doc ( )
const testText = ydoc . getText ( 'test' )
testText . applyDelta ( [
{ insert : 'Text' } ,
{ insert : '\n' , attributes : { title : true } } ,
{ insert : '\nText' } ,
{ insert : '\n' , attributes : { title : true } }
] )
2025-07-07 20:23:50 +02:00
t . compare ( testText . getDelta ( ) . toJSON ( ) , [
2021-01-31 18:17:10 +03:00
{ insert : 'Text' } ,
{ insert : '\n' , attributes : { title : true } } ,
{ insert : '\nText' } ,
{ insert : '\n' , attributes : { title : true } }
] )
}
2022-01-31 14:49:16 -05:00
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2022-01-31 14:49:16 -05:00
* /
2023-03-10 12:53:48 +01:00
export const testPreserveAttributesThroughDelete = _tc => {
2022-01-31 14:49:16 -05:00
const ydoc = new Y . Doc ( )
2022-02-03 15:19:57 -05:00
const testText = ydoc . getText ( 'test' )
2022-01-31 14:49:16 -05:00
testText . applyDelta ( [
{ insert : 'Text' } ,
{ insert : '\n' , attributes : { title : true } } ,
{ insert : '\n' }
] )
testText . applyDelta ( [
{ retain : 4 } ,
{ delete : 1 } ,
2022-02-03 15:19:57 -05:00
{ retain : 1 , attributes : { title : true } }
2022-01-31 14:49:16 -05:00
] )
2025-07-07 20:23:50 +02:00
t . compare ( testText . getDelta ( ) . toJSON ( ) , [
2022-01-31 14:49:16 -05:00
{ insert : 'Text' } ,
2022-02-03 15:19:57 -05:00
{ insert : '\n' , attributes : { title : true } }
2022-01-31 14:49:16 -05:00
] )
}
2019-05-25 13:48:57 +03:00
/ * *
* @ param { t . TestCase } tc
* /
export const testGetDeltaWithEmbeds = tc => {
2019-05-28 14:18:20 +02:00
const { text0 } = init ( tc , { users : 1 } )
2019-05-25 13:48:57 +03:00
text0 . applyDelta ( [ {
2019-10-25 23:44:09 +02:00
insert : { linebreak : 's' }
2019-05-25 13:48:57 +03:00
} ] )
2025-07-07 20:23:50 +02:00
t . compare ( text0 . getDelta ( ) . toJSON ( ) , [ {
2019-10-25 23:44:09 +02:00
insert : { linebreak : 's' }
2019-05-25 13:48:57 +03:00
} ] )
}
2019-08-31 22:42:18 +02:00
2021-09-25 11:51:08 +02:00
/ * *
* @ param { t . TestCase } tc
* /
export const testTypesAsEmbed = tc => {
const { text0 , text1 , testConnector } = init ( tc , { users : 2 } )
text0 . applyDelta ( [ {
2021-11-06 15:55:59 +01:00
insert : new Y . Map ( [ [ 'key' , 'val' ] ] )
2021-09-25 11:51:08 +02:00
} ] )
2025-07-07 20:23:50 +02:00
t . compare ( /** @type {delta.InsertEmbedOp<any>} */ ( text0 . getDelta ( ) . ops [ 0 ] ) . insert . toJSON ( ) , { key : 'val' } )
2021-09-25 11:51:08 +02:00
let firedEvent = false
text1 . observe ( event => {
const d = event . delta
2025-05-07 00:35:57 +02:00
t . assert ( d . ops . length === 1 )
t . compare ( d . ops . map ( x => /** @type {any} */ ( x ) . insert . toJSON ( ) ) , [ { key : 'val' } ] )
2021-09-25 11:51:08 +02:00
firedEvent = true
} )
testConnector . flushAllMessages ( )
2025-07-07 20:23:50 +02:00
const delta = text1 . getDelta ( ) . toJSON ( )
2021-09-25 11:51:08 +02:00
t . assert ( delta . length === 1 )
2025-05-07 00:35:57 +02:00
t . compare ( /** @type {any} */ ( delta [ 0 ] ) . insert . toJSON ( ) , { key : 'val' } )
2021-09-25 11:51:08 +02:00
t . assert ( firedEvent , 'fired the event observer containing a Type-Embed' )
}
2019-08-31 22:42:18 +02:00
/ * *
* @ param { t . TestCase } tc
* /
export const testSnapshot = tc => {
const { text0 } = init ( tc , { users : 1 } )
const doc0 = /** @type {Y.Doc} */ ( text0 . doc )
doc0 . gc = false
text0 . applyDelta ( [ {
insert : 'abcd'
} ] )
2019-09-03 16:33:29 +02:00
const snapshot1 = Y . snapshot ( doc0 )
2019-08-31 22:42:18 +02:00
text0 . applyDelta ( [ {
retain : 1
} , {
insert : 'x'
} , {
delete : 1
} ] )
2019-09-03 16:33:29 +02:00
const snapshot2 = Y . snapshot ( doc0 )
2019-08-31 22:42:18 +02:00
text0 . applyDelta ( [ {
retain : 2
} , {
delete : 3
} , {
insert : 'x'
} , {
delete : 1
} ] )
2025-07-07 20:23:50 +02:00
const state1 = text0 . getDelta ( createAttributionManagerFromSnapshots ( snapshot1 ) )
2025-05-07 00:35:57 +02:00
t . compare ( state1 . toJSON ( ) , [ { insert : 'abcd' } ] )
2025-07-07 20:23:50 +02:00
const state2 = text0 . getDelta ( createAttributionManagerFromSnapshots ( snapshot2 ) )
2025-05-07 00:35:57 +02:00
t . compare ( state2 . toJSON ( ) , [ { insert : 'axcd' } ] )
2025-07-07 20:23:50 +02:00
const state2Diff = text0 . getDelta ( createAttributionManagerFromSnapshots ( snapshot1 , snapshot2 ) ) . toJSON ( )
2025-05-07 00:35:57 +02:00
const expected = [ { insert : 'a' } , { insert : 'x' , attribution : { insert : [ ] } } , { insert : 'b' , attribution : { delete : [ ] } } , { insert : 'cd' } ]
t . compare ( state2Diff , expected )
2019-08-31 22:42:18 +02:00
}
/ * *
* @ param { t . TestCase } tc
* /
export const testSnapshotDeleteAfter = tc => {
const { text0 } = init ( tc , { users : 1 } )
const doc0 = /** @type {Y.Doc} */ ( text0 . doc )
doc0 . gc = false
text0 . applyDelta ( [ {
insert : 'abcd'
} ] )
2019-09-03 16:33:29 +02:00
const snapshot1 = Y . snapshot ( doc0 )
2019-08-31 22:42:18 +02:00
text0 . applyDelta ( [ {
retain : 4
} , {
insert : 'e'
} ] )
2025-07-07 20:23:50 +02:00
const state1 = text0 . getDelta ( createAttributionManagerFromSnapshots ( snapshot1 ) )
2025-05-07 00:35:57 +02:00
t . compare ( state1 , delta . fromJSON ( [ { insert : 'abcd' } ] ) )
2019-08-31 22:42:18 +02:00
}
2020-01-22 11:11:42 +01:00
/ * *
* @ param { t . TestCase } tc
* /
export const testToJson = tc => {
2020-01-22 16:42:16 +01:00
const { text0 } = init ( tc , { users : 1 } )
2020-01-22 11:11:42 +01:00
text0 . insert ( 0 , 'abc' , { bold : true } )
t . assert ( text0 . toJSON ( ) === 'abc' , 'toJSON returns the unformatted text' )
}
2020-03-31 16:06:28 +01:00
/ * *
* @ param { t . TestCase } tc
* /
export const testToDeltaEmbedAttributes = tc => {
const { text0 } = init ( tc , { users : 1 } )
2020-04-01 23:39:27 +02:00
text0 . insert ( 0 , 'ab' , { bold : true } )
text0 . insertEmbed ( 1 , { image : 'imageSrc.png' } , { width : 100 } )
2025-07-07 20:23:50 +02:00
const delta0 = text0 . getDelta ( ) . toJSON ( )
2020-04-01 23:39:27 +02:00
t . compare ( delta0 , [ { insert : 'a' , attributes : { bold : true } } , { insert : { image : 'imageSrc.png' } , attributes : { width : 100 } } , { insert : 'b' , attributes : { bold : true } } ] )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testToDeltaEmbedNoAttributes = tc => {
const { text0 } = init ( tc , { users : 1 } )
text0 . insert ( 0 , 'ab' , { bold : true } )
text0 . insertEmbed ( 1 , { image : 'imageSrc.png' } )
2025-07-07 20:23:50 +02:00
const delta0 = text0 . getDelta ( ) . toJSON ( )
2020-04-01 23:39:27 +02:00
t . compare ( delta0 , [ { insert : 'a' , attributes : { bold : true } } , { insert : { image : 'imageSrc.png' } } , { insert : 'b' , attributes : { bold : true } } ] , 'toDelta does not set attributes key when no attributes are present' )
2020-03-31 16:06:28 +01:00
}
2020-05-11 01:45:27 +02:00
/ * *
* @ param { t . TestCase } tc
* /
export const testFormattingRemoved = tc => {
const { text0 } = init ( tc , { users : 1 } )
text0 . insert ( 0 , 'ab' , { bold : true } )
text0 . delete ( 0 , 2 )
t . assert ( Y . getTypeChildren ( text0 ) . length === 1 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testFormattingRemovedInMidText = tc => {
const { text0 } = init ( tc , { users : 1 } )
text0 . insert ( 0 , '1234' )
text0 . insert ( 2 , 'ab' , { bold : true } )
text0 . delete ( 2 , 2 )
t . assert ( Y . getTypeChildren ( text0 ) . length === 3 )
}
2021-11-06 14:35:04 +01:00
/ * *
* Reported in https : //github.com/yjs/yjs/issues/344
*
* @ param { t . TestCase } tc
* /
export const testFormattingDeltaUnnecessaryAttributeChange = tc => {
const { text0 , text1 , testConnector } = init ( tc , { users : 2 } )
text0 . insert ( 0 , '\n' , {
PARAGRAPH _STYLES : 'normal' ,
LIST _STYLES : 'bullet'
} )
text0 . insert ( 1 , 'abc' , {
PARAGRAPH _STYLES : 'normal'
} )
testConnector . flushAllMessages ( )
/ * *
2025-05-07 22:43:23 +02:00
* @ type { Array < delta . TextDelta < any >> }
2021-11-06 14:35:04 +01:00
* /
const deltas = [ ]
text0 . observe ( event => {
deltas . push ( event . delta )
} )
text1 . observe ( event => {
deltas . push ( event . delta )
} )
text1 . format ( 0 , 1 , { LIST _STYLES : 'number' } )
testConnector . flushAllMessages ( )
2025-05-07 00:35:57 +02:00
const filteredDeltas = deltas . filter ( d => d . ops . length > 0 )
2021-11-06 14:35:04 +01:00
t . assert ( filteredDeltas . length === 2 )
2025-05-07 00:35:57 +02:00
t . compare ( filteredDeltas [ 0 ] . toJSON ( ) , [
2021-11-06 14:35:04 +01:00
{ retain : 1 , attributes : { LIST _STYLES : 'number' } }
] )
t . compare ( filteredDeltas [ 0 ] , filteredDeltas [ 1 ] )
}
2020-07-15 22:03:02 +02:00
/ * *
* @ param { t . TestCase } tc
* /
export const testInsertAndDeleteAtRandomPositions = tc => {
2020-07-16 20:44:54 +02:00
const N = 100000
2020-07-15 22:03:02 +02:00
const { text0 } = init ( tc , { users : 1 } )
const gen = tc . prng
// create initial content
// let expectedResult = init
text0 . insert ( 0 , prng . word ( gen , N / 2 , N / 2 ) )
// apply changes
for ( let i = 0 ; i < N ; i ++ ) {
const pos = prng . uint32 ( gen , 0 , text0 . length )
if ( prng . bool ( gen ) ) {
const len = prng . uint32 ( gen , 1 , 5 )
const word = prng . word ( gen , 0 , len )
text0 . insert ( pos , word )
// expectedResult = expectedResult.slice(0, pos) + word + expectedResult.slice(pos)
} else {
const len = prng . uint32 ( gen , 0 , math . min ( 3 , text0 . length - pos ) )
text0 . delete ( pos , len )
// expectedResult = expectedResult.slice(0, pos) + expectedResult.slice(pos + len)
}
}
// t.compareStrings(text0.toString(), expectedResult)
t . describe ( 'final length' , '' + text0 . length )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testAppendChars = tc => {
const N = 10000
const { text0 } = init ( tc , { users : 1 } )
// apply changes
for ( let i = 0 ; i < N ; i ++ ) {
text0 . insert ( text0 . length , 'a' )
}
t . assert ( text0 . length === N )
}
2020-10-31 02:05:33 +01:00
const largeDocumentSize = 100000
2020-07-13 15:47:51 +02:00
const id = Y . createID ( 0 , 0 )
const c = new Y . ContentString ( 'a' )
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2020-07-13 15:47:51 +02:00
* /
2023-03-10 12:53:48 +01:00
export const testBestCase = _tc => {
2020-10-31 02:05:33 +01:00
const N = largeDocumentSize
2020-07-13 15:47:51 +02:00
const items = new Array ( N )
t . measureTime ( 'time to create two million items in the best case' , ( ) => {
const parent = /** @type {any} */ ( { } )
let prevItem = null
for ( let i = 0 ; i < N ; i ++ ) {
/ * *
* @ type { Y . Item }
* /
const n = new Y . Item ( Y . createID ( 0 , 0 ) , null , null , null , null , null , null , c )
// items.push(n)
items [ i ] = n
n . right = prevItem
n . rightOrigin = prevItem ? id : null
n . content = c
n . parent = parent
prevItem = n
}
} )
const newArray = new Array ( N )
t . measureTime ( 'time to copy two million items to new Array' , ( ) => {
for ( let i = 0 ; i < N ; i ++ ) {
newArray [ i ] = items [ i ]
}
} )
}
2020-06-09 16:34:07 +02:00
const tryGc = ( ) => {
2021-01-09 14:45:51 +01:00
// @ts-ignore
2020-06-09 16:34:07 +02:00
if ( typeof global !== 'undefined' && global . gc ) {
2021-01-09 14:45:51 +01:00
// @ts-ignore
2020-06-09 16:34:07 +02:00
global . gc ( )
}
}
2020-06-02 23:20:45 +02:00
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2020-06-05 00:27:36 +02:00
* /
2023-03-10 12:53:48 +01:00
export const testLargeFragmentedDocument = _tc => {
2020-10-31 02:05:33 +01:00
const itemsToInsert = largeDocumentSize
2020-06-09 00:53:05 +02:00
let update = /** @type {any} */ ( null )
; ( ( ) => {
const doc1 = new Y . Doc ( )
const text0 = doc1 . getText ( 'txt' )
2020-06-09 16:34:07 +02:00
tryGc ( )
t . measureTime ( ` time to insert ${ itemsToInsert } items ` , ( ) => {
2020-06-09 00:53:05 +02:00
doc1 . transact ( ( ) => {
for ( let i = 0 ; i < itemsToInsert ; i ++ ) {
text0 . insert ( 0 , '0' )
}
} )
} )
2020-06-09 16:34:07 +02:00
tryGc ( )
t . measureTime ( 'time to encode document' , ( ) => {
2020-07-12 18:25:45 +02:00
update = Y . encodeStateAsUpdateV2 ( doc1 )
2020-06-09 00:53:05 +02:00
} )
2020-07-12 18:25:45 +02:00
t . describe ( 'Document size:' , update . byteLength )
2020-06-09 00:53:05 +02:00
} ) ( )
; ( ( ) => {
const doc2 = new Y . Doc ( )
2020-06-09 16:34:07 +02:00
tryGc ( )
t . measureTime ( ` time to apply ${ itemsToInsert } updates ` , ( ) => {
2020-07-12 18:25:45 +02:00
Y . applyUpdateV2 ( doc2 , update )
2020-06-09 00:53:05 +02:00
} )
} ) ( )
2020-06-02 23:20:45 +02:00
}
2021-02-08 13:46:22 +01:00
/ * *
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2021-02-08 13:46:22 +01:00
* /
2023-03-10 12:53:48 +01:00
export const testIncrementalUpdatesPerformanceOnLargeFragmentedDocument = _tc => {
2021-02-08 13:46:22 +01:00
const itemsToInsert = largeDocumentSize
const updates = /** @type {Array<Uint8Array>} */ ( [ ] )
; ( ( ) => {
const doc1 = new Y . Doc ( )
doc1 . on ( 'update' , update => {
updates . push ( update )
} )
const text0 = doc1 . getText ( 'txt' )
tryGc ( )
t . measureTime ( ` time to insert ${ itemsToInsert } items ` , ( ) => {
doc1 . transact ( ( ) => {
for ( let i = 0 ; i < itemsToInsert ; i ++ ) {
text0 . insert ( 0 , '0' )
}
} )
} )
tryGc ( )
} ) ( )
; ( ( ) => {
t . measureTime ( ` time to merge ${ itemsToInsert } updates (differential updates) ` , ( ) => {
Y . mergeUpdates ( updates )
} )
tryGc ( )
t . measureTime ( ` time to merge ${ itemsToInsert } updates (ydoc updates) ` , ( ) => {
const ydoc = new Y . Doc ( )
updates . forEach ( update => {
Y . applyUpdate ( ydoc , update )
} )
} )
} ) ( )
}
2020-10-31 02:05:33 +01:00
/ * *
* Splitting surrogates can lead to invalid encoded documents .
*
* https : //github.com/yjs/yjs/issues/248
*
* @ param { t . TestCase } tc
* /
export const testSplitSurrogateCharacter = tc => {
{
const { users , text0 } = init ( tc , { users : 2 } )
users [ 1 ] . disconnect ( ) // disconnecting forces the user to encode the split surrogate
text0 . insert ( 0 , '👾' ) // insert surrogate character
// split surrogate, which should not lead to an encoding error
text0 . insert ( 1 , 'hi!' )
compare ( users )
}
{
const { users , text0 } = init ( tc , { users : 2 } )
users [ 1 ] . disconnect ( ) // disconnecting forces the user to encode the split surrogate
text0 . insert ( 0 , '👾👾' ) // insert surrogate character
// partially delete surrogate
text0 . delete ( 1 , 2 )
compare ( users )
}
{
const { users , text0 } = init ( tc , { users : 2 } )
users [ 1 ] . disconnect ( ) // disconnecting forces the user to encode the split surrogate
text0 . insert ( 0 , '👾👾' ) // insert surrogate character
// formatting will also split surrogates
text0 . format ( 1 , 2 , { bold : true } )
compare ( users )
}
}
2021-05-31 17:54:24 +02:00
/ * *
* Search marker bug https : //github.com/yjs/yjs/issues/307
*
* @ param { t . TestCase } tc
* /
export const testSearchMarkerBug1 = tc => {
const { users , text0 , text1 , testConnector } = init ( tc , { users : 2 } )
users [ 0 ] . on ( 'update' , update => {
users [ 0 ] . transact ( ( ) => {
Y . applyUpdate ( users [ 0 ] , update )
} )
} )
users [ 0 ] . on ( 'update' , update => {
users [ 1 ] . transact ( ( ) => {
Y . applyUpdate ( users [ 1 ] , update )
} )
} )
text0 . insert ( 0 , 'a_a' )
testConnector . flushAllMessages ( )
text0 . insert ( 2 , 's' )
testConnector . flushAllMessages ( )
text1 . insert ( 3 , 'd' )
testConnector . flushAllMessages ( )
text0 . delete ( 0 , 5 )
testConnector . flushAllMessages ( )
text0 . insert ( 0 , 'a_a' )
testConnector . flushAllMessages ( )
text0 . insert ( 2 , 's' )
testConnector . flushAllMessages ( )
text1 . insert ( 3 , 'd' )
testConnector . flushAllMessages ( )
t . compareStrings ( text0 . toString ( ) , text1 . toString ( ) )
t . compareStrings ( text0 . toString ( ) , 'a_sda' )
compare ( users )
}
2021-10-14 16:09:23 +02:00
/ * *
* Reported in https : //github.com/yjs/yjs/pull/32
*
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2021-10-14 16:09:23 +02:00
* /
2023-03-10 12:53:48 +01:00
export const testFormattingBug = async _tc => {
2021-10-14 16:09:23 +02:00
const ydoc1 = new Y . Doc ( )
const ydoc2 = new Y . Doc ( )
const text1 = ydoc1 . getText ( )
text1 . insert ( 0 , '\n\n\n' )
text1 . format ( 0 , 3 , { url : 'http://example.com' } )
ydoc1 . getText ( ) . format ( 1 , 1 , { url : 'http://docs.yjs.dev' } )
ydoc2 . getText ( ) . format ( 1 , 1 , { url : 'http://docs.yjs.dev' } )
Y . applyUpdate ( ydoc2 , Y . encodeStateAsUpdate ( ydoc1 ) )
const text2 = ydoc2 . getText ( )
const expectedResult = [
{ insert : '\n' , attributes : { url : 'http://example.com' } } ,
{ insert : '\n' , attributes : { url : 'http://docs.yjs.dev' } } ,
{ insert : '\n' , attributes : { url : 'http://example.com' } }
]
2025-07-07 20:23:50 +02:00
t . compare ( text1 . getDelta ( ) . toJSON ( ) , expectedResult )
t . compare ( text1 . getDelta ( ) . toJSON ( ) , text2 . getDelta ( ) . toJSON ( ) )
console . log ( text1 . getDelta ( ) . toJSON ( ) )
2021-10-14 16:09:23 +02:00
}
2022-02-23 14:47:04 +01:00
/ * *
* Delete formatting should not leave redundant formatting items .
*
2023-03-10 12:53:48 +01:00
* @ param { t . TestCase } _tc
2022-02-23 14:47:04 +01:00
* /
2023-03-10 12:53:48 +01:00
export const testDeleteFormatting = _tc => {
2022-02-23 14:47:04 +01:00
const doc = new Y . Doc ( )
const text = doc . getText ( )
text . insert ( 0 , 'Attack ships on fire off the shoulder of Orion.' )
const doc2 = new Y . Doc ( )
const text2 = doc2 . getText ( )
Y . applyUpdate ( doc2 , Y . encodeStateAsUpdate ( doc ) )
text . format ( 13 , 7 , { bold : true } )
Y . applyUpdate ( doc2 , Y . encodeStateAsUpdate ( doc ) )
text . format ( 16 , 4 , { bold : null } )
Y . applyUpdate ( doc2 , Y . encodeStateAsUpdate ( doc ) )
const expected = [
{ insert : 'Attack ships ' } ,
{ insert : 'on ' , attributes : { bold : true } } ,
{ insert : 'fire off the shoulder of Orion.' }
]
2025-07-07 20:23:50 +02:00
t . compare ( text . getDelta ( ) . toJSON ( ) , expected )
t . compare ( text2 . getDelta ( ) . toJSON ( ) , expected )
2022-02-23 14:47:04 +01:00
}
2025-04-21 02:00:14 +02:00
/ * *
2025-04-21 18:27:18 +02:00
* @ param { t . TestCase } _tc
2025-04-21 02:00:14 +02:00
* /
2025-04-21 18:27:18 +02:00
export const testAttributedContent = _tc => {
2025-04-21 15:59:03 +02:00
const ydoc = new Y . Doc ( { gc : false } )
2025-04-21 02:00:14 +02:00
const ytext = ydoc . getText ( )
ytext . insert ( 0 , 'Hello World!' )
2025-04-22 23:13:57 +02:00
let attributionManager = noAttributionsManager
2025-04-21 02:00:14 +02:00
ydoc . on ( 'afterTransaction' , tr => {
2025-04-22 23:13:57 +02:00
// 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 TwosetAttributionManager ( createIdMapFromIdSet ( tr . insertSet , [ ] ) , createIdMapFromIdSet ( tr . deleteSet , [ ] ) )
2025-04-21 02:00:14 +02:00
} )
2025-04-21 18:27:18 +02:00
t . group ( 'insert / delete / format' , ( ) => {
ytext . applyDelta ( [ { retain : 4 , attributes : { italic : true } } , { retain : 2 } , { delete : 5 } , { insert : 'attributions' } ] )
2025-04-29 18:02:15 +02:00
const expectedContent = delta . createTextDelta ( ) . insert ( 'Hell' , { italic : true } , { attributes : { italic : [ ] } } ) . insert ( 'o ' ) . insert ( 'World' , { } , { delete : [ ] } ) . insert ( 'attributions' , { } , { insert : [ ] } ) . insert ( '!' )
2025-07-07 20:23:50 +02:00
const attributedContent = ytext . getDelta ( attributionManager )
2025-05-07 22:43:23 +02:00
console . log ( attributedContent . toJSON ( ) )
2025-04-21 18:27:18 +02:00
t . assert ( attributedContent . equals ( expectedContent ) )
} )
t . group ( 'unformat' , ( ) => {
2025-04-29 18:02:15 +02:00
ytext . applyDelta ( [ { retain : 5 , attributes : { italic : null } } ] )
2025-05-19 17:52:59 +02:00
const expectedContent = delta . createTextDelta ( ) . insert ( 'Hell' , null , { attributes : { italic : [ ] } } ) . insert ( 'o attributions!' )
2025-07-07 20:23:50 +02:00
const attributedContent = ytext . getDelta ( attributionManager )
2025-05-07 22:43:23 +02:00
console . log ( attributedContent . toJSON ( ) )
2025-04-21 18:27:18 +02:00
t . assert ( attributedContent . equals ( expectedContent ) )
} )
2025-04-21 02:00:14 +02:00
}
2025-04-24 00:03:00 +02:00
/ * *
* @ param { t . TestCase } _tc
* /
export const testAttributedDiffing = _tc => {
const ydocVersion0 = new Y . Doc ( { gc : false } )
ydocVersion0 . clientID = 0
ydocVersion0 . getText ( ) . insert ( 0 , 'Hello World!' )
const ydoc = new Y . Doc ( { gc : false } )
ydoc . clientID = 1
Y . applyUpdate ( ydoc , Y . encodeStateAsUpdate ( ydocVersion0 ) )
const ytext = ydoc . getText ( )
ytext . applyDelta ( [ { retain : 4 , attributes : { italic : true } } , { retain : 2 } , { delete : 5 } , { insert : 'attributions' } ] )
// this represents to all insertions of ydoc
2025-05-01 14:44:24 +02:00
const insertionSet = Y . createInsertionSetFromStructStore ( ydoc . store , false )
2025-04-24 00:03:00 +02:00
const deleteSet = Y . createDeleteSetFromStructStore ( ydoc . store )
// exclude the changes from `ydocVersion0`
2025-05-01 14:44:24 +02:00
const insertionSetDiff = Y . diffIdSet ( insertionSet , Y . createInsertionSetFromStructStore ( ydocVersion0 . store , false ) )
2025-04-24 00:03:00 +02:00
const deleteSetDiff = Y . diffIdSet ( deleteSet , Y . createDeleteSetFromStructStore ( ydocVersion0 . store ) )
// assign attributes to the diff
const attributedInsertions = createIdMapFromIdSet ( insertionSetDiff , [ new Y . Attribution ( 'insert' , 'Bob' ) ] )
const attributedDeletions = createIdMapFromIdSet ( deleteSetDiff , [ new Y . Attribution ( 'delete' , 'Bob' ) ] )
// now we can define an attribution manager that maps these changes to output. One of the
// implementations is the TwosetAttributionManager
const attributionManager = new TwosetAttributionManager ( attributedInsertions , attributedDeletions )
// we render the attributed content with the attributionManager
2025-07-07 20:23:50 +02:00
const attributedContent = ytext . getDelta ( attributionManager )
2025-05-07 22:43:23 +02:00
console . log ( JSON . stringify ( attributedContent . toJSON ( ) , null , 2 ) )
2025-04-29 18:02:15 +02:00
const expectedContent = delta . createTextDelta ( ) . insert ( 'Hell' , { italic : true } , { attributes : { italic : [ 'Bob' ] } } ) . insert ( 'o ' ) . insert ( 'World' , { } , { delete : [ 'Bob' ] } ) . insert ( 'attributions' , { } , { insert : [ 'Bob' ] } ) . insert ( '!' )
2025-04-24 00:03:00 +02:00
t . assert ( attributedContent . equals ( expectedContent ) )
console . log ( Y . encodeIdMap ( attributedInsertions ) . length )
}
2020-05-11 01:45:27 +02:00
// RANDOM TESTS
let charCounter = 0
2020-07-15 22:03:02 +02:00
/ * *
* Random tests for pure text operations without formatting .
*
* @ type Array < function ( any , prng . PRNG ) : void >
* /
const textChanges = [
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // insert text
const ytext = y . getText ( 'text' )
const insertPos = prng . int32 ( gen , 0 , ytext . length )
const text = charCounter ++ + prng . word ( gen )
const prevText = ytext . toString ( )
ytext . insert ( insertPos , text )
t . compareStrings ( ytext . toString ( ) , prevText . slice ( 0 , insertPos ) + text + prevText . slice ( insertPos ) )
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // delete text
const ytext = y . getText ( 'text' )
const contentLen = ytext . toString ( ) . length
const insertPos = prng . int32 ( gen , 0 , contentLen )
const overwrite = math . min ( prng . int32 ( gen , 0 , contentLen - insertPos ) , 2 )
const prevText = ytext . toString ( )
ytext . delete ( insertPos , overwrite )
t . compareStrings ( ytext . toString ( ) , prevText . slice ( 0 , insertPos ) + prevText . slice ( insertPos + overwrite ) )
}
]
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges5 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 5 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges30 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 30 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges40 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 40 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges50 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 50 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges70 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 70 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges90 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 90 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateTextChanges300 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , textChanges , 300 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
2020-05-11 01:45:27 +02:00
const marks = [
{ bold : true } ,
{ italic : true } ,
{ italic : true , color : '#888' }
]
const marksChoices = [
undefined ,
... marks
]
/ * *
2020-07-15 22:03:02 +02:00
* Random tests for all features of y - text ( formatting , embeds , . . ) .
*
2020-05-11 01:45:27 +02:00
* @ type Array < function ( any , prng . PRNG ) : void >
* /
const qChanges = [
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // insert text
const ytext = y . getText ( 'text' )
2020-07-15 22:03:02 +02:00
const insertPos = prng . int32 ( gen , 0 , ytext . length )
2020-05-11 01:45:27 +02:00
const attrs = prng . oneOf ( gen , marksChoices )
const text = charCounter ++ + prng . word ( gen )
ytext . insert ( insertPos , text , attrs )
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // insert embed
const ytext = y . getText ( 'text' )
2020-07-15 22:03:02 +02:00
const insertPos = prng . int32 ( gen , 0 , ytext . length )
2021-09-25 11:51:08 +02:00
if ( prng . bool ( gen ) ) {
ytext . insertEmbed ( insertPos , { image : 'https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png' } )
} else {
2021-11-06 15:55:59 +01:00
ytext . insertEmbed ( insertPos , new Y . Map ( [ [ prng . word ( gen ) , prng . word ( gen ) ] ] ) )
2021-09-25 11:51:08 +02:00
}
2020-05-11 01:45:27 +02:00
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // delete text
const ytext = y . getText ( 'text' )
const contentLen = ytext . toString ( ) . length
const insertPos = prng . int32 ( gen , 0 , contentLen )
const overwrite = math . min ( prng . int32 ( gen , 0 , contentLen - insertPos ) , 2 )
ytext . delete ( insertPos , overwrite )
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // format text
const ytext = y . getText ( 'text' )
const contentLen = ytext . toString ( ) . 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 )
ytext . format ( insertPos , overwrite , format )
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // insert codeblock
const ytext = y . getText ( 'text' )
const insertPos = prng . int32 ( gen , 0 , ytext . toString ( ) . length )
const text = charCounter ++ + prng . word ( gen )
const ops = [ ]
if ( insertPos > 0 ) {
ops . push ( { retain : insertPos } )
}
ops . push ( { insert : text } , { insert : '\n' , format : { 'code-block' : true } } )
ytext . applyDelta ( ops )
2023-03-10 12:53:48 +01:00
} ,
/ * *
* @ param { Y . Doc } y
* @ param { prng . PRNG } gen
* /
( y , gen ) => { // complex delta op
const ytext = y . getText ( 'text' )
const contentLen = ytext . toString ( ) . length
let currentPos = math . max ( 0 , prng . int32 ( gen , 0 , contentLen - 1 ) )
/ * *
* @ type { Array < any > }
* /
const ops = currentPos > 0 ? [ { retain : currentPos } ] : [ ]
// create max 3 ops
for ( let i = 0 ; i < 7 && currentPos < contentLen ; i ++ ) {
prng . oneOf ( gen , [
( ) => { // format
const retain = math . min ( prng . int32 ( gen , 0 , contentLen - currentPos ) , 5 )
const format = prng . oneOf ( gen , marks )
ops . push ( { retain , attributes : format } )
currentPos += retain
} ,
( ) => { // insert
const attrs = prng . oneOf ( gen , marksChoices )
const text = prng . word ( gen , 1 , 3 )
ops . push ( { insert : text , attributes : attrs } )
} ,
( ) => { // delete
const delLen = math . min ( prng . int32 ( gen , 0 , contentLen - currentPos ) , 10 )
ops . push ( { delete : delLen } )
currentPos += delLen
}
] ) ( )
}
ytext . applyDelta ( ops )
2020-05-11 01:45:27 +02:00
}
]
/ * *
* @ param { any } result
* /
const checkResult = result => {
for ( let i = 1 ; i < result . testObjects . length ; i ++ ) {
2021-09-25 11:51:08 +02:00
/ * *
* @ param { any } d
* /
const typeToObject = d => d . insert instanceof Y . AbstractType ? d . insert . toJSON ( ) : d
2025-04-22 23:13:57 +02:00
2025-04-29 18:02:15 +02:00
t . info ( 'length of text = ' + result . users [ i - 1 ] . getText ( 'text' ) . length )
2025-04-22 23:13:57 +02:00
t . measureTime ( 'original toDelta perf' , ( ) => {
2025-05-07 00:35:57 +02:00
result . users [ i - 1 ] . getText ( 'text' ) . getContent ( ) . toJSON ( ) . map ( typeToObject )
2025-04-22 23:13:57 +02:00
} )
t . measureTime ( 'getContent(attributionManager) performance)' , ( ) => {
2025-04-29 18:02:15 +02:00
result . users [ i - 1 ] . getText ( 'text' ) . getContent ( )
2025-04-22 23:13:57 +02:00
} )
2025-05-07 00:35:57 +02:00
const p1 = result . users [ i - 1 ] . getText ( 'text' ) . getContent ( ) . toJSON ( ) . map ( typeToObject )
const p2 = result . users [ i ] . getText ( 'text' ) . getContent ( ) . toJSON ( ) . map ( typeToObject )
2020-05-11 01:45:27 +02:00
t . compare ( p1 , p2 )
}
// Uncomment this to find formatting-cleanup issues
// const cleanups = Y.cleanupYTextFormatting(result.users[0].getText('text'))
// t.assert(cleanups === 0)
return result
}
2025-04-24 17:46:00 +02:00
/ * *
2025-04-24 19:49:08 +02:00
* Generally , the content reader with attributed content works better if there is more deleted
* content . Increasing the number of deletions improves performance of getContent ( even to the point
* that is beats toString ( ) , but that might be because of internal js optimization steps ) .
*
2025-04-24 17:46:00 +02:00
* @ param { t . TestCase } tc
* /
export const testAttributionManagerDefaultPerformance = tc => {
2025-05-07 00:35:57 +02:00
const N = 100000
2025-04-24 19:49:08 +02:00
const MaxDeletionLength = 5 // 25% chance of deletion
const MaxInsertionLength = 5
2025-04-24 17:46:00 +02:00
const ydoc = new Y . Doc ( )
const ytext = ydoc . getText ( )
for ( let i = 0 ; i < N ; i ++ ) {
2025-04-24 19:49:08 +02:00
if ( prng . bool ( tc . prng ) && prng . bool ( tc . prng ) && ytext . length > 0 ) {
2025-04-24 17:46:00 +02:00
const index = prng . int31 ( tc . prng , 0 , ytext . length - 1 )
2025-04-24 19:49:08 +02:00
const len = prng . int31 ( tc . prng , 0 , math . min ( ytext . length - index , MaxDeletionLength ) )
2025-04-24 17:46:00 +02:00
ytext . delete ( index , len )
} else {
const index = prng . int31 ( tc . prng , 0 , ytext . length )
2025-04-24 19:49:08 +02:00
const content = prng . utf16String ( tc . prng , MaxInsertionLength )
2025-04-24 17:46:00 +02:00
ytext . insert ( index , content )
}
}
2025-04-29 18:02:15 +02:00
t . info ( ` number of changes: ${ N / 1000 } k ` )
2025-04-24 19:49:08 +02:00
t . info ( ` length of text: ${ ytext . length } ` )
2025-04-24 17:46:00 +02:00
const M = 100
2025-04-24 19:49:08 +02:00
t . measureTime ( ` original toString perf <executed ${ M } times> ` , ( ) => {
for ( let i = 0 ; i < M ; i ++ ) {
2025-05-07 00:35:57 +02:00
ytext . toString ( )
2025-04-24 17:46:00 +02:00
}
} )
2025-04-24 19:49:08 +02:00
t . measureTime ( ` getContent(attributionManager) performance <executed ${ M } times> ` , ( ) => {
2025-04-24 17:46:00 +02:00
for ( let i = 0 ; i < M ; i ++ ) {
2025-07-07 20:23:50 +02:00
ytext . getDelta ( )
2025-04-24 17:46:00 +02:00
}
} )
}
2020-05-11 01:45:27 +02:00
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges1 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , qChanges , 1 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges2 = tc => {
const { users } = checkResult ( Y . applyRandomTests ( tc , qChanges , 2 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges2Repeat = tc => {
for ( let i = 0 ; i < 1000 ; i ++ ) {
const { users } = checkResult ( Y . applyRandomTests ( tc , qChanges , 2 ) )
const cleanups = Y . cleanupYTextFormatting ( users [ 0 ] . getText ( 'text' ) )
t . assert ( cleanups === 0 )
}
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges3 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 3 ) )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges30 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 30 ) )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges40 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 40 ) )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges70 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 70 ) )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges100 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 100 ) )
}
/ * *
* @ param { t . TestCase } tc
* /
export const testRepeatGenerateQuillChanges300 = tc => {
checkResult ( Y . applyRandomTests ( tc , qChanges , 300 ) )
}