Esanders has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/268788

Change subject: Bring in unit tests from VE
......................................................................

Bring in unit tests from VE

Change-Id: I7da5f25f61d0c7f2331ab927c35d8f6a975041b8
---
M .jshintrc
M CiteHooks.php
M extension.json
A modules/ve-cite/tests/ve.dm.Converter.test.js
A modules/ve-cite/tests/ve.dm.InternalList.test.js
A modules/ve-cite/tests/ve.dm.Transaction.test.js
A modules/ve-cite/tests/ve.dm.citeExample.js
7 files changed, 1,116 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Cite 
refs/changes/88/268788/1

diff --git a/.jshintrc b/.jshintrc
index c9907b0..446e96b 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -29,6 +29,7 @@
                "mediaWiki": false,
                "mw": false,
                "OO": false,
-               "ve": false
+               "ve": false,
+               "QUnit": false
        }
 }
diff --git a/CiteHooks.php b/CiteHooks.php
index 9908a5e..9cf6e24 100644
--- a/CiteHooks.php
+++ b/CiteHooks.php
@@ -28,4 +28,35 @@
 
                return true;
        }
+
+       /**
+        * Conditionally register the unit testing module for the 
ext.cite.visualEditor module
+        * only if that module is loaded
+        *
+        * @param array $testModules The array of registered test modules
+        * @param ResourceLoader $resourceLoader The reference to the resource 
loader
+        * @return true
+        */
+       public static function onResourceLoaderTestModules( array 
&$testModules, ResourceLoader &$resourceLoader ) {
+               $resourceModules = $resourceLoader->getConfig()->get( 
'ResourceModules' );
+
+               if ( isset( $resourceModules[ 'ext.cite.visualEditor' ] ) || 
$resourceLoader->isModuleRegistered( 'ext.cite.visualEditor' ) ) {
+                       $testModules['qunit']['ext.cite.visualEditor.test'] = 
array(
+                               'scripts' => array(
+                                       
'modules/ve-cite/tests/ve.dm.citeExample.js',
+                                       
'modules/ve-cite/tests/ve.dm.Converter.test.js',
+                                       
'modules/ve-cite/tests/ve.dm.InternalList.test.js',
+                                       
'modules/ve-cite/tests/ve.dm.Transaction.test.js',
+                               ),
+                               'dependencies' => array(
+                                       'ext.cite.visualEditor',
+                                       'ext.visualEditor.test'
+                               ),
+                               'localBasePath' => dirname( __DIR__ ),
+                               'remoteExtPath' => 'Cite'
+                       );
+               }
+
+               return true;
+       }
 }
