mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-16 10:59:56 +00:00
fc8c46dd74
Objective: * Allow editing reference groups and names in the reference dialog Bonus: * Modify attribute transaction builder to support multiple attribute changes in a single transaction Changes: ve.ui.MWReferenceDialog.js * Load ref name and group from model * Save ref name and group, if changed, to model ve.ui.ListAction.js, ve.ui.Transaction.test.js, ve.ce.ResizableNode.js * Update use of newFromAttributeChange to newFromAttributeChanges ve.dm.SurfaceFragment.test.js * Add test for new changeAttributes method ve.dm.InternalList.js * Missing new line at end of file ve.dm.Transaction.js * Change newFromAttributeChange to accept an list of attribute changes and produce a single transaction that applies one or more attribute changes at once ve.dm.SurfaceFragment.js * Fix bug in getCoveredNodes where the wrong mode name was being used * Add changeAttributes method, which applies attributes to all covered nodes and allows filtering of which types of nodes the attributes are applied to ve.dm.MWReferenceNode.js * Actually write key and group back to DOM * Separate onRoot functionality into addToInternalList so it can be called separately (similarly onUnroot/removeFromInternalList) ve.ce.MWReferenceListNode.js * Clone internal item CE node before appending to avoid rendering bug. *.php * Add links to messages and sort them Change-Id: Ic4121e4fcfc09265d5863af6f078cdeb77926c8e
265 lines
7.9 KiB
JavaScript
265 lines
7.9 KiB
JavaScript
/*!
|
|
* VisualEditor DataModel InternalList tests.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
QUnit.module( 've.dm.InternalList' );
|
|
|
|
/* Tests */
|
|
|
|
QUnit.test( 'getDocument', 1, function ( assert ) {
|
|
var doc = ve.dm.example.createExampleDocument(),
|
|
internalList = doc.getInternalList();
|
|
assert.deepEqual( internalList.getDocument(), doc, 'Returns original document' );
|
|
} );
|
|
|
|
QUnit.test( 'queueItemHtml/getItemHtmlQueue', 5, function ( assert ) {
|
|
var doc = ve.dm.example.createExampleDocument(),
|
|
internalList = doc.getInternalList();
|
|
assert.deepEqual(
|
|
internalList.queueItemHtml( 'reference', 'foo', 'Bar' ),
|
|
{ 'index': 0, 'isNew': true },
|
|
'First queued item returns index 0 and is new'
|
|
);
|
|
assert.deepEqual(
|
|
internalList.queueItemHtml( 'reference', 'foo', 'Baz' ),
|
|
{ 'index': 0, 'isNew': false },
|
|
'Duplicate key returns index 0 and is not new'
|
|
);
|
|
assert.deepEqual(
|
|
internalList.queueItemHtml( 'reference', 'bar', 'Baz' ),
|
|
{ 'index': 1, 'isNew': true },
|
|
'Second queued item returns index 1 and is new'
|
|
);
|
|
|
|
// Queue up empty data
|
|
internalList.queueItemHtml( 'reference', 'baz', '' ),
|
|
assert.deepEqual(
|
|
internalList.queueItemHtml( 'reference', 'baz', 'Quux' ),
|
|
{ 'index': 2, 'isNew': true },
|
|
'Third queued item is new because existing data in queue was empty'
|
|
);
|
|
|
|
assert.deepEqual( internalList.getItemHtmlQueue(), ['Bar', 'Baz', 'Quux'], 'getItemHtmlQueue returns stored HTML items' );
|
|
} );
|
|
|
|
QUnit.test( 'convertToData', 2, function ( assert ) {
|
|
var doc = ve.dm.example.createExampleDocument(),
|
|
internalList = doc.getInternalList(),
|
|
expectedData = [
|
|
{ 'type': 'internalList' },
|
|
{ 'type': 'internalItem' },
|
|
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
|
'B', 'a', 'r',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/internalItem' },
|
|
{ 'type': 'internalItem' },
|
|
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
|
'B', 'a', 'z',
|
|
{ 'type': '/paragraph' },
|
|
{ 'type': '/internalItem' },
|
|
{ 'type': '/internalList' }
|
|
];
|
|
|
|
// Mimic convert state setup (as done in ve.dm.Converter#getDataFromDom)
|
|
// TODO: The test should not (directly) reference the global instance
|
|
ve.dm.converter.doc = doc;
|
|
ve.dm.converter.store = doc.getStore();
|
|
ve.dm.converter.internalList = internalList;
|
|
ve.dm.converter.contextStack = [];
|
|
|
|
internalList.queueItemHtml( 'reference', 'foo', 'Bar' );
|
|
internalList.queueItemHtml( 'reference', 'bar', 'Baz' );
|
|
assert.deepEqual( internalList.convertToData( ve.dm.converter ), expectedData, 'Data matches' );
|
|
assert.deepEqual( internalList.getItemHtmlQueue(), [], 'Items html is emptied after conversion' );
|
|
} );
|
|
|
|
QUnit.test( 'clone', 2, function ( assert ) {
|
|
var doc = ve.dm.example.createExampleDocument(),
|
|
doc2 = ve.dm.example.createExampleDocument(),
|
|
internalList = doc.getInternalList(),
|
|
internalListClone = internalList.clone(),
|
|
internalListClone2 = internalList.clone( doc2 );
|
|
|
|
assert.equal( internalListClone.getDocument(), internalList.getDocument(), 'Documents match' );
|
|
assert.equal( internalListClone2.getDocument(), doc2, 'Cloning with document parameter' );
|
|
} );
|
|
|
|
QUnit.test( 'addNode/removeNode', 6, function ( assert ) {
|
|
var doc = ve.dm.example.createExampleDocument( 'references' ),
|
|
newInternalList = new ve.dm.InternalList( doc ),
|
|
referenceNodes = [
|
|
doc.documentNode.children[0].children[0],
|
|
doc.documentNode.children[1].children[1],
|
|
doc.documentNode.children[1].children[3],
|
|
doc.documentNode.children[1].children[5],
|
|
doc.documentNode.children[2].children[0],
|
|
doc.documentNode.children[2].children[1]
|
|
],
|
|
expectedNodes = {
|
|
'mwReference/': {
|
|
'keyedNodes': {
|
|
'bar': [ referenceNodes[1], referenceNodes[3] ],
|
|
'quux': [ referenceNodes[2] ]
|
|
},
|
|
'firstNodes': [
|
|
referenceNodes[0],
|
|
referenceNodes[1],
|
|
referenceNodes[2],
|
|
referenceNodes[4],
|
|
referenceNodes[5]
|
|
],
|
|
'indexOrder': [ 0, 1, 2, 3, 4 ]
|
|
}
|
|
};
|
|
|
|
newInternalList.addNode( 'mwReference/', null, 0, referenceNodes[0] );
|
|
newInternalList.addNode( 'mwReference/', 'bar', 1, referenceNodes[1] );
|
|
newInternalList.addNode( 'mwReference/', 'quux', 2, referenceNodes[2] );
|
|
newInternalList.addNode( 'mwReference/', 'bar', 1, referenceNodes[3] );
|
|
newInternalList.addNode( 'mwReference/', null, 3, referenceNodes[4] );
|
|
newInternalList.addNode( 'mwReference/', null, 4, referenceNodes[5] );
|
|
newInternalList.onTransact();
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
expectedNodes,
|
|
'Nodes added in order'
|
|
);
|
|
|
|
newInternalList = new ve.dm.InternalList( doc );
|
|
|
|
newInternalList.addNode( 'mwReference/', null, 4, referenceNodes[5] );
|
|
newInternalList.addNode( 'mwReference/', null, 3, referenceNodes[4] );
|
|
newInternalList.addNode( 'mwReference/', 'bar', 1, referenceNodes[3] );
|
|
newInternalList.addNode( 'mwReference/', 'quux', 2, referenceNodes[2] );
|
|
newInternalList.addNode( 'mwReference/', 'bar', 1, referenceNodes[1] );
|
|
newInternalList.addNode( 'mwReference/', null, 0, referenceNodes[0] );
|
|
newInternalList.onTransact();
|
|
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
expectedNodes,
|
|
'Nodes added in reverse order'
|
|
);
|
|
|
|
newInternalList.removeNode( 'mwReference/', 'bar', 1, referenceNodes[1] );
|
|
newInternalList.onTransact();
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
{
|
|
'mwReference/': {
|
|
'keyedNodes': {
|
|
'bar': [ referenceNodes[3] ],
|
|
'quux': [ referenceNodes[2] ]
|
|
},
|
|
'firstNodes': [
|
|
referenceNodes[0],
|
|
referenceNodes[3],
|
|
referenceNodes[2],
|
|
referenceNodes[4],
|
|
referenceNodes[5]
|
|
],
|
|
'indexOrder': [ 0, 2, 1, 3, 4 ]
|
|
}
|
|
},
|
|
'Keys re-ordered after one item of key removed'
|
|
);
|
|
|
|
newInternalList.removeNode( 'mwReference/', 'bar', 1, referenceNodes[3] );
|
|
newInternalList.onTransact();
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
{
|
|
'mwReference/': {
|
|
'keyedNodes': {
|
|
'quux': [ referenceNodes[2] ]
|
|
},
|
|
'firstNodes': [
|
|
referenceNodes[0],
|
|
undefined,
|
|
referenceNodes[2],
|
|
referenceNodes[4],
|
|
referenceNodes[5]
|
|
],
|
|
'indexOrder': [ 0, 2, 3, 4 ]
|
|
}
|
|
},
|
|
'Keys truncated after last item of key removed'
|
|
);
|
|
|
|
newInternalList.removeNode( 'mwReference/', null, 0, referenceNodes[0] );
|
|
newInternalList.onTransact();
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
{
|
|
'mwReference/': {
|
|
'keyedNodes': {
|
|
'quux': [ referenceNodes[2] ]
|
|
},
|
|
'firstNodes': [
|
|
undefined,
|
|
undefined,
|
|
referenceNodes[2],
|
|
referenceNodes[4],
|
|
referenceNodes[5]
|
|
],
|
|
'indexOrder': [ 2, 3, 4 ]
|
|
}
|
|
},
|
|
'Removing keyless item'
|
|
);
|
|
|
|
newInternalList.removeNode( 'mwReference/', null, 4, referenceNodes[5] );
|
|
newInternalList.removeNode( 'mwReference/', null, 3, referenceNodes[4] );
|
|
newInternalList.removeNode( 'mwReference/', 'quux', 2, referenceNodes[2] );
|
|
newInternalList.onTransact();
|
|
|
|
assert.deepEqualWithNodeTree(
|
|
newInternalList.nodes,
|
|
{
|
|
'mwReference/': {
|
|
'keyedNodes': {},
|
|
'firstNodes': new Array( 5 ),
|
|
'indexOrder': []
|
|
}
|
|
},
|
|
'All nodes removed'
|
|
);
|
|
} );
|
|
|
|
QUnit.test( 'getItemInsertion', 4, function ( assert ) {
|
|
var insertion, index,
|
|
doc = ve.dm.example.createExampleDocument( 'references' ),
|
|
internalList = doc.getInternalList();
|
|
|
|
insertion = internalList.getItemInsertion( 'mwReference/', 'foo', [] );
|
|
index = internalList.getItemNodeCount();
|
|
assert.equal( insertion.index, index, 'Insertion creates a new reference' );
|
|
assert.deepEqual(
|
|
insertion.transaction.getOperations(),
|
|
[
|
|
{ 'type': 'retain', 'length': 91 },
|
|
{
|
|
'type': 'replace',
|
|
'remove': [],
|
|
'insert': [
|
|
{ 'type': 'internalItem' },
|
|
{ 'type': '/internalItem' }
|
|
]
|
|
},
|
|
{ 'type': 'retain', 'length': 1 }
|
|
],
|
|
'New reference operations match' );
|
|
|
|
insertion = internalList.getItemInsertion( 'mwReference/', 'foo', [] );
|
|
assert.equal( insertion.index, index, 'Insertion with duplicate key reuses old index' );
|
|
assert.equal( insertion.transaction, null, 'Insertion with duplicate key has null transaction' );
|
|
} );
|