Implemented Xml Struct
This commit is contained in:
parent
86c46cf0ec
commit
ab6cde07e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
/y.*
|
/y.*
|
||||||
|
/examples/yjs-dist.js*
|
||||||
|
58
examples/infiniteyjs/index.html
Normal file
58
examples/infiniteyjs/index.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 7px;
|
||||||
|
}
|
||||||
|
.one {
|
||||||
|
grid-column: 1 ;
|
||||||
|
}
|
||||||
|
.two {
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
.three {
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: calc(100% - 10px)
|
||||||
|
}
|
||||||
|
.editor-container {
|
||||||
|
background-color: #4caf50;
|
||||||
|
padding: 4px 5px 10px 5px;
|
||||||
|
border-radius: 11px;
|
||||||
|
}
|
||||||
|
.editor-container[disconnected] {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
.disconnected-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.editor-container[disconnected] .disconnected-info {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div id="container1" class="one editor-container">
|
||||||
|
<h1>Server 1 <span class="disconnected-info">(disconnected)</span></h1>
|
||||||
|
<textarea id="textarea1" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||||
|
</div>
|
||||||
|
<div id="container2" class="two editor-container">
|
||||||
|
<h1>Server 2 <span class="disconnected-info">(disconnected)</span></h1>
|
||||||
|
<textarea id="textarea2" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||||
|
</div>
|
||||||
|
<div id="container3" class="three editor-container">
|
||||||
|
<h1>Server 3 <span class="disconnected-info">(disconnected)</span></h1>
|
||||||
|
<textarea id="textarea3" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="../../y.js"></script>
|
||||||
|
<script src="../../../y-array/y-array.js"></script>
|
||||||
|
<script src="../../../y-text/dist/y-text.js"></script>
|
||||||
|
<script src="../../../y-memory/y-memory.js"></script>
|
||||||
|
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
|
||||||
|
<script src="./index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
64
examples/infiniteyjs/index.js
Normal file
64
examples/infiniteyjs/index.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* global Y */
|
||||||
|
|
||||||
|
Y({
|
||||||
|
db: {
|
||||||
|
name: 'memory'
|
||||||
|
},
|
||||||
|
connector: {
|
||||||
|
name: 'websockets-client',
|
||||||
|
room: 'Textarea-example',
|
||||||
|
url: 'https://yjs-v13.herokuapp.com/'
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
textarea: 'Text'
|
||||||
|
}
|
||||||
|
}).then(function (y) {
|
||||||
|
window.y1 = y
|
||||||
|
y.share.textarea.bind(document.getElementById('textarea1'))
|
||||||
|
})
|
||||||
|
|
||||||
|
Y({
|
||||||
|
db: {
|
||||||
|
name: 'memory'
|
||||||
|
},
|
||||||
|
connector: {
|
||||||
|
name: 'websockets-client',
|
||||||
|
room: 'Textarea-example',
|
||||||
|
url: 'https://yjs-v13-second.herokuapp.com/'
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
textarea: 'Text'
|
||||||
|
}
|
||||||
|
}).then(function (y) {
|
||||||
|
window.y2 = y
|
||||||
|
y.share.textarea.bind(document.getElementById('textarea2'))
|
||||||
|
y.connector.socket.on('connection', function () {
|
||||||
|
document.getElementById('container2').removeAttribute('disconnected')
|
||||||
|
})
|
||||||
|
y.connector.socket.on('disconnect', function () {
|
||||||
|
document.getElementById('container2').setAttribute('disconnected', true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Y({
|
||||||
|
db: {
|
||||||
|
name: 'memory'
|
||||||
|
},
|
||||||
|
connector: {
|
||||||
|
name: 'websockets-client',
|
||||||
|
room: 'Textarea-example',
|
||||||
|
url: 'https://yjs-v13-third.herokuapp.com/'
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
textarea: 'Text'
|
||||||
|
}
|
||||||
|
}).then(function (y) {
|
||||||
|
window.y3 = y
|
||||||
|
y.share.textarea.bind(document.getElementById('textarea3'))
|
||||||
|
y.connector.socket.on('connection', function () {
|
||||||
|
document.getElementById('container3').removeAttribute('disconnected')
|
||||||
|
})
|
||||||
|
y.connector.socket.on('disconnect', function () {
|
||||||
|
document.getElementById('container3').setAttribute('disconnected', true)
|
||||||
|
})
|
||||||
|
})
|
@ -2,6 +2,10 @@
|
|||||||
"name": "examples",
|
"name": "examples",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"dist": "rollup -c",
|
||||||
|
"watch": "rollup -cw"
|
||||||
|
},
|
||||||
"author": "Kevin Jahns",
|
"author": "Kevin Jahns",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
27
examples/rollup.config.js
Normal file
27
examples/rollup.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import nodeResolve from 'rollup-plugin-node-resolve'
|
||||||
|
import commonjs from 'rollup-plugin-commonjs'
|
||||||
|
|
||||||
|
var pkg = require('./package.json')
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: 'yjs-dist.esm',
|
||||||
|
dest: 'yjs-dist.js',
|
||||||
|
moduleName: 'Y',
|
||||||
|
format: 'umd',
|
||||||
|
plugins: [
|
||||||
|
nodeResolve({
|
||||||
|
main: true,
|
||||||
|
module: true,
|
||||||
|
browser: true
|
||||||
|
}),
|
||||||
|
commonjs()
|
||||||
|
],
|
||||||
|
sourceMap: true,
|
||||||
|
banner: `
|
||||||
|
/**
|
||||||
|
* ${pkg.name} - ${pkg.description}
|
||||||
|
* @version v${pkg.version}
|
||||||
|
* @license ${pkg.license}
|
||||||
|
*/
|
||||||
|
`
|
||||||
|
}
|
@ -12,12 +12,12 @@ Y({
|
|||||||
connector: {
|
connector: {
|
||||||
name: 'websockets-client',
|
name: 'websockets-client',
|
||||||
room: 'Textarea-example',
|
room: 'Textarea-example',
|
||||||
url: url || 'http://127.0.0.1:1234'
|
// url: '//localhost:1234',
|
||||||
|
url: 'https://yjs-v13.herokuapp.com/',
|
||||||
|
// options: { transports: ['websocket'], upgrade: false }
|
||||||
},
|
},
|
||||||
sourceDir: '/bower_components',
|
|
||||||
share: {
|
share: {
|
||||||
textarea: 'Text', // y.share.textarea is of type Y.Text
|
textarea: 'Text'
|
||||||
test: 'Array'
|
|
||||||
}
|
}
|
||||||
}).then(function (y) {
|
}).then(function (y) {
|
||||||
window.yTextarea = y
|
window.yTextarea = y
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
</head>
|
</head>
|
||||||
<script src="../bower_components/yjs/y.js"></script>
|
<!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
|
||||||
<script src="../bower_components/jquery/dist/jquery.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||||
|
<script src="../yjs-dist.js"></script>
|
||||||
<script src="./index.js"></script>
|
<script src="./index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -7,6 +7,8 @@ Y({
|
|||||||
},
|
},
|
||||||
connector: {
|
connector: {
|
||||||
name: 'websockets-client',
|
name: 'websockets-client',
|
||||||
|
// url: 'http://127.0.0.1:1234',
|
||||||
|
url: 'http://192.168.178.81:1234',
|
||||||
room: 'Xml-example'
|
room: 'Xml-example'
|
||||||
},
|
},
|
||||||
sourceDir: '/bower_components',
|
sourceDir: '/bower_components',
|
||||||
|
12
examples/yjs-dist.esm
Normal file
12
examples/yjs-dist.esm
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
import Y from '../src/y.js'
|
||||||
|
import yArray from '../../y-array/src/y-array.js'
|
||||||
|
import yMap from '../../y-map/src/Map.js'
|
||||||
|
import yText from '../../y-text/src/Text.js'
|
||||||
|
import yXml from '../../y-xml/src/y-xml.js'
|
||||||
|
import yMemory from '../../y-memory/src/y-memory.js'
|
||||||
|
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
|
||||||
|
|
||||||
|
Y.extend(yArray, yMap, yText, yXml, yMemory, yWebsocketsClient)
|
||||||
|
|
||||||
|
export default Y
|
@ -10,6 +10,7 @@
|
|||||||
"debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
|
"debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
|
||||||
"lint": "standard",
|
"lint": "standard",
|
||||||
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
|
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
|
||||||
|
"watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'",
|
||||||
"postversion": "npm run dist",
|
"postversion": "npm run dist",
|
||||||
"postpublish": "tag-dist-files --overwrite-existing-tag"
|
"postpublish": "tag-dist-files --overwrite-existing-tag"
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
|
|||||||
import multiEntry from 'rollup-plugin-multi-entry'
|
import multiEntry from 'rollup-plugin-multi-entry'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: 'test/*',
|
entry: 'test/y-xml.tests.js',
|
||||||
moduleName: 'y-tests',
|
moduleName: 'y-tests',
|
||||||
format: 'umd',
|
format: 'umd',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -588,7 +588,7 @@ export default function extendDatabase (Y /* :any */) {
|
|||||||
createType (typedefinition, id) {
|
createType (typedefinition, id) {
|
||||||
var structname = typedefinition[0].struct
|
var structname = typedefinition[0].struct
|
||||||
id = id || this.getNextOpId(1)
|
id = id || this.getNextOpId(1)
|
||||||
var op = Y.Struct[structname].create(id)
|
var op = Y.Struct[structname].create(id, typedefinition[1])
|
||||||
op.type = typedefinition[0].name
|
op.type = typedefinition[0].name
|
||||||
|
|
||||||
this.requestTransaction(function * () {
|
this.requestTransaction(function * () {
|
||||||
|
1079
src/Struct.js
1079
src/Struct.js
File diff suppressed because it is too large
Load Diff
@ -863,7 +863,12 @@ export default function extendTransaction (Y) {
|
|||||||
var comp = id[1].split('_')
|
var comp = id[1].split('_')
|
||||||
if (comp.length > 1) {
|
if (comp.length > 1) {
|
||||||
var struct = comp[0]
|
var struct = comp[0]
|
||||||
var op = Y.Struct[struct].create(id)
|
let type = Y[comp[1]]
|
||||||
|
let args = null
|
||||||
|
if (type != null) {
|
||||||
|
args = Y.utils.parseTypeDefinition(type, comp[3])
|
||||||
|
}
|
||||||
|
var op = Y.Struct[struct].create(id, args)
|
||||||
op.type = comp[1]
|
op.type = comp[1]
|
||||||
yield * this.setOperation(op)
|
yield * this.setOperation(op)
|
||||||
return op
|
return op
|
||||||
|
13
src/Utils.js
13
src/Utils.js
@ -840,4 +840,17 @@ export default function Utils (Y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Y.utils.generateUserId = generateUserId
|
Y.utils.generateUserId = generateUserId
|
||||||
|
|
||||||
|
Y.utils.parseTypeDefinition = function parseTypeDefinition (type, typeArgs) {
|
||||||
|
var args = []
|
||||||
|
try {
|
||||||
|
args = JSON.parse('[' + typeArgs + ']')
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Was not able to parse type definition!')
|
||||||
|
}
|
||||||
|
if (type.typeDefinition.parseArguments != null) {
|
||||||
|
args = type.typeDefinition.parseArguments(args[0])[1]
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
src/y.js
20
src/y.js
@ -176,23 +176,15 @@ class YConfig extends Y.utils.NamedEventHandler {
|
|||||||
// create shared object
|
// create shared object
|
||||||
for (var propertyname in opts.share) {
|
for (var propertyname in opts.share) {
|
||||||
var typeConstructor = opts.share[propertyname].split('(')
|
var typeConstructor = opts.share[propertyname].split('(')
|
||||||
|
let typeArgs = ''
|
||||||
|
if (typeConstructor.length === 2) {
|
||||||
|
typeArgs = typeConstructor[1].split(')')[0] || ''
|
||||||
|
}
|
||||||
var typeName = typeConstructor.splice(0, 1)
|
var typeName = typeConstructor.splice(0, 1)
|
||||||
var type = Y[typeName]
|
var type = Y[typeName]
|
||||||
var typedef = type.typeDefinition
|
var typedef = type.typeDefinition
|
||||||
var id = [0xFFFFFF, typedef.struct + '_' + typeName + '_' + propertyname + '_' + typeConstructor]
|
var id = [0xFFFFFF, typedef.struct + '_' + typeName + '_' + propertyname + '_' + typeArgs]
|
||||||
var args = []
|
let args = Y.utils.parseTypeDefinition(type, typeArgs)
|
||||||
if (typeConstructor.length === 1) {
|
|
||||||
try {
|
|
||||||
args = JSON.parse('[' + typeConstructor[0].split(')')[0] + ']')
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error('Was not able to parse type definition! (share.' + propertyname + ')')
|
|
||||||
}
|
|
||||||
if (type.typeDefinition.parseArguments == null) {
|
|
||||||
throw new Error(typeName + ' does not expect arguments!')
|
|
||||||
} else {
|
|
||||||
args = typedef.parseArguments(args[0])[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
share[propertyname] = yield * this.store.initType.call(this, id, args)
|
share[propertyname] = yield * this.store.initType.call(this, id, args)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -163,30 +163,6 @@ test('encode/decode List operations', async function binList (t) {
|
|||||||
id: [100, 33],
|
id: [100, 33],
|
||||||
type: 'Array'
|
type: 'Array'
|
||||||
})
|
})
|
||||||
|
|
||||||
t.log('info is an object')
|
|
||||||
testEncoding(t, writeList, readList, {
|
|
||||||
struct: 'List',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Array',
|
|
||||||
info: { prop: 'yay' }
|
|
||||||
})
|
|
||||||
|
|
||||||
t.log('info is a string')
|
|
||||||
testEncoding(t, writeList, readList, {
|
|
||||||
struct: 'List',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Array',
|
|
||||||
info: 'hi'
|
|
||||||
})
|
|
||||||
|
|
||||||
t.log('info is a number')
|
|
||||||
testEncoding(t, writeList, readList, {
|
|
||||||
struct: 'List',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Array',
|
|
||||||
info: 400
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const writeMap = Y.Struct.Map.binaryEncode
|
const writeMap = Y.Struct.Map.binaryEncode
|
||||||
@ -199,31 +175,4 @@ test('encode/decode Map operations', async function binMap (t) {
|
|||||||
type: 'Map',
|
type: 'Map',
|
||||||
map: {}
|
map: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.log('info is an object')
|
|
||||||
testEncoding(t, writeMap, readMap, {
|
|
||||||
struct: 'Map',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Map',
|
|
||||||
info: { prop: 'yay' },
|
|
||||||
map: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.log('info is a string')
|
|
||||||
testEncoding(t, writeMap, readMap, {
|
|
||||||
struct: 'Map',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Map',
|
|
||||||
map: {},
|
|
||||||
info: 'hi'
|
|
||||||
})
|
|
||||||
|
|
||||||
t.log('info is a number')
|
|
||||||
testEncoding(t, writeMap, readMap, {
|
|
||||||
struct: 'Map',
|
|
||||||
id: [100, 33],
|
|
||||||
type: 'Map',
|
|
||||||
map: {},
|
|
||||||
info: 400
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
288
test/y-xml.tests.js
Normal file
288
test/y-xml.tests.js
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import { wait, initArrays, compareUsers, Y, flushAll, garbageCollectUsers, applyRandomTests } from '../../yjs/tests-lib/helper.js'
|
||||||
|
import { test, proxyConsole } from 'cutest'
|
||||||
|
|
||||||
|
test('set property', async function xml0 (t) {
|
||||||
|
var { users, xml0, xml1 } = await initArrays(t, { users: 2 })
|
||||||
|
xml0.setAttribute('height', 10)
|
||||||
|
t.assert(xml0.getAttribute('height') === 10, 'Simple set+get works')
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.assert(xml1.getAttribute('height') === 10, 'Simple set+get works (remote)')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('events', async function xml1 (t) {
|
||||||
|
var { users, xml0, xml1 } = await initArrays(t, { users: 2 })
|
||||||
|
var event
|
||||||
|
var remoteEvent
|
||||||
|
let expectedEvent
|
||||||
|
xml0.observe(function (e) {
|
||||||
|
delete e._content
|
||||||
|
delete e.nodes
|
||||||
|
delete e.values
|
||||||
|
event = e
|
||||||
|
})
|
||||||
|
xml1.observe(function (e) {
|
||||||
|
delete e._content
|
||||||
|
delete e.nodes
|
||||||
|
delete e.values
|
||||||
|
remoteEvent = e
|
||||||
|
})
|
||||||
|
xml0.setAttribute('key', 'value')
|
||||||
|
expectedEvent = {
|
||||||
|
type: 'attributeChanged',
|
||||||
|
value: 'value',
|
||||||
|
name: 'key'
|
||||||
|
}
|
||||||
|
t.compare(event, expectedEvent, 'attribute changed event')
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.compare(remoteEvent, expectedEvent, 'attribute changed event (remote)')
|
||||||
|
// check attributeRemoved
|
||||||
|
xml0.removeAttribute('key')
|
||||||
|
expectedEvent = {
|
||||||
|
type: 'attributeRemoved',
|
||||||
|
name: 'key'
|
||||||
|
}
|
||||||
|
t.compare(event, expectedEvent, 'attribute deleted event')
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.compare(remoteEvent, expectedEvent, 'attribute deleted event (remote)')
|
||||||
|
// test childInserted event
|
||||||
|
expectedEvent = {
|
||||||
|
type: 'childInserted',
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
xml0.insert(0, [Y.XmlText('some text')])
|
||||||
|
t.compare(event, expectedEvent, 'child inserted event')
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.compare(remoteEvent, expectedEvent, 'child inserted event (remote)')
|
||||||
|
// test childRemoved
|
||||||
|
xml0.delete(0)
|
||||||
|
expectedEvent = {
|
||||||
|
type: 'childRemoved',
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
t.compare(event, expectedEvent, 'child deleted event')
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.compare(remoteEvent, expectedEvent, 'child deleted event (remote)')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('attribute modifications (y -> dom)', async function xml2 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.setAttribute('height', '100px')
|
||||||
|
await wait()
|
||||||
|
t.assert(dom0.getAttribute('height') === '100px', 'setAttribute')
|
||||||
|
xml0.removeAttribute('height')
|
||||||
|
await wait()
|
||||||
|
t.assert(dom0.getAttribute('height') == null, 'removeAttribute')
|
||||||
|
xml0.setAttribute('class', 'stuffy stuff')
|
||||||
|
await wait()
|
||||||
|
t.assert(dom0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('attribute modifications (dom -> y)', async function xml3 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
dom0.setAttribute('height', '100px')
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.getAttribute('height') === '100px', 'setAttribute')
|
||||||
|
dom0.removeAttribute('height')
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.getAttribute('height') == null, 'removeAttribute')
|
||||||
|
dom0.setAttribute('class', 'stuffy stuff')
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('element insert (dom -> y)', async function xml4 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
dom0.insertBefore(document.createTextNode('some text'), null)
|
||||||
|
dom0.insertBefore(document.createElement('p'), null)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.get(0).toString() === 'some text', 'Retrieve Text Node')
|
||||||
|
t.assert(xml0.get(1).nodeName === 'P', 'Retrieve Element node')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('element insert (y -> dom)', async function xml5 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, [Y.XmlText('some text')])
|
||||||
|
xml0.insert(1, [Y.Xml('p')])
|
||||||
|
t.assert(dom0.childNodes[0].textContent === 'some text', 'Retrieve Text node')
|
||||||
|
t.assert(dom0.childNodes[1].nodeName === 'P', 'Retrieve Element node')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y on insert, then delete (dom -> y)', async function xml6 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
dom0.insertBefore(document.createElement('p'), null)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 1, 'one node present')
|
||||||
|
dom0.childNodes[0].remove()
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 0, 'no node present after delete')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y on insert, then delete (y -> dom)', async function xml7 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, [Y.Xml('p')])
|
||||||
|
t.assert(dom0.childNodes[0].nodeName === 'P', 'Get inserted element from dom')
|
||||||
|
xml0.delete(0, 1)
|
||||||
|
t.assert(dom0.childNodes.length === 0, '#childNodes is empty after delete')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete consecutive (1) (Text)', async function xml8 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, ['1', '2', '3'].map(Y.XmlText))
|
||||||
|
await wait()
|
||||||
|
xml0.delete(1, 2)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 1, 'check length (y)')
|
||||||
|
t.assert(dom0.childNodes.length === 1, 'check length (dom)')
|
||||||
|
t.assert(dom0.childNodes[0].textContent === '1', 'check content')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete consecutive (2) (Text)', async function xml9 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, ['1', '2', '3'].map(Y.XmlText))
|
||||||
|
await wait()
|
||||||
|
xml0.delete(0, 1)
|
||||||
|
xml0.delete(1, 1)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 1, 'check length (y)')
|
||||||
|
t.assert(dom0.childNodes.length === 1, 'check length (dom)')
|
||||||
|
t.assert(dom0.childNodes[0].textContent === '2', 'check content')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete consecutive (1) (Element)', async function xml10 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
|
||||||
|
await wait()
|
||||||
|
xml0.delete(1, 2)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 1, 'check length (y)')
|
||||||
|
t.assert(dom0.childNodes.length === 1, 'check length (dom)')
|
||||||
|
t.assert(dom0.childNodes[0].nodeName === 'A', 'check content')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete consecutive (2) (Element)', async function xml11 (t) {
|
||||||
|
var { users, xml0 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
|
||||||
|
await wait()
|
||||||
|
xml0.delete(0, 1)
|
||||||
|
xml0.delete(1, 1)
|
||||||
|
await wait()
|
||||||
|
t.assert(xml0.length === 1, 'check length (y)')
|
||||||
|
t.assert(dom0.childNodes.length === 1, 'check length (dom)')
|
||||||
|
t.assert(dom0.childNodes[0].nodeName === 'B', 'check content')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Receive a bunch of elements (with disconnect)', async function xml12 (t) {
|
||||||
|
var { users, xml0, xml1 } = await initArrays(t, { users: 3 })
|
||||||
|
let dom0 = xml0.getDom()
|
||||||
|
let dom1 = xml1.getDom()
|
||||||
|
users[1].disconnect()
|
||||||
|
xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
|
||||||
|
xml0.insert(0, [Y.Xml('X'), Y.Xml('Y'), Y.Xml('Z')])
|
||||||
|
await users[1].reconnect()
|
||||||
|
await flushAll(t, users)
|
||||||
|
t.assert(xml0.length === 6, 'check length (y)')
|
||||||
|
t.assert(xml1.length === 6, 'check length (y) (reconnected user)')
|
||||||
|
t.assert(dom0.childNodes.length === 6, 'check length (dom)')
|
||||||
|
t.assert(dom1.childNodes.length === 6, 'check length (dom) (reconnected user)')
|
||||||
|
await compareUsers(t, users)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: move elements
|
||||||
|
var xmlTransactions = [
|
||||||
|
function attributeChange (t, user, chance) {
|
||||||
|
user.share.xml.getDom().setAttribute(chance.word(), chance.word())
|
||||||
|
},
|
||||||
|
function insertText (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
|
||||||
|
dom.insertBefore(document.createTextNode(chance.word()), succ)
|
||||||
|
},
|
||||||
|
function insertDom (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
|
||||||
|
dom.insertBefore(document.createElement(chance.word()), succ)
|
||||||
|
},
|
||||||
|
function deleteChild (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
if (dom.childNodes.length > 0) {
|
||||||
|
var d = chance.pickone(dom.childNodes)
|
||||||
|
d.remove()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function insertTextSecondLayer (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
if (dom.children.length > 0) {
|
||||||
|
let dom2 = chance.pickone(dom.children)
|
||||||
|
let succ = dom2.childNodes.length > 0 ? chance.pickone(dom2.childNodes) : null
|
||||||
|
dom2.insertBefore(document.createTextNode(chance.word()), succ)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function insertDomSecondLayer (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
if (dom.children.length > 0) {
|
||||||
|
let dom2 = chance.pickone(dom.children)
|
||||||
|
let succ = dom2.childNodes.length > 0 ? chance.pickone(dom2.childNodes) : null
|
||||||
|
dom2.insertBefore(document.createElement(chance.word()), succ)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function deleteChildSecondLayer (t, user, chance) {
|
||||||
|
let dom = user.share.xml.getDom()
|
||||||
|
if (dom.children.length > 0) {
|
||||||
|
let dom2 = chance.pickone(dom.children)
|
||||||
|
if (dom2.childNodes.length > 0) {
|
||||||
|
let d = chance.pickone(dom2.childNodes)
|
||||||
|
d.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
test('y-xml: Random tests (10)', async function randomXml10 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (42)', async function randomXml42 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 42)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (43)', async function randomXml43 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 43)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (44)', async function randomXml44 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 44)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (45)', async function randomXml45 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 45)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (46)', async function randomXml46 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 46)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('y-xml: Random tests (47)', async function randomXml47 (t) {
|
||||||
|
await applyRandomTests(t, xmlTransactions, 47)
|
||||||
|
})
|
@ -3,14 +3,16 @@ import _Y from '../../yjs/src/y.js'
|
|||||||
|
|
||||||
import yMemory from '../../y-memory/src/y-memory.js'
|
import yMemory from '../../y-memory/src/y-memory.js'
|
||||||
import yArray from '../../y-array/src/y-array.js'
|
import yArray from '../../y-array/src/y-array.js'
|
||||||
|
import yText from '../../y-text/src/Text.js'
|
||||||
import yMap from '../../y-map/src/Map.js'
|
import yMap from '../../y-map/src/Map.js'
|
||||||
|
import yXml from '../../y-xml/src/y-xml.js'
|
||||||
import yTest from './test-connector.js'
|
import yTest from './test-connector.js'
|
||||||
|
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
|
|
||||||
export let Y = _Y
|
export let Y = _Y
|
||||||
|
|
||||||
Y.extend(yMemory, yArray, yMap, yTest)
|
Y.extend(yMemory, yArray, yText, yMap, yTest, yXml)
|
||||||
|
|
||||||
export var database = { name: 'memory' }
|
export var database = { name: 'memory' }
|
||||||
export var connector = { name: 'test', url: 'http://localhost:1234' }
|
export var connector = { name: 'test', url: 'http://localhost:1234' }
|
||||||
@ -47,6 +49,31 @@ export async function garbageCollectUsers (t, users) {
|
|||||||
await Promise.all(users.map(u => u.db.emptyGarbageCollector()))
|
await Promise.all(users.map(u => u.db.emptyGarbageCollector()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function attrsToObject (attrs) {
|
||||||
|
let obj = {}
|
||||||
|
for (var i = 0; i < attrs.length; i++) {
|
||||||
|
let attr = attrs[i]
|
||||||
|
obj[attr.name] = attr.value
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
export function domToJson (dom) {
|
||||||
|
if (dom.nodeType === document.TEXT_NODE) {
|
||||||
|
return dom.textContent
|
||||||
|
} else if (dom.nodeType === document.ELEMENT_NODE) {
|
||||||
|
let attributes = attrsToObject(dom.attributes)
|
||||||
|
let children = Array.from(dom.childNodes.values()).map(domToJson)
|
||||||
|
return {
|
||||||
|
name: dom.nodeName,
|
||||||
|
children: children,
|
||||||
|
attributes: attributes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported node type')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 1. reconnect and flush all
|
* 1. reconnect and flush all
|
||||||
* 2. user 0 gc
|
* 2. user 0 gc
|
||||||
@ -73,6 +100,7 @@ export async function compareUsers (t, users) {
|
|||||||
}
|
}
|
||||||
var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable)
|
var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable)
|
||||||
var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable)
|
var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable)
|
||||||
|
var userXmlValues = users.map(u => u.share.xml.getDom()).map(domToJson)
|
||||||
|
|
||||||
await users[0].db.garbageCollect()
|
await users[0].db.garbageCollect()
|
||||||
await users[0].db.garbageCollect()
|
await users[0].db.garbageCollect()
|
||||||
@ -133,6 +161,7 @@ export async function compareUsers (t, users) {
|
|||||||
t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types')
|
t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types')
|
||||||
t.compare(userMapOneValues[i], userMapOneValues[i + 1], 'map types (propery "one")')
|
t.compare(userMapOneValues[i], userMapOneValues[i + 1], 'map types (propery "one")')
|
||||||
t.compare(userMapTwoValues[i], userMapTwoValues[i + 1], 'map types (propery "two")')
|
t.compare(userMapTwoValues[i], userMapTwoValues[i + 1], 'map types (propery "two")')
|
||||||
|
t.compare(userXmlValues[i], userXmlValues[i + 1], 'xml types')
|
||||||
t.compare(data[i].os, data[i + 1].os, 'os')
|
t.compare(data[i].os, data[i + 1].os, 'os')
|
||||||
t.compare(data[i].ds, data[i + 1].ds, 'ds')
|
t.compare(data[i].ds, data[i + 1].ds, 'ds')
|
||||||
t.compare(data[i].ss, data[i + 1].ss, 'ss')
|
t.compare(data[i].ss, data[i + 1].ss, 'ss')
|
||||||
@ -147,7 +176,7 @@ export async function initArrays (t, opts) {
|
|||||||
var result = {
|
var result = {
|
||||||
users: []
|
users: []
|
||||||
}
|
}
|
||||||
var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map' }, opts.share)
|
var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map', xml: 'Xml("div")' }, opts.share)
|
||||||
var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
|
var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
|
||||||
var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector)
|
var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector)
|
||||||
for (let i = 0; i < opts.users; i++) {
|
for (let i = 0; i < opts.users; i++) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user