diff --git a/extension.json b/extension.json
index ddf6425..884109b 100644
--- a/extension.json
+++ b/extension.json
@@ -24,6 +24,9 @@
                ],
                "ContentHandlerDefaultModelFor": [
                        "CiteHooks::onContentHandlerDefaultModelFor"
+               ],
+               "ResourceLoaderTestModules": [
+                       "CiteHooks::onResourceLoaderTestModules"
                ]
        },
        "ResourceModules": {
diff --git a/modules/ve-cite/tests/ve.dm.Converter.test.js 
b/modules/ve-cite/tests/ve.dm.Converter.test.js
new file mode 100644
index 0000000..04b5bd6
--- /dev/null
+++ b/modules/ve-cite/tests/ve.dm.Converter.test.js
@@ -0,0 +1,40 @@
+/*!
+ * VisualEditor DataModel Cite-specific Converter tests.
+ *
+ * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+QUnit.module( 've.dm.Converter (Cite)', ve.test.utils.mwEnvironment );
+
+QUnit.test( 'getModelFromDom', function ( assert ) {
+       var msg, caseItem,
+               cases = ve.dm.citeExample.domToDataCases;
+
+       QUnit.expect( ve.test.utils.countGetModelFromDomTests( cases ) );
+
+       for ( msg in cases ) {
+               caseItem = ve.copy( cases[ msg ] );
+               if ( caseItem.mwConfig ) {
+                       mw.config.set( caseItem.mwConfig );
+               }
+
+               ve.test.utils.runGetModelFromDomTest( assert, caseItem, msg );
+       }
+} );
+
+QUnit.test( 'getDomFromModel', function ( assert ) {
+       var msg, caseItem,
+               cases = ve.dm.citeExample.domToDataCases;
+
+       QUnit.expect( 3 * Object.keys( cases ).length );
+
+       for ( msg in cases ) {
+               caseItem = ve.copy( cases[ msg ] );
+               if ( caseItem.mwConfig ) {
+                       mw.config.set( caseItem.mwConfig );
+               }
+
+               ve.test.utils.runGetDomFromModelTest( assert, caseItem, msg );
+       }
+} );
diff --git a/modules/ve-cite/tests/ve.dm.InternalList.test.js 
b/modules/ve-cite/tests/ve.dm.InternalList.test.js
new file mode 100644
index 0000000..6fd6b05
--- /dev/null
+++ b/modules/ve-cite/tests/ve.dm.InternalList.test.js
@@ -0,0 +1,239 @@
+/*!
+ * VisualEditor DataModel Cite-specific InternalList tests.
+ *
+ * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+QUnit.module( 've.dm.InternalList (Cite)', ve.test.utils.mwEnvironment );
+
+/* Tests */
+
+QUnit.test( 'addNode/removeNode', 6, function ( assert ) {
+       var doc = ve.dm.citeExample.createExampleDocument( 'references' ),
+               newInternalList = new ve.dm.InternalList( doc ),
+               referenceNodes = [
+                       doc.getDocumentNode().children[ 0 ].children[ 0 ],
+                       doc.getDocumentNode().children[ 1 ].children[ 1 ],
+                       doc.getDocumentNode().children[ 1 ].children[ 3 ],
+                       doc.getDocumentNode().children[ 1 ].children[ 5 ],
+                       doc.getDocumentNode().children[ 2 ].children[ 0 ],
+                       doc.getDocumentNode().children[ 2 ].children[ 1 ]
+               ],
+               expectedNodes = {
+                       'mwReference/': {
+                               keyedNodes: {
+                                       'auto/0': [ referenceNodes[ 0 ] ],
+                                       'literal/bar': [ referenceNodes[ 1 ], 
referenceNodes[ 3 ] ],
+                                       'literal/:3': [ referenceNodes[ 2 ] ],
+                                       'auto/1': [ referenceNodes[ 4 ] ]
+                               },
+                               firstNodes: [
+                                       referenceNodes[ 0 ],
+                                       referenceNodes[ 1 ],
+                                       referenceNodes[ 2 ],
+                                       referenceNodes[ 4 ]
+                               ],
+                               indexOrder: [ 0, 1, 2, 3 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       },
+                       'mwReference/foo': {
+                               keyedNodes: {
+                                       'auto/2': [ referenceNodes[ 5 ] ]
+                               },
+                               firstNodes: [ undefined, undefined, undefined, 
undefined, referenceNodes[ 5 ] ],
+                               indexOrder: [ 4 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       }
+               };
+
+       assert.deepEqualWithNodeTree(
+               doc.internalList.nodes,
+               expectedNodes,
+               'Document construction populates internal list correctly'
+       );
+
+       newInternalList.addNode( 'mwReference/', 'auto/0', 0, referenceNodes[ 0 
] );
+       newInternalList.addNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 1 ] );
+       newInternalList.addNode( 'mwReference/', 'literal/:3', 2, 
referenceNodes[ 2 ] );
+       newInternalList.addNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 3 ] );
+       newInternalList.addNode( 'mwReference/', 'auto/1', 3, referenceNodes[ 4 
] );
+       newInternalList.addNode( 'mwReference/foo', 'auto/2', 4, 
referenceNodes[ 5 ] );
+       newInternalList.onTransact();
+
+       assert.deepEqualWithNodeTree(
+               newInternalList.nodes,
+               expectedNodes,
+               'Nodes added in order'
+       );
+
+       newInternalList = new ve.dm.InternalList( doc );
+
+       newInternalList.addNode( 'mwReference/foo', 'auto/2', 4, 
referenceNodes[ 5 ] );
+       newInternalList.addNode( 'mwReference/', 'auto/1', 3, referenceNodes[ 4 
] );
+       newInternalList.addNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 3 ] );
+       newInternalList.addNode( 'mwReference/', 'literal/:3', 2, 
referenceNodes[ 2 ] );
+       newInternalList.addNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 1 ] );
+       newInternalList.addNode( 'mwReference/', 'auto/0', 0, referenceNodes[ 0 
] );
+       newInternalList.onTransact();
+
+       assert.deepEqualWithNodeTree(
+               newInternalList.nodes,
+               expectedNodes,
+               'Nodes added in reverse order'
+       );
+
+       newInternalList.removeNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 1 ] );
+       newInternalList.onTransact();
+
+       assert.deepEqualWithNodeTree(
+               newInternalList.nodes,
+               {
+                       'mwReference/': {
+                               keyedNodes: {
+                                       'auto/0': [ referenceNodes[ 0 ] ],
+                                       'literal/bar': [ referenceNodes[ 3 ] ],
+                                       'literal/:3': [ referenceNodes[ 2 ] ],
+                                       'auto/1': [ referenceNodes[ 4 ] ]
+                               },
+                               firstNodes: [
+                                       referenceNodes[ 0 ],
+                                       referenceNodes[ 3 ],
+                                       referenceNodes[ 2 ],
+                                       referenceNodes[ 4 ]
+                               ],
+                               indexOrder: [ 0, 2, 1, 3 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       },
+                       'mwReference/foo': {
+                               keyedNodes: {
+                                       'auto/2': [ referenceNodes[ 5 ] ]
+                               },
+                               firstNodes: [ undefined, undefined, undefined, 
undefined, referenceNodes[ 5 ] ],
+                               indexOrder: [ 4 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       }
+               },
+               'Keys re-ordered after one item of key removed'
+       );
+
+       newInternalList.removeNode( 'mwReference/', 'literal/bar', 1, 
referenceNodes[ 3 ] );
+       newInternalList.onTransact();
+
+       assert.deepEqualWithNodeTree(
+               newInternalList.nodes,
+               {
+                       'mwReference/': {
+                               keyedNodes: {
+                                       'auto/0': [ referenceNodes[ 0 ] ],
+                                       'literal/:3': [ referenceNodes[ 2 ] ],
+                                       'auto/1': [ referenceNodes[ 4 ] ]
+                               },
+                               firstNodes: [
+                                       referenceNodes[ 0 ],
+                                       undefined,
+                                       referenceNodes[ 2 ],
+                                       referenceNodes[ 4 ]
+                               ],
+                               indexOrder: [ 0, 2, 3 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       },
+                       'mwReference/foo': {
+                               keyedNodes: {
+                                       'auto/2': [ referenceNodes[ 5 ] ]
+                               },
+                               firstNodes: [ undefined, undefined, undefined, 
undefined, referenceNodes[ 5 ] ],
+                               indexOrder: [ 4 ],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       }
+               },
+               'Keys truncated after last item of key removed'
+       );
+
+       newInternalList.removeNode( 'mwReference/', 'auto/0', 0, 
referenceNodes[ 0 ] );
+       newInternalList.removeNode( 'mwReference/foo', 'auto/2', 4, 
referenceNodes[ 5 ] );
+       newInternalList.removeNode( 'mwReference/', 'auto/1', 3, 
referenceNodes[ 4 ] );
+       newInternalList.removeNode( 'mwReference/', 'literal/:3', 2, 
referenceNodes[ 2 ] );
+       newInternalList.onTransact();
+
+       assert.deepEqualWithNodeTree(
+               newInternalList.nodes,
+               {
+                       'mwReference/': {
+                               keyedNodes: {},
+                               firstNodes: new Array( 4 ),
+                               indexOrder: [],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       },
+                       'mwReference/foo': {
+                               keyedNodes: {},
+                               firstNodes: new Array( 5 ),
+                               indexOrder: [],
+                               uniqueListKeys: {},
+                               uniqueListKeysInUse: {}
+                       }
+               },
+               'All nodes removed'
+       );
+} );
+
+QUnit.test( 'getItemInsertion', 4, function ( assert ) {
+       var insertion, index,
+               doc = ve.dm.citeExample.createExampleDocument( 'references' ),
+               internalList = doc.getInternalList();
+
+       insertion = internalList.getItemInsertion( 'mwReference/', 
'literal/foo', [] );
+       index = internalList.getItemNodeCount();
+       assert.strictEqual( 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' }
+                               ],
+                               insertedDataOffset: 0,
+                               insertedDataLength: 2
+                       },
+                       { type: 'retain', length: 1 }
+               ],
+               'New reference operations match' );
+
+       insertion = internalList.getItemInsertion( 'mwReference/', 
'literal/foo', [] );
+       assert.strictEqual( insertion.index, index, 'Insertion with duplicate 
key reuses old index' );
+       assert.strictEqual( insertion.transaction, null, 'Insertion with 
duplicate key has null transaction' );
+} );
+
+QUnit.test( 'getUniqueListKey', 7, function ( assert ) {
+       var generatedName,
+               doc = ve.dm.citeExample.createExampleDocument( 'references' ),
+               internalList = doc.getInternalList();
+
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/0', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:0', '0 maps to 0' );
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/1', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:1', '1 maps to 1' );
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/2', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:2', '2 maps to 2' );
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/3', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:4', '3 maps to 4 (because 
a literal :3 is present)' );
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/4', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:5', '4 maps to 5' );
+
+       generatedName = internalList.getUniqueListKey( 'mwReference/', 
'auto/0', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:0', 'Reusing a key reuses 
the name' );
+
+       generatedName = internalList.getUniqueListKey( 'mwReference/foo', 
'auto/4', 'literal/:' );
+       assert.strictEqual( generatedName, 'literal/:0', 'Different groups are 
treated separately' );
+} );
diff --git a/modules/ve-cite/tests/ve.dm.Transaction.test.js 
b/modules/ve-cite/tests/ve.dm.Transaction.test.js
new file mode 100644
index 0000000..174a3d4
--- /dev/null
+++ b/modules/ve-cite/tests/ve.dm.Transaction.test.js
@@ -0,0 +1,193 @@
+/*!
+ * VisualEditor DataModel Cite-specific Transaction tests.
+ *
+ * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+QUnit.module( 've.dm.Transaction (Cite)', ve.test.utils.mwEnvironment );
+
+// FIXME: Duplicates test runner; should be using a data provider
+QUnit.test( 'newFromDocumentInsertion with references', function ( assert ) {
+       var i, j, doc2, tx, actualStoreItems, expectedStoreItems, removalOps, 
doc,
+               complexDoc = ve.dm.citeExample.createExampleDocument( 
'complexInternalData' ),
+               comment = { type: 'alienMeta', originalDomElements: $( '<!-- 
hello -->' ).toArray() },
+               withReference = [
+                       { type: 'paragraph' },
+                       'B', 'a', 'r',
+                       { type: 'mwReference', attributes: {
+                               mw: {},
+                               about: '#mwt4',
+                               listIndex: 0,
+                               listGroup: 'mwReference/',
+                               listKey: 'auto/0',
+                               refGroup: '',
+                               contentsUsed: true
+                       } },
+                       { type: '/mwReference' },
+                       { type: '/paragraph' },
+                       { type: 'internalList' },
+                       { type: 'internalItem' },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       'B',
+                       'a',
+                       'z',
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: '/internalList' }
+               ],
+               cases = [
+                       {
+                               msg: 'metadata insertion',
+                               doc: 'complexInternalData',
+                               offset: 0,
+                               range: new ve.Range( 0, 7 ),
+                               modify: function ( newDoc ) {
+                                       newDoc.commit( 
ve.dm.Transaction.newFromMetadataInsertion(
+                                               newDoc, 4, 0, [ comment ]
+                                       ) );
+                               },
+                               removalOps: [
+                                       {
+                                               type: 'replace',
+                                               remove: complexDoc.getData( new 
ve.Range( 0, 7 ) ),
+                                               insert: [
+                                                       { type: 'paragraph' },
+                                                       { type: '/paragraph' }
+                                               ],
+                                               removeMetadata: 
complexDoc.getMetadata( new ve.Range( 0, 7 ) ),
+                                               insertMetadata: [ undefined, 
undefined ],
+                                               insertedDataLength: 2,
+                                               insertedDataOffset: 0
+                                       },
+                                       { type: 'retain', length: 26 }
+                               ],
+                               expectedOps: [
+                                       {
+                                               type: 'replace',
+                                               remove: [],
+                                               insert: complexDoc.getData( new 
ve.Range( 0, 4 ) )
+                                                       // Reference gets 
(unnecessarily) renumbered from auto/0 to auto/1
+                                                       .concat( [
+                                                               
ve.extendObject( true, {}, complexDoc.data.data[ 4 ],
+                                                                       { 
attributes: { listKey: 'auto/1' } }
+                                                               )
+                                                       ] )
+                                                       .concat( 
complexDoc.getData( new ve.Range( 5, 7 ) ) ),
+                                               removeMetadata: [],
+                                               insertMetadata: 
complexDoc.getMetadata( new ve.Range( 0, 4 ) )
+                                                       .concat( [ [ comment ] 
] )
+                                                       .concat( 
complexDoc.getMetadata( new ve.Range( 5, 7 ) ) )
+                                       },
+                                       { type: 'retain', length: 3 },
+                                       {
+                                               type: 'replace',
+                                               remove: complexDoc.getData( new 
ve.Range( 8, 32 ) ),
+                                               insert: complexDoc.getData( new 
ve.Range( 8, 32 ) ),
+                                               removeMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 32 ) ),
+                                               insertMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 32 ) )
+                                       },
+                                       { type: 'retain', length: 1 }
+                               ]
+                       },
+                       {
+                               msg: 'metadata removal',
+                               doc: 'complexInternalData',
+                               offset: 24,
+                               range: new ve.Range( 24, 31 ),
+                               modify: function ( newDoc ) {
+                                       newDoc.commit( 
ve.dm.Transaction.newFromMetadataRemoval(
+                                               newDoc, 6, new ve.Range( 0, 1 )
+                                       ) );
+                               },
+                               removalOps: [
+                                       { type: 'retain', length: 24 },
+                                       {
+                                               type: 'replace',
+                                               remove: complexDoc.getData( new 
ve.Range( 24, 31 ) ),
+                                               insert: [],
+                                               removeMetadata: 
complexDoc.getMetadata( new ve.Range( 24, 31 ) ),
+                                               insertMetadata: []
+                                       },
+                                       { type: 'retain', length: 2 }
+                               ],
+                               expectedOps: [
+                                       { type: 'retain', length: 8 },
+                                       {
+                                               type: 'replace',
+                                               remove: complexDoc.getData( new 
ve.Range( 8, 24 ) )
+                                                       .concat( 
complexDoc.getData( new ve.Range( 31, 32 ) ) ),
+                                               insert: complexDoc.getData( new 
ve.Range( 8, 32 ) ),
+                                               removeMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 24 ) )
+                                                       .concat( 
complexDoc.getMetadata( new ve.Range( 31, 32 ) ) ),
+                                               insertMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 30 ) )
+                                                       .concat( [ [] ] )
+                                                       .concat( 
complexDoc.getMetadata( new ve.Range( 31, 32 ) ) )
+                                       },
+                                       { type: 'retain', length: 1 }
+                               ]
+                       },
+                       {
+                               msg: 'inserting a brand new document; internal 
lists are merged and items renumbered',
+                               doc: 'complexInternalData',
+                               offset: 7,
+                               newDocData: withReference,
+                               removalOps: [],
+                               expectedOps: [
+                                       { type: 'retain', length: 7 },
+                                       {
+                                               type: 'replace',
+                                               remove: [],
+                                               insert: withReference.slice( 0, 
4 )
+                                                       // Renumber listIndex 
from 0 to 2
+                                                       // Renumber listKey 
from auto/0 to auto/1
+                                                       .concat( [
+                                                               
ve.extendObject( true, {}, withReference[ 4 ],
+                                                                       { 
attributes: { listIndex: 2, listKey: 'auto/1' } }
+                                                               )
+                                                       ] )
+                                                       .concat( 
withReference.slice( 5, 7 ) )
+                                       },
+                                       { type: 'retain', length: 1 },
+                                       {
+                                               type: 'replace',
+                                               remove: complexDoc.getData( new 
ve.Range( 8, 32 ) ),
+                                               insert: complexDoc.getData( new 
ve.Range( 8, 32 ) )
+                                                       .concat( 
withReference.slice( 8, 15 ) ),
+                                               removeMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 32 ) ),
+                                               insertMetadata: 
complexDoc.getMetadata( new ve.Range( 8, 32 ) )
+                                                       .concat( new Array( 7 ) 
)
+                                       },
+                                       { type: 'retain', length: 1 }
+                               ]
+                       }
+               ];
+       QUnit.expect( 3 * cases.length );
+       for ( i = 0; i < cases.length; i++ ) {
+               doc = ve.dm.citeExample.createExampleDocument( cases[ i ].doc );
+               if ( cases[ i ].newDocData ) {
+                       doc2 = new ve.dm.Document( cases[ i ].newDocData );
+                       removalOps = [];
+               } else if ( cases[ i ].range ) {
+                       doc2 = doc.cloneFromRange( cases[ i ].range );
+                       cases[ i ].modify( doc2 );
+                       tx = ve.dm.Transaction.newFromRemoval( doc, cases[ i 
].range, true );
+                       doc.commit( tx );
+                       removalOps = tx.getOperations();
+               }
+
+               assert.deepEqualWithDomElements( removalOps, cases[ i 
].removalOps, cases[ i ].msg + ': removal' );
+
+               tx = ve.dm.Transaction.newFromDocumentInsertion( doc, cases[ i 
].offset, doc2 );
+               assert.deepEqualWithDomElements( tx.getOperations(), cases[ i 
].expectedOps, cases[ i ].msg + ': transaction' );
+
+               actualStoreItems = [];
+               expectedStoreItems = cases[ i ].expectedStoreItems || [];
+               for ( j = 0; j < expectedStoreItems.length; j++ ) {
+                       actualStoreItems[ j ] = doc.store.value( 
doc.store.indexOfHash(
+                               OO.getHash( expectedStoreItems[ j ] )
+                       ) );
+               }
+               assert.deepEqual( actualStoreItems, expectedStoreItems, cases[ 
i ].msg + ': store items' );
+       }
+} );
diff --git a/modules/ve-cite/tests/ve.dm.citeExample.js 
b/modules/ve-cite/tests/ve.dm.citeExample.js
new file mode 100644
index 0000000..c1efabc
--- /dev/null
+++ b/modules/ve-cite/tests/ve.dm.citeExample.js
@@ -0,0 +1,608 @@
+/*!
+ * VisualEditor DataModel Cite-specific example data sets.
+ *
+ * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+ve.dm.citeExample = {};
+
+ve.dm.citeExample.createExampleDocument = function ( name, store ) {
+       return ve.dm.example.createExampleDocumentFromObject( name, store, 
ve.dm.citeExample );
+};
+
+ve.dm.citeExample.domToDataCases = {
+       'mw:Reference': {
+               // Wikitext:
+               // Foo<ref name="bar" /> Baz<ref group="g1" 
name=":0">Quux</ref> Whee<ref name="bar">[[Bar]]</ref> Yay<ref group="g1">No 
name</ref> Quux<ref name="bar">Different content</ref> Foo<ref group="g1" 
name="foo" />
+               // <references group="g1"><ref group="g1" name="foo">Ref in 
refs</ref></references>
+               body:
+                       '<p>Foo' +
+                               '<span about="#mwt1" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}"
 id="cite_ref-bar-1-0" rel="dc:references" typeof="mw:Extension/ref" 
data-parsoid="{}">' +
+                                       '<a href="#cite_note-bar-1">[1]</a>' +
+                               '</span>' +
+                               ' Baz' +
+                               '<span about="#mwt2" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Quux&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;:0&quot;}}"
 id="cite_ref-quux-2-0" rel="dc:references" typeof="mw:Extension/ref" 
data-parsoid="{}">' +
+                                       '<a href="#cite_note-.3A0-2">[g1 
1]</a>' +
+                               '</span>' +
+                               ' Whee' +
+                               '<span about="#mwt3" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;'
 +
+                               '<a rel=\\&quot;mw:WikiLink\\&quot; 
href=\\&quot;./Bar\\&quot;>Bar' +
+                               
'</a>&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" 
id="cite_ref-bar-1-1" rel="dc:references" typeof="mw:Extension/ref" 
data-parsoid="{}">' +
+                                       '<a href="#cite_note-bar-1">[1]</a>' +
+                               '</span>' +
+                               ' Yay' +
+                               // This reference has .body.id instead of 
.body.html
+                               '<span about="#mwt4" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-cite-3&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;}}"
 id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" 
data-parsoid="{}">' +
+                                       '<a href="#cite_note-3">[g1 2]</a>' +
+                               '</span>' +
+                               ' Quux' +
+                               '<span about="#mwt5" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Different
 content&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" 
id="cite_ref-bar-1-2" rel="dc:references" typeof="mw:Extension/ref" 
data-parsoid="{}">' +
+                                       '<a href="#cite_note-bar-1">[1]</a>' +
+                               '</span>' +
+                               ' Foo' +
+                               '<span about="#mwt6" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;foo&quot;}}"
 ' +
+                                       'id="cite_ref-foo-4" 
rel="dc:references" typeof="mw:Extension/ref" data-parsoid="{}">' +
+                                       '<a href="#cite_ref-foo-4">[g1 3]</a>' +
+                               '</span>' +
+                       '</p>' +
+                       // The HTML below is enriched to wrap reference 
contents in <span id="mw-cite-[...]">
+                       // which Parsoid doesn't do yet, but T88290 asks for
+                       '<ol class="references" 
typeof="mw:Extension/references" about="#mwt7" data-parsoid="{}"' +
+                               
'data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;body&quot;:{' +
+                               '&quot;html&quot;:&quot;<span 
about=\\&quot;#mwt8\\&quot; class=\\&quot;reference\\&quot; ' +
+                               
'data-mw=\\&quot;{&amp;quot;name&amp;quot;:&amp;quot;ref&amp;quot;,&amp;quot;body&amp;quot;:{&amp;quot;html&amp;quot;:&amp;quot;Ref
 in refs&amp;quot;},' +
+                               
'&amp;quot;attrs&amp;quot;:{&amp;quot;group&amp;quot;:&amp;quot;g1&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;foo&amp;quot;}}\\&quot;
 ' +
+                               'rel=\\&quot;dc:references\\&quot; 
typeof=\\&quot;mw:Extension/ref\\&quot;>' +
+                               '<a 
href=\\&quot;#cite_note-foo-3\\&quot;>[3]</a></span>&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;}}">'
 +
+                               '<li about="#cite_note-.3A0-2" 
id="cite_note-.3A0-2"><span rel="mw:referencedBy"><a 
href="#cite_ref-.3A0_2-0">↑</a></span> <span id="mw-cite-:0">Quux</span></li>' +
+                               '<li about="#cite_note-3" 
id="cite_note-3"><span rel="mw:referencedBy"><a href="#cite_ref-3">↑</a></span> 
<span id="mw-cite-3">No name</span></li>' +
+                               '<li about="#cite_note-foo-4" 
id="cite_note-foo-4"><span rel="mw:referencedBy"><a 
href="#cite_ref-foo_4-0">↑</a></span> <span id="mw-cite-foo">Ref in 
refs</span></li>' +
+                       '</ol>',
+               fromDataBody:
+                       '<p>Foo' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}"
 typeof="mw:Extension/ref">' +
+                               '</span>' +
+                               ' Baz' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Quux&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;:0&quot;}}"
 typeof="mw:Extension/ref">' +
+                               '</span>' +
+                               ' Whee' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;'
 +
+                               '<a rel=\\&quot;mw:WikiLink\\&quot; 
href=\\&quot;./Bar\\&quot;>Bar' +
+                               
'</a>&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" 
typeof="mw:Extension/ref">' +
+                               '</span>' +
+                               ' Yay' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-cite-3&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;}}"
 typeof="mw:Extension/ref">' +
+                               '</span>' +
+                               ' Quux' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Different
 content&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" 
typeof="mw:Extension/ref">' +
+                               '</span>' +
+                               ' Foo' +
+                               '<span 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;foo&quot;}}"
 ' +
+                                       'typeof="mw:Extension/ref">' +
+                               '</span>' +
+                       '</p>' +
+                       '<div typeof="mw:Extension/references" ' +
+                               
'data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;},&quot;body&quot;:{'
 +
+                               '&quot;html&quot;:&quot;<span 
typeof=\\&quot;mw:Extension/ref\\&quot; ' +
+                               
'data-mw=\\&quot;{&amp;quot;name&amp;quot;:&amp;quot;ref&amp;quot;,&amp;quot;body&amp;quot;:{&amp;quot;html&amp;quot;:&amp;quot;Ref
 in refs&amp;quot;},' +
+                               
'&amp;quot;attrs&amp;quot;:{&amp;quot;group&amp;quot;:&amp;quot;g1&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;foo&amp;quot;}}\\&quot;>'
 +
+                               '</span>&quot;}}">' +
+                       '</div>',
+               clipboardBody:
+                       '<p>Foo' +
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}">'
 +
+                                       '<sup>[1]</sup>' +
+                               '</span>' +
+                               ' Baz' +
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Quux&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;:0&quot;}}">'
 +
+                                       '<sup>[g1 1]</sup>' +
+                               '</span>' +
+                               ' Whee' +
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;'
 +
+                               '<a href=\\&quot;./Bar\\&quot; 
rel=\\&quot;mw:WikiLink\\&quot;>Bar' +
+                               
'</a>&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}">' +
+                                       '<sup>[1]</sup>' +
+                               '</span>' +
+                               ' Yay' +
+                               // This reference has .body.id instead of 
.body.html
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-cite-3&quot;,&quot;html&quot;:&quot;No
 name&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;}}">' +
+                                       '<sup>[g1 2]</sup>' +
+                               '</span>' +
+                               ' Quux' +
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Different
 content&quot;},&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}">' +
+                                       '<sup>[1]</sup>' +
+                               '</span>' +
+                               ' Foo' +
+                               '<span typeof="mw:Extension/ref" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;foo&quot;}}">'
 +
+                                       '<sup>[g1 3]</sup>' +
+                               '</span>' +
+                       '</p>' +
+                       // The HTML below is enriched to wrap reference 
contents in <span id="mw-cite-[...]">
+                       // which Parsoid doesn't do yet, but T88290 asks for
+                       '<div typeof="mw:Extension/references"' +
+                               
'data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;},&quot;body&quot;:{'
 +
+                               '&quot;html&quot;:&quot;<span 
typeof=\\&quot;mw:Extension/ref\\&quot; ' +
+                               
'data-mw=\\&quot;{&amp;quot;name&amp;quot;:&amp;quot;ref&amp;quot;,&amp;quot;attrs&amp;quot;:{&amp;quot;group&amp;quot;:&amp;quot;g1&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;foo&amp;quot;},&amp;quot;body&amp;quot;:{&amp;quot;html&amp;quot;:&amp;quot;Ref
 in refs&amp;quot;}}' +
+                               '\\&quot;><sup>[g1 3]</sup></span>&quot;}}">' +
+                       '</div>',
+               head: '<base href="http://example.com"; />',
+               data: [
+                       { type: 'paragraph' },
+                       'F', 'o', 'o',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listIndex: 0,
+                                       listGroup: 'mwReference/',
+                                       listKey: 'literal/bar',
+                                       refGroup: '',
+                                       mw: { name: 'ref', attrs: { name: 'bar' 
} },
+                                       originalMw: 
'{"name":"ref","attrs":{"name":"bar"}}',
+                                       contentsUsed: false
+                               }
+                       },
+                       { type: '/mwReference' },
+                       ' ', 'B', 'a', 'z',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listIndex: 1,
+                                       listGroup: 'mwReference/g1',
+                                       listKey: 'literal/:0',
+                                       refGroup: 'g1',
+                                       mw: { name: 'ref', body: { html: 'Quux' 
}, attrs: { group: 'g1', name: ':0' } },
+                                       originalMw: 
'{"name":"ref","body":{"html":"Quux"},"attrs":{"group":"g1","name":":0"}}',
+                                       contentsUsed: true
+                               }
+                       },
+                       { type: '/mwReference' },
+                       ' ', 'W', 'h', 'e', 'e',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listIndex: 0,
+                                       listGroup: 'mwReference/',
+                                       listKey: 'literal/bar',
+                                       refGroup: '',
+                                       mw: { name: 'ref', body: { html: '<a 
rel="mw:WikiLink" href="./Bar">Bar</a>' }, attrs: { name: 'bar' } },
+                                       originalMw: 
'{"name":"ref","body":{"html":"<a rel=\\"mw:WikiLink\\" 
href=\\"./Bar\\">Bar</a>"},"attrs":{"name":"bar"}}',
+                                       contentsUsed: true
+                               }
+                       },
+                       { type: '/mwReference' },
+                       ' ', 'Y', 'a', 'y',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listIndex: 2,
+                                       listGroup: 'mwReference/g1',
+                                       listKey: 'auto/0',
+                                       refGroup: 'g1',
+                                       mw: { name: 'ref', body: { id: 
'mw-cite-3' }, attrs: { group: 'g1' } },
+                                       originalMw: 
'{"name":"ref","body":{"id":"mw-cite-3"},"attrs":{"group":"g1"}}',
+                                       contentsUsed: true,
+                                       refListItemId: 'mw-cite-3'
+                               }
+                       },
+                       { type: '/mwReference' },
+                       ' ', 'Q', 'u', 'u', 'x',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listIndex: 0,
+                                       listGroup: 'mwReference/',
+                                       listKey: 'literal/bar',
+                                       refGroup: '',
+                                       mw: { name: 'ref', body: { html: 
'Different content' }, attrs: { name: 'bar' } },
+                                       originalMw: 
'{"name":"ref","body":{"html":"Different content"},"attrs":{"name":"bar"}}',
+                                       contentsUsed: false
+                               }
+                       },
+                       { type: '/mwReference' },
+                       ' ', 'F', 'o', 'o',
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       listGroup: 'mwReference/g1',
+                                       listIndex: 3,
+                                       listKey: 'literal/foo',
+                                       refGroup: 'g1',
+                                       mw: { name: 'ref', attrs: { group: 
'g1', name: 'foo' } },
+                                       originalMw: 
'{"name":"ref","attrs":{"group":"g1","name":"foo"}}',
+                                       contentsUsed: false
+                               }
+                       },
+                       { type: '/mwReference' },
+                       { type: '/paragraph' },
+                       {
+                               type: 'mwReferencesList',
+                               attributes: {
+                                       mw: {
+                                               name: 'references',
+                                               attrs: { group: 'g1' },
+                                               body: {
+                                                       html: '<span 
about="#mwt8" class="reference" 
data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Ref
 in 
refs&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;foo&quot;}}"
 rel="dc:references" typeof="mw:Extension/ref"><a 
href="#cite_note-foo-3">[3]</a></span>'
+                                               }
+                                       },
+                                       originalMw: 
'{"name":"references","body":{"html":"<span about=\\"#mwt8\\" 
class=\\"reference\\" 
data-mw=\\"{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;Ref
 in 
refs&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;g1&quot;,&quot;name&quot;:&quot;foo&quot;}}\\"
 rel=\\"dc:references\\" typeof=\\"mw:Extension/ref\\"><a 
href=\\"#cite_note-foo-3\\">[3]</a></span>"},"attrs":{"group":"g1"}}',
+                                       listGroup: 'mwReference/g1',
+                                       refGroup: 'g1'
+                               }
+                       },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       contentsUsed: true,
+                                       listGroup: 'mwReference/g1',
+                                       listIndex: 3,
+                                       listKey: 'literal/foo',
+                                       mw: { name: 'ref', attrs: { group: 
'g1', name: 'foo' }, body: { html: 'Ref in refs' } },
+                                       originalMw: 
'{"name":"ref","body":{"html":"Ref in 
refs"},"attrs":{"group":"g1","name":"foo"}}',
+                                       refGroup: 'g1'
+                               }
+                       },
+                       { type: '/mwReference' },
+                       { type: '/paragraph' },
+                       { type: '/mwReferencesList' },
+                       { type: 'internalList' },
+                       { type: 'internalItem', attributes: { originalHtml: '<a 
rel="mw:WikiLink" href="./Bar">Bar</a>' } },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       [
+                               'B',
+                               [ {
+                                       type: 'link/mwInternal',
+                                       attributes: {
+                                               title: 'Bar',
+                                               origTitle: 'Bar',
+                                               normalizedTitle: 'Bar',
+                                               lookupTitle: 'Bar',
+                                               hrefPrefix: './'
+                                       }
+                               } ]
+                       ],
+                       [
+                               'a',
+                               [ {
+                                       type: 'link/mwInternal',
+                                       attributes: {
+                                               title: 'Bar',
+                                               origTitle: 'Bar',
+                                               normalizedTitle: 'Bar',
+                                               lookupTitle: 'Bar',
+                                               hrefPrefix: './'
+                                       }
+                               } ]
+                       ],
+                       [
+                               'r',
+                               [ {
+                                       type: 'link/mwInternal',
+                                       attributes: {
+                                               title: 'Bar',
+                                               origTitle: 'Bar',
+                                               normalizedTitle: 'Bar',
+                                               lookupTitle: 'Bar',
+                                               hrefPrefix: './'
+                                       }
+                               } ]
+                       ],
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: 'internalItem', attributes: { originalHtml: 
'Quux' } },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       'Q', 'u', 'u', 'x',
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: 'internalItem', attributes: { originalHtml: 'No 
name' } },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       'N', 'o', ' ', 'n', 'a', 'm', 'e',
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: 'internalItem', attributes: { originalHtml: 
'Ref in refs' } },
+                       { type: 'paragraph', internal: { generated: 'wrapper' } 
},
+                       'R', 'e', 'f', ' ', 'i', 'n', ' ', 'r', 'e', 'f', 's',
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: '/internalList' }
+               ]
+       },
+       'mw:Reference with metadata': {
+               body: '<p><span about="#mwt2" class="reference" ' +
+                       
'data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:' +
+                       '{&quot;html&quot;:&quot;Foo<!-- bar 
-->&quot;},&quot;attrs&quot;:{}}" ' +
+                       'id="cite_ref-1-0" rel="dc:references" 
typeof="mw:Extension/ref" data-parsoid="{}">' +
+                       '<a href="#cite_note-bar-1" 
data-parsoid="{}">[1]</a></span></p>',
+               fromDataBody: '<p><span ' +
+                       
'data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:' +
+                       '{&quot;html&quot;:&quot;Foo<!-- bar 
-->&quot;},&quot;attrs&quot;:{}}" ' +
+                       'typeof="mw:Extension/ref"></span></p>',
+               clipboardBody: '<p><span typeof="mw:Extension/ref" ' +
+                       'data-mw="{&quot;attrs&quot;:{},&quot;body&quot;:' +
+                       '{&quot;html&quot;:&quot;Foo<span 
rel=\\&quot;ve:Comment\\&quot; data-ve-comment=\\&quot; bar 
\\&quot;>&amp;nbsp;</span>&quot;},&quot;name&quot;:&quot;ref&quot;}" ' +
+                       '>' +
+                       '<sup>[1]</sup></span></p>',
+               head: '<base href="http://example.com"; />',
+               data: [
+                       { type: 'paragraph' },
+                       {
+                               type: 'mwReference',
+                               attributes: {
+                                       contentsUsed: true,
+                                       listGroup: 'mwReference/',
+                                       listIndex: 0,
+                                       listKey: 'auto/0',
+                                       mw: {
+                                               attrs: {},
+                                               body: {
+                                                       html: 'Foo<!-- bar -->'
+                                               },
+                                               name: 'ref'
+                                       },
+                                       originalMw: 
'{"name":"ref","body":{"html":"Foo<!-- bar -->"},"attrs":{}}',
+                                       refGroup: ''
+                               }
+                       },
+                       { type: '/mwReference' },
+                       { type: '/paragraph' },
+                       { type: 'internalList' },
+                       { type: 'internalItem', attributes: { originalHtml: 
'Foo<!-- bar -->' } },
+                       {
+                               internal: {
+                                       generated: 'wrapper'
+                               },
+                               type: 'paragraph'
+                       },
+                       'F', 'o', 'o',
+                       {
+                               type: 'comment',
+                               attributes: {
+                                       text: ' bar '
+                               }
+                       },
+                       { type: '/comment' },
+                       { type: '/paragraph' },
+                       { type: '/internalItem' },
+                       { type: '/internalList' }
+               ]
+       }
+};
+
+ve.dm.citeExample.references = [
+       { type: 'paragraph' },
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: true,
+                       listGroup: 'mwReference/',
+                       listIndex: 0,
+                       listKey: 'auto/0',
+                       mw: {
+                               attrs: {},
+                               body: { html: 'No name 1' },
+                               name: 'ref'
+                       },
+                       originalMw: '{"name":"ref","body":{"html":"No name 
1"},"attrs":{}}',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReference' },
+       { type: '/paragraph' },
+       { type: 'paragraph' },
+       'F', 'o', 'o',
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: true,
+                       listGroup: 'mwReference/',
+                       listIndex: 1,
+                       listKey: 'literal/bar',
+                       mw: {
+                               attrs: { name: 'bar' },
+                               body: { html: 'Bar' },
+                               name: 'ref'
+                       },
+                       originalMw: 
'{"body":{"html":""},"attrs":{"name":"bar"}}',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReference' },
+       ' ', 'B', 'a', 'z',
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: true,
+                       listGroup: 'mwReference/',
+                       listIndex: 2,
+                       listKey: 'literal/:3',
+                       mw: {
+                               attrs: { name: ':3' },
+                               body: { html: 'Quux' },
+                               name: 'ref'
+                       },
+                       originalMw: 
'{"name":"ref","body":{"html":"Quux"},"attrs":{"name":":3"}}',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReference' },
+       ' ', 'W', 'h', 'e', 'e',
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: false,
+                       listGroup: 'mwReference/',
+                       listIndex: 1,
+                       listKey: 'literal/bar',
+                       mw: {
+                               attrs: { name: 'bar' },
+                               name: 'ref'
+                       },
+                       originalMw: 
'{"body":{"html":""},"attrs":{"name":"bar"}}',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReference' },
+       ' ', 'Y', 'a', 'y',
+       { type: '/paragraph' },
+       { type: 'paragraph' },
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: true,
+                       listGroup: 'mwReference/',
+                       listIndex: 3,
+                       listKey: 'auto/1',
+                       mw: {
+                               attrs: {},
+                               body: { html: 'No name 2' },
+                               name: 'ref'
+                       },
+                       originalMw: '{"name":"ref","body":{"html":"No name 
2"},"attrs":{}}',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReference' },
+       {
+               type: 'mwReference',
+               attributes: {
+                       contentsUsed: true,
+                       listGroup: 'mwReference/foo',
+                       listIndex: 4,
+                       listKey: 'auto/2',
+                       mw: {
+                               attrs: { group: 'foo' },
+                               body: { html: 'No name 3' },
+                               name: 'ref'
+                       },
+                       originalMw: '{"name":"ref","body":{"html":"No name 
3"},"attrs":{"group":"foo"}}',
+                       refGroup: 'foo'
+               }
+       },
+       { type: '/mwReference' },
+       { type: '/paragraph' },
+       {
+               type: 'mwReferencesList',
+               // originalDomElements: HTML,
+               attributes: {
+                       mw: {
+                               name: 'references',
+                               attrs: { group: 'g1' }
+                       },
+                       originalMw: 
'{"name":"references","attrs":{"group":"g1"}"}',
+                       listGroup: 'mwReference/',
+                       refGroup: ''
+               }
+       },
+       { type: '/mwReferencesList' },
+       { type: 'internalList' },
+       { type: 'internalItem' },
+       { type: 'paragraph' },
+       'N', 'o', ' ', 'n', 'a', 'm', 'e', ' ', '1',
+       { type: '/paragraph' },
+       { type: '/internalItem' },
+       { type: 'internalItem' },
+       { type: 'paragraph' },
+       'B', 'a', 'r',
+       { type: '/paragraph' },
+       { type: '/internalItem' },
+       { type: 'internalItem' },
+       { type: 'paragraph' },
+       'Q', 'u', 'u', 'x',
+       { type: '/paragraph' },
+       { type: '/internalItem' },
+       { type: 'internalItem' },
+       { type: 'paragraph' },
+       'N', 'o', ' ', 'n', 'a', 'm', 'e', ' ', '2',
+       { type: '/paragraph' },
+       { type: '/internalItem' },
+       { type: 'internalItem' },
+       { type: 'paragraph' },
+       'N', 'o', ' ', 'n', 'a', 'm', 'e', ' ', '3',
+       { type: '/paragraph' },
+       { type: '/internalItem' },
+       { type: '/internalList' }
+];
+
+ve.dm.citeExample.complexInternalData = [
+       // 0
+       { type: 'alienMeta', originalDomElements: $( '<!-- before -->' 
).toArray() },
+       { type: '/alienMeta' },
+       { type: 'paragraph' },
+       'F', [ 'o', [ ve.dm.example.bold ] ], [ 'o', [ ve.dm.example.italic ] ],
+       // 4
+       { type: 'mwReference', attributes: {
+               mw: {},
+               about: '#mwt1',
+               listIndex: 0,
+               listGroup: 'mwReference/',
+               listKey: 'auto/0',
+               refGroup: '',
+               contentsUsed: true
+       } },
+       // 5
+       { type: '/mwReference' },
+       // 6
+       { type: '/paragraph' },
+       { type: 'alienMeta', originalDomElements: $( '<!-- after -->' 
).toArray() },
+       { type: '/alienMeta' },
+       // 7
+       { type: 'internalList' },
+       // 8
+       { type: 'internalItem' },
+       // 9
+       { type: 'paragraph', internal: { generated: 'wrapper' } },
+       'R', [ 'e', [ ve.dm.example.bold ] ], 'f',
+       // 13
+       { type: 'alienMeta', originalDomElements: $( '<!-- reference -->' 
).toArray() },
+       { type: '/alienMeta' },
+       'e', [ 'r', [ ve.dm.example.italic ] ], [ 'e', [ ve.dm.example.italic ] 
],
+       // 16
+       { type: 'mwReference', attributes: {
+               mw: {},
+               about: '#mwt2',
+               listIndex: 1,
+               listGroup: 'mwReference/',
+               listKey: 'foo',
+               refGroup: '',
+               contentsUsed: true
+       } },
+       // 17
+       { type: '/mwReference' },
+       'n', 'c', 'e',
+       // 21
+       { type: '/paragraph' },
+       // 22
+       { type: '/internalItem' },
+       // 23
+       { type: 'internalItem' },
+       { type: 'alienMeta', originalDomElements: $( '<!-- beginning -->' 
).toArray() },
+       { type: '/alienMeta' },
+       // 24
+       { type: 'preformatted' },
+       { type: 'alienMeta', originalDomElements: $( '<!-- inside -->' 
).toArray() },
+       { type: '/alienMeta' },
+       // 25
+       { type: 'mwEntity', attributes: { character: '€' } },
+       // 26
+       { type: '/mwEntity' },
+       '2', '5', '0',
+       { type: 'alienMeta', originalDomElements: $( '<!-- inside2 -->' 
).toArray() },
+       { type: '/alienMeta' },
+       // 30
+       { type: '/preformatted' },
+       { type: 'alienMeta', originalDomElements: $( '<!-- end -->' ).toArray() 
},
+       { type: '/alienMeta' },
+       // 31
+       { type: '/internalItem' },
+       // 32
+       { type: '/internalList' }
+       // 33
+];
+
+ve.dm.citeExample.complexInternalData.internalItems = [
+       { group: 'mwReference', key: null, body: 'First reference' },
+       { group: 'mwReference', key: 'foo', body: 'Table in ref: 
<table><tr><td>because I can</td></tr></table>' }
+];
+
+ve.dm.citeExample.complexInternalData.internalListNextUniqueNumber = 1;

-- 
To view, visit https://gerrit.wikimedia.org/r/268788
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7da5f25f61d0c7f2331ab927c35d8f6a975041b8
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Cite
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to