mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
7a726b0278
To handle replace operations that are not themselves consistent (these are common, for instance when replacing an opening element in one place, then replacing the closing element somewhere else), we process subsequent replace operations inside the first one until things are balanced again, then issue a single rebuild for the whole thing. Change-Id: Ide4613f046fabfeeef383138c39e350b1b710033
952 lines
28 KiB
JavaScript
952 lines
28 KiB
JavaScript
module( 've/dm' );
|
|
|
|
test( 've.dm.TransactionProcessor', 79, function() {
|
|
var documentModel = ve.dm.DocumentNode.newFromPlainObject( veTest.obj );
|
|
|
|
// FIXME: These tests shouldn't use prepareFoo() because those functions
|
|
// normalize the transactions they create and are tested separately.
|
|
// We should be creating transactions directly and feeding those into
|
|
// commit()/rollback() --Roan
|
|
var elementAttributeChange = documentModel.prepareElementAttributeChange(
|
|
0, 'test', 1
|
|
);
|
|
|
|
// Test 1
|
|
ve.dm.TransactionProcessor.commit( documentModel, elementAttributeChange );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph', 'attributes': { 'test': 1 } },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'commit applies an element attribute change transaction to the content'
|
|
);
|
|
|
|
// Test 2
|
|
ve.dm.TransactionProcessor.rollback( documentModel, elementAttributeChange );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback reverses the effect of an element attribute change transaction on the content'
|
|
);
|
|
|
|
var contentAnnotation = documentModel.prepareContentAnnotation(
|
|
new ve.Range( 1, 4 ), 'set', { 'type': 'textStyle/bold' }
|
|
);
|
|
|
|
// Test 3
|
|
ve.dm.TransactionProcessor.commit( documentModel, contentAnnotation );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
['a', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
[
|
|
'c',
|
|
{ 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' },
|
|
{ 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
|
|
],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'commit applies a content annotation transaction to the content'
|
|
);
|
|
|
|
// Test 4
|
|
ve.dm.TransactionProcessor.rollback( documentModel, contentAnnotation );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback reverses the effect of a content annotation transaction on the content'
|
|
);
|
|
|
|
var insertion = documentModel.prepareInsertion( 3, ['d'] );
|
|
|
|
// Test 5
|
|
ve.dm.TransactionProcessor.commit( documentModel, insertion );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 6 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
'd',
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'commit applies an insertion transaction to the content'
|
|
);
|
|
|
|
// Test 6
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
'd',
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'commit keeps model tree up to date with insertions'
|
|
);
|
|
|
|
// Test 7
|
|
ve.dm.TransactionProcessor.rollback( documentModel, insertion );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback reverses the effect of an insertion transaction on the content'
|
|
);
|
|
|
|
// Test 8
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'rollback keeps model tree up to date with insertions'
|
|
);
|
|
|
|
var removal = documentModel.prepareRemoval( new ve.Range( 2, 4 ) );
|
|
|
|
// Test 9
|
|
ve.dm.TransactionProcessor.commit( documentModel, removal );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 3 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'commit applies a removal transaction to the content'
|
|
);
|
|
|
|
// Test 10
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
['a'],
|
|
'commit keeps model tree up to date with removals'
|
|
);
|
|
|
|
// Test 11
|
|
ve.dm.TransactionProcessor.rollback( documentModel, removal );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback reverses the effect of a removal transaction on the content'
|
|
);
|
|
|
|
// Test 12
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'rollback keeps model tree up to date with removals'
|
|
);
|
|
|
|
var paragraphBreak = documentModel.prepareInsertion(
|
|
2, [{ 'type': '/paragraph' }, { 'type': 'paragraph' }]
|
|
);
|
|
|
|
// Test 13
|
|
ve.dm.TransactionProcessor.commit( documentModel, paragraphBreak );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 7 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'commit applies an insertion transaction that splits the paragraph'
|
|
);
|
|
|
|
// Test 14
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
['a'],
|
|
'commit keeps model tree up to date with paragraph split (paragraph 1)'
|
|
);
|
|
|
|
// Test 15
|
|
deepEqual(
|
|
documentModel.getChildren()[1].getContentData(),
|
|
[
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'commit keeps model tree up to date with paragraph split (paragraph 2)'
|
|
);
|
|
|
|
// Test 16
|
|
ve.dm.TransactionProcessor.rollback( documentModel, paragraphBreak );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback reverses the effect of a paragraph split on the content'
|
|
);
|
|
|
|
// Test 17
|
|
deepEqual(
|
|
documentModel.getChildren()[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'rollback keeps model tree up to date with paragraph split (paragraphs are merged back)'
|
|
);
|
|
|
|
// Test 18
|
|
deepEqual(
|
|
documentModel.getChildren()[1].getElementType(),
|
|
'table',
|
|
'rollback keeps model tree up to date with paragraph split (table follows the paragraph)'
|
|
);
|
|
|
|
var listItemMerge = documentModel.prepareRemoval( new ve.Range( 14, 19 ) );
|
|
|
|
// Test 19
|
|
ve.dm.TransactionProcessor.commit( documentModel, listItemMerge );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 12, 22 ) ),
|
|
[
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet'] } },
|
|
{ 'type': 'paragraph' },
|
|
'f',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/listItem' },
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['number'] } },
|
|
{ 'type': 'paragraph' },
|
|
'g',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/listItem' }
|
|
],
|
|
'removal merges two list items with paragraphs'
|
|
);
|
|
|
|
// Test 20
|
|
deepEqual( documentModel.children[1].children[0].children[0].children[1].children.length, 2,
|
|
'removal keeps model tree up to date with list item merge (number of children)'
|
|
);
|
|
|
|
// Test 21
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContentData(),
|
|
[ 'f' ],
|
|
'removal keeps model tree up to date with list item merge (first list item)'
|
|
);
|
|
|
|
// Test 22
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContentData(),
|
|
[ 'g' ],
|
|
'removal keeps model tree up to date with list item merge (second list item)'
|
|
);
|
|
|
|
// Test 23
|
|
deepEqual(
|
|
documentModel.children[2].getContentData(),
|
|
[ 'h' ],
|
|
'rollback keeps model tree up to date with list item split (final paragraph)'
|
|
);
|
|
|
|
// Test 24
|
|
ve.dm.TransactionProcessor.rollback( documentModel, listItemMerge );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 12, 27 ) ),
|
|
[
|
|
{ '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' }
|
|
],
|
|
'rollback reverses list item merge (splits the list items)'
|
|
);
|
|
|
|
// Test 25
|
|
deepEqual( documentModel.children[1].children[0].children[0].children[1].children.length, 3,
|
|
'rollback keeps model tree up to date with list item split (number of children)'
|
|
);
|
|
|
|
// Test 26
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContentData(),
|
|
[ 'e' ],
|
|
'rollback keeps model tree up to date with list item split (first list item)'
|
|
);
|
|
|
|
// Test 27
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContentData(),
|
|
[ 'f' ],
|
|
'rollback keeps model tree up to date with list item split (second list item)'
|
|
);
|
|
|
|
// Test 28
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].children[2].children[0].getContentData(),
|
|
[ 'g' ],
|
|
'rollback keeps model tree up to date with list item split (third list item)'
|
|
);
|
|
|
|
// Test 29
|
|
deepEqual(
|
|
documentModel.children[2].getContentData(),
|
|
[ 'h' ],
|
|
'rollback keeps model tree up to date with list item split (final paragraph)'
|
|
);
|
|
|
|
var listSplit = documentModel.prepareInsertion( 17, [{ 'type': '/list' }, { 'type': 'list' }] );
|
|
|
|
// Test 30
|
|
ve.dm.TransactionProcessor.commit( documentModel, listSplit );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 15, 21 ) ),
|
|
[
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/listItem' },
|
|
{ 'type': '/list' },
|
|
{ 'type': 'list' },
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] } },
|
|
{ 'type': 'paragraph' }
|
|
],
|
|
'commit splits list into two lists'
|
|
);
|
|
|
|
// Test 31
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children.length, 3,
|
|
'commit keeps model tree up to date with list split (number of children)'
|
|
);
|
|
|
|
// Test 32
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getElementType(), 'list',
|
|
'commit keeps model tree up to date with list split (first list type)'
|
|
);
|
|
|
|
// Test 33
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[2].getElementType(), 'list',
|
|
'commit keeps model tree up to date with list split (second list type)'
|
|
);
|
|
|
|
// Test 34
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getContentData(),
|
|
[
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet'] } },
|
|
{ 'type': 'paragraph' },
|
|
'e',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/listItem' }
|
|
],
|
|
'commit keeps model tree up to date with list split (first list content)'
|
|
);
|
|
|
|
// Test 35
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[2].getContentData(),
|
|
[
|
|
{ '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' }
|
|
],
|
|
'commit keeps model tree up to date with list split (second list content)'
|
|
);
|
|
|
|
// Test 36
|
|
ve.dm.TransactionProcessor.rollback( documentModel, listSplit );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 15, 19 ) ),
|
|
[
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/listItem' },
|
|
{ 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] } },
|
|
{ 'type': 'paragraph' }
|
|
],
|
|
'rollback reverses list split'
|
|
);
|
|
|
|
// Test 37
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children.length, 2,
|
|
'rollback keeps model tree up to date with list split (number of children)'
|
|
);
|
|
|
|
// Test 38
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getElementType(), 'list',
|
|
'rollback keeps model tree up to date with list split (first list type)'
|
|
);
|
|
|
|
// Test 39
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getContentData(),
|
|
[
|
|
{ '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' }
|
|
],
|
|
'rollback keeps model tree up to date with list split (first list content)'
|
|
);
|
|
|
|
|
|
var contentReplacement = documentModel.prepareContentReplacement( new ve.Range( 32, 33 ), [ 'i', 'j', 'k' ] );
|
|
|
|
// Test 40
|
|
ve.dm.TransactionProcessor.commit( documentModel, contentReplacement );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 31, 36 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'i', 'j', 'k',
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'replacement replaces content'
|
|
);
|
|
|
|
// Test 41
|
|
ve.dm.TransactionProcessor.rollback( documentModel, contentReplacement );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 31, 34 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'h',
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback restores content'
|
|
);
|
|
|
|
var paragraphToHeading = documentModel.prepareWrap( new ve.Range( 1, 4 ), [ { 'type': 'paragraph' } ], [ { 'type': 'heading', 'level': 2 } ], [], [] );
|
|
|
|
// Test 42
|
|
ve.dm.TransactionProcessor.commit( documentModel, paragraphToHeading );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'heading', 'level': 2 },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/heading' }
|
|
],
|
|
'changing paragraph to heading'
|
|
);
|
|
|
|
// Test 43
|
|
deepEqual(
|
|
documentModel.children[0].getElementType(), 'heading',
|
|
'commit keeps model tree up to date with paragraph->heading change (element type)'
|
|
);
|
|
|
|
// Test 44
|
|
deepEqual(
|
|
documentModel.children[0].getElement(), { 'type': 'heading', 'level': 2 },
|
|
'commit keeps model tree up to date with paragraph->heading change (element)'
|
|
);
|
|
|
|
// Test 45
|
|
deepEqual(
|
|
documentModel.children[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'commit keeps model tree up to date with paragraph->heading change (content data)'
|
|
);
|
|
|
|
// Test 46
|
|
ve.dm.TransactionProcessor.rollback( documentModel, paragraphToHeading );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 0, 5 ) ),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
|
|
{ 'type': '/paragraph' }
|
|
],
|
|
'rollback puts paragraph back'
|
|
);
|
|
|
|
// Test 47
|
|
deepEqual(
|
|
documentModel.children[0].getElementType(), 'paragraph',
|
|
'rollback keeps model tree up to date with paragraph->heading change (element type)'
|
|
);
|
|
|
|
// Test 48
|
|
deepEqual(
|
|
documentModel.children[0].getElement(), { 'type': 'paragraph' },
|
|
'rollback keeps model tree up to date with paragraph->heading change (element)'
|
|
);
|
|
|
|
// Test 49
|
|
deepEqual(
|
|
documentModel.children[0].getContentData(),
|
|
[
|
|
'a',
|
|
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
|
|
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
|
|
],
|
|
'rollback keeps model tree up to date with paragraph->heading change (content data)'
|
|
);
|
|
|
|
var unwrapList = documentModel.prepareWrap( new ve.Range( 12, 27 ), [ { 'type': 'list' } ], [] , [ { 'type': 'listItem' } ], [] );
|
|
|
|
// Test 50
|
|
ve.dm.TransactionProcessor.commit( documentModel, unwrapList );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 7, 21 ) ),
|
|
[
|
|
{ 'type': 'tableCell' },
|
|
{ 'type': 'paragraph' },
|
|
'd',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'e',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'f',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'g',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/tableCell' }
|
|
],
|
|
'unwrapping the list produces a cell with four adjacent paragraphs'
|
|
);
|
|
|
|
// Test 51
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children.length, 4,
|
|
'commit keeps model tree up to date with list unwrap (number of children)'
|
|
);
|
|
|
|
// Test 52
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[0].getElementType(), 'paragraph',
|
|
'commit keeps model tree up to date with list unwrap (first child is a paragraph)'
|
|
);
|
|
|
|
// Test 53
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getElementType(), 'paragraph',
|
|
'commit keeps model tree up to date with list unwrap (second child is a paragraph)'
|
|
);
|
|
|
|
// Test 54
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[2].getElementType(), 'paragraph',
|
|
'commit keeps model tree up to date with list unwrap (third child is a paragraph)'
|
|
);
|
|
|
|
// Test 55
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[3].getElementType(), 'paragraph',
|
|
'commit keeps model tree up to date with list unwrap (fourth child is a paragraph)'
|
|
);
|
|
|
|
// Test 56
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].getContentData(),
|
|
[
|
|
{ 'type': 'paragraph' },
|
|
'd',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'e',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'f',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': 'paragraph' },
|
|
'g',
|
|
{ 'type': '/paragraph' },
|
|
],
|
|
'commit keeps model tree up to date with list unwrap (content data)'
|
|
);
|
|
|
|
// Test 57
|
|
ve.dm.TransactionProcessor.rollback( documentModel, unwrapList );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 7, 29 ) ),
|
|
[
|
|
{ 'type': 'tableCell' },
|
|
{ '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' },
|
|
{ 'type': '/tableCell' }
|
|
],
|
|
'rollback puts the list back'
|
|
);
|
|
|
|
// Test 58
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children.length, 2,
|
|
'rollback keeps model tree up to date with list unwrap (number of children)'
|
|
);
|
|
|
|
// Test 59
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[0].getElementType(), 'paragraph',
|
|
'rollback keeps model tree up to date with list unwrap (first child is a paragraph)'
|
|
);
|
|
|
|
// Test 60
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children[1].getElementType(), 'list',
|
|
'rollback keeps model tree up to date with list unwrap (second child is a list)'
|
|
);
|
|
|
|
// Test 61
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].getContentData(),
|
|
[
|
|
{ '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' },
|
|
],
|
|
'rollback keeps model tree up to date with list unwrap (content data)'
|
|
);
|
|
|
|
|
|
var replaceTable = documentModel.prepareWrap( new ve.Range( 8, 28 ), [ { 'type': 'table' }, { 'type': 'tableRow' }, { 'type': 'tableCell' } ],
|
|
[ { 'type': 'list' }, { 'type': 'listItem' } ], [], [] );
|
|
|
|
// Test 62
|
|
ve.dm.TransactionProcessor.commit( documentModel, replaceTable );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 5, 30 ) ),
|
|
[
|
|
{ 'type': 'list' },
|
|
{ 'type': 'listItem' },
|
|
{ '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' },
|
|
{ 'type': '/listItem' },
|
|
{ 'type': '/list' },
|
|
{ 'type': 'paragraph' }
|
|
],
|
|
'replacing a table with the list reverses the order of the closing tags correctly'
|
|
);
|
|
|
|
// Test 63
|
|
deepEqual(
|
|
documentModel.children.length, 3,
|
|
'commit keeps model tree up to date with table replace (number of children of root node)'
|
|
);
|
|
|
|
// Test 64
|
|
deepEqual(
|
|
documentModel.children[1].getElementType(), 'list',
|
|
'commit keeps model tree up to date with table replace (second child of root node is a list)'
|
|
);
|
|
|
|
// Test 65
|
|
deepEqual(
|
|
documentModel.children[1].children.length, 1,
|
|
'commit keeps model tree up to date with table replace (number of children of list)'
|
|
);
|
|
|
|
// Test 66
|
|
deepEqual(
|
|
documentModel.children[1].children[0].getElementType(), 'listItem',
|
|
'commit keeps model tree up to date with table replace (child of list is a listItem)'
|
|
);
|
|
|
|
// Test 67
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children.length, 2,
|
|
'commit keeps model tree up to date with table replace (number of children of listItem)'
|
|
);
|
|
|
|
// Test 68
|
|
deepEqual(
|
|
documentModel.children[1].children[0].getContentData(),
|
|
[
|
|
{ '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' }
|
|
],
|
|
'commit keeps model tree up to date with table replace (content data of listItem)'
|
|
);
|
|
|
|
// Test 69
|
|
ve.dm.TransactionProcessor.rollback( documentModel, replaceTable );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 5, 32 ) ),
|
|
[
|
|
{ 'type': 'table' },
|
|
{ 'type': 'tableRow' },
|
|
{ 'type': 'tableCell' },
|
|
{ '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' },
|
|
{ 'type': '/tableCell' },
|
|
{ 'type': '/tableRow' },
|
|
{ 'type': '/table' },
|
|
{ 'type': 'paragraph' }
|
|
],
|
|
'rollback puts the table back'
|
|
);
|
|
|
|
// Test 70
|
|
deepEqual(
|
|
documentModel.children.length, 3,
|
|
'rollback keeps model tree up to date with table replace (number of children of root node)'
|
|
);
|
|
|
|
// Test 71
|
|
deepEqual(
|
|
documentModel.children[1].getElementType(), 'table',
|
|
'rollback keeps model tree up to date with table replace (second child of root node is a table)'
|
|
);
|
|
|
|
// Test 72
|
|
deepEqual(
|
|
documentModel.children[1].children.length, 1,
|
|
'rollback keeps model tree up to date with table replace (number of children of table)'
|
|
);
|
|
|
|
// Test 73
|
|
deepEqual(
|
|
documentModel.children[1].children[0].getElementType(), 'tableRow',
|
|
'rollback keeps model tree up to date with table replace (child of table is a tableRow)'
|
|
);
|
|
|
|
// Test 74
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children.length, 1,
|
|
'rollback keeps model tree up to date with table replace (number of children of tableRow)'
|
|
);
|
|
|
|
// Test 75
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].getElementType(), 'tableCell',
|
|
'rollback keeps model tree up to date with table replace (child of tableRow is a tableCell)'
|
|
);
|
|
|
|
// Test 76
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].children.length, 2,
|
|
'rollback keeps model tree up to date with table replace (number of children of tableCell)'
|
|
);
|
|
|
|
// Test 77
|
|
deepEqual(
|
|
documentModel.children[1].children[0].children[0].getContentData(),
|
|
[
|
|
{ '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' }
|
|
],
|
|
'rollback keeps model tree up to date with table replace (content data of tableCell)'
|
|
);
|
|
|
|
var replacementWithAnnotations = new ve.dm.Transaction();
|
|
replacementWithAnnotations.pushRetain( 24 );
|
|
replacementWithAnnotations.pushStartAnnotating( 'set', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' } );
|
|
replacementWithAnnotations.pushReplace( [ 'g' ], [ 'i', 'j', 'k' ] );
|
|
replacementWithAnnotations.pushStopAnnotating( 'set', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' } );
|
|
replacementWithAnnotations.pushRetain( 10 );
|
|
|
|
// Test 78
|
|
ve.dm.TransactionProcessor.commit( documentModel, replacementWithAnnotations );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 24, 27 ) ),
|
|
[
|
|
[ 'i', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' } ],
|
|
[ 'j', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' } ],
|
|
[ 'k', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' } ]
|
|
],
|
|
'replacement replaces content and applies annotations'
|
|
);
|
|
|
|
// Test 79
|
|
ve.dm.TransactionProcessor.rollback( documentModel, replacementWithAnnotations );
|
|
deepEqual(
|
|
documentModel.getData( new ve.Range( 24, 25 ) ),
|
|
[
|
|
'g'
|
|
],
|
|
'rollback restores content and removes annotations'
|
|
);
|
|
|
|
} );
|