2012-03-08 00:52:30 +00:00
|
|
|
module( 've/dm' );
|
|
|
|
|
2012-03-14 21:02:33 +00:00
|
|
|
test( 've.dm.DocumentSynchronizer', 11, function() {
|
2012-03-08 19:35:51 +00:00
|
|
|
var tests = {
|
|
|
|
// Test 1
|
|
|
|
'resize actions adjust node lengths': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel();
|
|
|
|
// Delete bold "b" from first paragraph
|
|
|
|
model.data.splice( 2, 1 );
|
|
|
|
// Push resize action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushResize( model.getChildren()[0], -1 );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[0].getContentLength();
|
|
|
|
},
|
|
|
|
'expected': 2
|
|
|
|
},
|
|
|
|
// Test 2
|
|
|
|
'insert actions can add new nodes in the middle': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }],
|
|
|
|
node = ve.dm.DocumentNode.createNodesFromData( data )[0];
|
|
|
|
// Insert element after first paragraph
|
|
|
|
ve.insertIntoArray( model.data, 5, data );
|
|
|
|
// Push insertion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushInsert( node, 5 );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[1].getContentData();
|
|
|
|
},
|
|
|
|
'expected': ['x']
|
|
|
|
},
|
|
|
|
// Test 3
|
|
|
|
'insert actions can add new nodes at the beginning': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }],
|
|
|
|
node = ve.dm.DocumentNode.createNodesFromData( data )[0];
|
|
|
|
// Insert element after first paragraph
|
|
|
|
ve.insertIntoArray( model.data, 0, data );
|
|
|
|
// Push insertion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushInsert( node, 0 );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[0].getContentData();
|
|
|
|
},
|
|
|
|
'expected': ['x']
|
|
|
|
},
|
|
|
|
// Test 4
|
|
|
|
'insert actions can add new nodes at the end': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }],
|
|
|
|
node = ve.dm.DocumentNode.createNodesFromData( data )[0];
|
|
|
|
// Insert element after first paragraph
|
|
|
|
ve.insertIntoArray( model.data, 34, data );
|
|
|
|
// Push insertion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushInsert( node, 34 );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[3].getContentData();
|
|
|
|
},
|
|
|
|
'expected': ['x']
|
|
|
|
},
|
|
|
|
// Test 5
|
|
|
|
'delete actions can remove nodes from the middle': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
node = model.getChildren()[1];
|
|
|
|
// Delete the table
|
|
|
|
model.data.splice( 5, 26 );
|
|
|
|
// Push deletion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushDelete( node );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren().length;
|
|
|
|
},
|
|
|
|
'expected': 2
|
|
|
|
},
|
|
|
|
// Test 6
|
|
|
|
'delete actions can remove nodes from the beginning': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
node = model.getChildren()[0];
|
|
|
|
// Delete the first paragraph
|
|
|
|
model.data.splice( 0, 5 );
|
|
|
|
// Push deletion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushDelete( node );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren().length;
|
|
|
|
},
|
|
|
|
'expected': 2
|
|
|
|
},
|
|
|
|
// Test 7
|
|
|
|
'delete actions can remove nodes from the end': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
node = model.getChildren()[2];
|
|
|
|
// Delete the first paragraph
|
|
|
|
model.data.splice( 31, 3 );
|
|
|
|
// Push deletion action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushDelete( node );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren().length;
|
|
|
|
},
|
|
|
|
'expected': 2
|
|
|
|
},
|
|
|
|
// Test 8
|
|
|
|
'rebuild actions can convert element types': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
node = model.getChildren()[0];
|
|
|
|
// Convert the first paragraph to a level 1 heading
|
|
|
|
model.data[0].type = 'heading';
|
|
|
|
model.data[0].attributes = { 'level': 1 };
|
|
|
|
model.data[4].type = '/heading';
|
|
|
|
// Push rebuild action
|
2012-03-14 21:02:31 +00:00
|
|
|
sync.pushRebuild( new ve.Range( 0, 5 ), new ve.Range( 0, 5 ) );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[0].getElementType();
|
|
|
|
},
|
|
|
|
'expected': 'heading'
|
|
|
|
},
|
|
|
|
// Test 9
|
|
|
|
'rebuild actions can replace one node with more than one node': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
node = model.getChildren()[0],
|
|
|
|
data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }];
|
|
|
|
// Insert element after first paragraph
|
|
|
|
ve.insertIntoArray( model.data, 5, data );
|
|
|
|
// Push rebuild action with a length adustment of 3 to account for the new element
|
2012-03-14 21:02:31 +00:00
|
|
|
sync.pushRebuild( new ve.Range( 0, 5 ), new ve.Range( 0, 8 ) );
|
2012-03-08 19:35:51 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return model.getChildren()[1].getContentData();
|
|
|
|
},
|
|
|
|
'expected': ['x']
|
2012-03-09 19:38:54 +00:00
|
|
|
},
|
|
|
|
// Test 10
|
2012-03-14 21:02:33 +00:00
|
|
|
'rebuild actions can unwrap and rewrap multiple nodes': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(), retval = {}, i,
|
|
|
|
node = model.getChildren()[1].getChildren()[0].getChildren()[0],
|
|
|
|
unwrappedData = [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'd',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'e',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'f',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'g',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
],
|
|
|
|
wrappedData = [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'd',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': 'list' },
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'e',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'f',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['number'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'g',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': '/list' }
|
|
|
|
];
|
|
|
|
|
|
|
|
// Unwrap the list in the linear model
|
|
|
|
ve.batchedSplice( model.data, 8, wrappedData.length, unwrappedData );
|
|
|
|
// Rebuild it
|
|
|
|
sync.pushRebuild( new ve.Range( 8, 8 + wrappedData.length ), new ve.Range( 8, 8 + unwrappedData.length ) );
|
|
|
|
sync.synchronize();
|
|
|
|
retval.afterUnwrap = {
|
|
|
|
'numChildren': node.getChildren().length,
|
|
|
|
'childContents': []
|
|
|
|
};
|
|
|
|
for ( i = 0; i < node.getChildren().length; i++ ) {
|
|
|
|
retval.afterUnwrap.childContents[i] = node.getChildren()[i].getContentData();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rewrap the list in the linear model
|
|
|
|
ve.batchedSplice( model.data, 8, unwrappedData.length, wrappedData );
|
|
|
|
// Rebuild it
|
|
|
|
sync.pushRebuild( new ve.Range( 8, 8 + unwrappedData.length ), new ve.Range( 8, 8 + wrappedData.length ) );
|
|
|
|
sync.synchronize();
|
|
|
|
retval.afterRewrap = {
|
|
|
|
'numChildren': node.getChildren().length,
|
|
|
|
'childContents': []
|
|
|
|
};
|
|
|
|
for ( i = 0; i < node.getChildren().length; i++ ) {
|
|
|
|
retval.afterRewrap.childContents[i] = node.getChildren()[i].getContentData();
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
},
|
|
|
|
'expected': {
|
|
|
|
'afterUnwrap': {
|
|
|
|
'numChildren': 4,
|
|
|
|
'childContents': [ ['d'], ['e'], ['f'], ['g'] ]
|
|
|
|
},
|
|
|
|
'afterRewrap': {
|
|
|
|
'numChildren': 2,
|
|
|
|
'childContents': [
|
|
|
|
['d'],
|
|
|
|
[
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'e',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'f',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['number'] } },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'g',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Test 11
|
2012-03-09 19:38:54 +00:00
|
|
|
'multiple actions can be synchronized together': {
|
|
|
|
'actual': function( sync ) {
|
|
|
|
var model = sync.getModel(),
|
|
|
|
data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }],
|
|
|
|
node = ve.dm.DocumentNode.createNodesFromData( data )[0];
|
|
|
|
// Delete bold "b" from first paragraph
|
|
|
|
model.data.splice( 2, 1 );
|
|
|
|
// Push resize action
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushResize( model.getChildren()[0], -1 );
|
2012-03-09 19:38:54 +00:00
|
|
|
// Delete the first paragraph (offset adjusted for previous action)
|
|
|
|
model.data.splice( 30, 3 );
|
2012-03-14 21:02:29 +00:00
|
|
|
// Push deletion action
|
|
|
|
sync.pushDelete( model.getChildren()[2] );
|
2012-03-09 19:38:54 +00:00
|
|
|
// Insert element after last paragraph
|
|
|
|
ve.insertIntoArray( model.data, 30, data );
|
|
|
|
// Push insertion action (note: using original offset)
|
2012-03-14 21:02:29 +00:00
|
|
|
sync.pushInsert( node, 34 );
|
2012-03-09 19:38:54 +00:00
|
|
|
// Sync
|
|
|
|
sync.synchronize();
|
|
|
|
return [
|
|
|
|
model.getChildren()[0].getContentLength(),
|
|
|
|
model.getChildren().length,
|
|
|
|
model.getChildren()[2].getContentData()
|
|
|
|
];
|
|
|
|
},
|
|
|
|
'expected': [2, 3, ['x']]
|
2012-03-08 19:35:51 +00:00
|
|
|
}
|
|
|
|
};
|
2012-03-08 00:52:30 +00:00
|
|
|
|
2012-03-08 19:35:51 +00:00
|
|
|
// Run tests
|
|
|
|
for ( var test in tests ) {
|
|
|
|
deepEqual(
|
|
|
|
tests[test].actual(
|
|
|
|
new ve.dm.DocumentSynchronizer(
|
|
|
|
ve.dm.DocumentNode.newFromPlainObject( veTest.obj )
|
|
|
|
)
|
|
|
|
),
|
|
|
|
tests[test].expected,
|
|
|
|
test
|
|
|
|
);
|
|
|
|
}
|
2012-03-08 00:52:30 +00:00
|
|
|
} );
|