summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Cite/modules/ve-cite/tests
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/Cite/modules/ve-cite/tests')
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Converter.test.js36
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.InternalList.test.js237
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Transaction.test.js97
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.citeExample.js754
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.DiffElement.test.js67
-rw-r--r--www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.MWWikitextStringTransferHandler.test.js95
6 files changed, 1286 insertions, 0 deletions
diff --git a/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Converter.test.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Converter.test.js
new file mode 100644
index 00000000..a3410e8f
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Converter.test.js
@@ -0,0 +1,36 @@
+/*!
+ * VisualEditor DataModel Cite-specific Converter tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
+ * @license MIT
+ */
+
+QUnit.module( 've.dm.Converter (Cite)', ve.test.utils.mwEnvironment );
+
+QUnit.test( 'getModelFromDom', function ( assert ) {
+ var msg, caseItem,
+ cases = ve.dm.citeExample.domToDataCases;
+
+ 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;
+
+ 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/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.InternalList.test.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.InternalList.test.js
new file mode 100644
index 00000000..78d3d10f
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.InternalList.test.js
@@ -0,0 +1,237 @@
+/*!
+ * VisualEditor DataModel Cite-specific InternalList tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
+ * @license MIT
+ */
+
+QUnit.module( 've.dm.InternalList (Cite)', ve.test.utils.mwEnvironment );
+
+/* Tests */
+
+QUnit.test( 'addNode/removeNode', 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', 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' }
+ ]
+ },
+ { 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', 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/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Transaction.test.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Transaction.test.js
new file mode 100644
index 00000000..bf9f30bc
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.Transaction.test.js
@@ -0,0 +1,97 @@
+/*!
+ * VisualEditor DataModel Cite-specific Transaction tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
+ * @license MIT
+ */
+
+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' ),
+ 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: '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 ) )
+ },
+ { type: 'retain', length: 1 }
+ ]
+ }
+ ];
+
+ 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.TransactionBuilder.static.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.TransactionBuilder.static.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( OO.getHash( expectedStoreItems[ j ] ) );
+ }
+ assert.deepEqual( actualStoreItems, expectedStoreItems, cases[ i ].msg + ': store items' );
+ }
+} );
diff --git a/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.citeExample.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.citeExample.js
new file mode 100644
index 00000000..2fd8c7f4
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.dm.citeExample.js
@@ -0,0 +1,754 @@
+/*!
+ * VisualEditor DataModel Cite-specific example data sets.
+ *
+ * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
+ * @license MIT
+ */
+
+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' +
+ '<sup 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">' +
+ '<a href="#cite_note-bar-1">[1]</a>' +
+ '</sup>' +
+ ' Baz' +
+ '<sup 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">' +
+ '<a href="#cite_note-.3A0-2">[g1 1]</a>' +
+ '</sup>' +
+ ' Whee' +
+ '<sup 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">' +
+ '<a href="#cite_note-bar-1">[1]</a>' +
+ '</sup>' +
+ ' Yay' +
+ // This reference has .body.id instead of .body.html
+ '<sup 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">' +
+ '<a href="#cite_note-3">[g1 2]</a>' +
+ '</sup>' +
+ ' Quux' +
+ '<sup 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">' +
+ '<a href="#cite_note-bar-1">[1]</a>' +
+ '</sup>' +
+ ' Foo' +
+ '<sup 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">' +
+ '<a href="#cite_ref-foo-4">[g1 3]</a>' +
+ '</sup>' +
+ '</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-mw="{&quot;name&quot;:&quot;references&quot;,&quot;body&quot;:{' +
+ '&quot;html&quot;:&quot;<sup 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></sup>&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' +
+ '<sup data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" typeof="mw:Extension/ref">' +
+ '</sup>' +
+ ' Baz' +
+ '<sup 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">' +
+ '</sup>' +
+ ' Whee' +
+ '<sup 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">' +
+ '</sup>' +
+ ' Yay' +
+ '<sup 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">' +
+ '</sup>' +
+ ' Quux' +
+ '<sup 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">' +
+ '</sup>' +
+ ' Foo' +
+ '<sup 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">' +
+ '</sup>' +
+ '</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;<sup 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;>' +
+ '</sup>&quot;}}">' +
+ '</div>',
+ clipboardBody:
+ '<p>Foo' +
+ '<sup typeof="mw:Extension/ref" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;attrs&quot;:{&quot;name&quot;:&quot;bar&quot;}}" class="mw-ref">' +
+ '<a style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a>' +
+ '</sup>' +
+ ' Baz' +
+ '<sup 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;}}" class="mw-ref">' +
+ '<a data-mw-group="g1" style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[g1 1]</span></a>' +
+ '</sup>' +
+ ' Whee' +
+ '<sup 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;}}" class="mw-ref">' +
+ '<a style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a>' +
+ '</sup>' +
+ ' Yay' +
+ // This reference has .body.id instead of .body.html
+ '<sup 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;}}" class="mw-ref">' +
+ '<a data-mw-group="g1" style="counter-reset: mw-Ref 2;"><span class="mw-reflink-text">[g1 2]</span></a>' +
+ '</sup>' +
+ ' Quux' +
+ '<sup 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;}}" class="mw-ref">' +
+ '<a style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a>' +
+ '</sup>' +
+ ' Foo' +
+ '<sup 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;}}" class="mw-ref">' +
+ '<a data-mw-group="g1" style="counter-reset: mw-Ref 3;"><span class="mw-reflink-text">[g1 3]</span></a>' +
+ '</sup>' +
+ '</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;<sup 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; class=\\&quot;mw-ref\\&quot;><a data-mw-group=\\&quot;g1\\&quot; style=\\&quot;counter-reset: mw-Ref 3;\\&quot;><span class=\\&quot;mw-reflink-text\\&quot;>[g1 3]</span></a></sup>&quot;}}">' +
+ '<ol class="mw-references references" data-mw-group="g1">' +
+ '<li>' +
+ '<a rel="mw:referencedBy" data-mw-group="g1"><span class="mw-linkback-text">↑ </span></a>' +
+ '<span class="reference-text"><span class="ve-ce-branchNode ve-ce-internalItemNode">Quux</span></span>' +
+ '</li>' +
+ '<li>' +
+ '<a rel="mw:referencedBy" data-mw-group="g1"><span class="mw-linkback-text">↑ </span></a>' +
+ '<span class="reference-text"><span class="ve-ce-branchNode ve-ce-internalItemNode">No name</span></span>' +
+ '</li>' +
+ '<li>' +
+ '<a rel="mw:referencedBy" data-mw-group="g1"><span class="mw-linkback-text">↑ </span></a>' +
+ '<span class="reference-text"><span class="ve-ce-branchNode ve-ce-internalItemNode">Ref in refs</span></span>' +
+ '</li>' +
+ '</ol>' +
+ '</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: '<sup 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></sup>'
+ }
+ },
+ originalMw: '{"name":"references","body":{"html":"<sup 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></sup>"},"attrs":{"group":"g1"}}',
+ listGroup: 'mwReference/g1',
+ refGroup: 'g1',
+ isResponsive: true,
+ templateGenerated: false
+ }
+ },
+ { 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 comment': {
+ body: '<p><sup 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">' +
+ '<a href="#cite_note-bar-1">[1]</a></sup></p>',
+ fromDataBody: '<p><sup ' +
+ '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"></sup></p>',
+ clipboardBody: '<p><sup 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;}" ' +
+ ' class="mw-ref">' +
+ '<a style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a></sup></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' }
+ ]
+ },
+ 'Template generated reflist': {
+ body: '<p><sup about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;notes&quot;}}"><a href="./Main_Page#cite_note-1" style="counter-reset: mw-Ref 1;" data-mw-group="notes"><span class="mw-reflink-text">[notes 1]</span></a></sup></p>' +
+ '<div class="mw-references-wrap" typeof="mw:Extension/references mw:Transclusion" about="#mwt4" data-mw="{&quot;parts&quot;:[{&quot;template&quot;:{&quot;target&quot;:{&quot;wt&quot;:&quot;echo&quot;,&quot;href&quot;:&quot;./Template:Echo&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;<references group=\\&quot;notes\\&quot; />&quot;}},&quot;i&quot;:0}}]}">' +
+ '<ol class="mw-references references" data-mw-group="notes">' +
+ '<li about="#cite_note-1" id="cite_note-1"><a href="./Main_Page#cite_ref-1" data-mw-group="notes" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">Foo</span></li>' +
+ '</ol>' +
+ '</div>',
+ fromDataBody: '<p><sup typeof="mw:Extension/ref" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;},&quot;attrs&quot;:{&quot;group&quot;:&quot;notes&quot;}}"></sup></p>' +
+ '<span typeof="mw:Transclusion" data-mw="{&quot;parts&quot;:[{&quot;template&quot;:{&quot;target&quot;:{&quot;wt&quot;:&quot;echo&quot;,&quot;href&quot;:&quot;./Template:Echo&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;<references group=\\&quot;notes\\&quot; />&quot;}},&quot;i&quot;:0}}]}"></span>',
+ clipboardBody: '<p><sup typeof="mw:Extension/ref" data-mw="{&quot;attrs&quot;:{&quot;group&quot;:&quot;notes&quot;},&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;,&quot;html&quot;:&quot;Foo&quot;},&quot;name&quot;:&quot;ref&quot;}" class="mw-ref"><a data-mw-group="notes" style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[notes 1]</span></a></sup></p>' +
+ '<div typeof="mw:Extension/references" data-mw="{&quot;parts&quot;:[{&quot;template&quot;:{&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;<references group=\\&quot;notes\\&quot; />&quot;}},&quot;target&quot;:{&quot;wt&quot;:&quot;echo&quot;,&quot;href&quot;:&quot;./Template:Echo&quot;},&quot;i&quot;:0}}],&quot;name&quot;:&quot;references&quot;}">' +
+ // TODO: This should list should get populated on copy
+ '<ol class="mw-references references"></ol>' +
+ '</div>',
+ data: [
+ { type: 'paragraph' },
+ {
+ type: 'mwReference',
+ attributes: {
+ contentsUsed: true,
+ listGroup: 'mwReference/notes',
+ listIndex: 0,
+ listKey: 'auto/0',
+ mw: {
+ attrs: {
+ group: 'notes'
+ },
+ body: {
+ id: 'mw-reference-text-cite_note-1'
+ },
+ name: 'ref'
+ },
+ originalMw: '{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{"group":"notes"}}',
+ refGroup: 'notes',
+ refListItemId: 'mw-reference-text-cite_note-1'
+ }
+ },
+ { type: '/mwReference' },
+ { type: '/paragraph' },
+ {
+ type: 'mwReferencesList',
+ attributes: {
+ mw: {
+ parts: [ {
+ template: {
+ params: {
+ 1: { wt: '<references group="notes" />' }
+ },
+ target: { wt: 'echo', href: './Template:Echo' },
+ i: 0
+ }
+ } ]
+ },
+ originalMw: '{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"<references group=\\"notes\\" />"}},"i":0}}]}',
+ refGroup: '',
+ listGroup: 'mwReference/',
+ isResponsive: true,
+ templateGenerated: true
+ }
+ },
+ { type: '/mwReferencesList' },
+ { type: 'internalList' },
+ { type: 'internalItem', attributes: { originalHtml: 'Foo' } },
+ {
+ internal: {
+ generated: 'wrapper'
+ },
+ type: 'paragraph'
+ },
+ 'F', 'o', 'o',
+ { type: '/paragraph' },
+ { type: '/internalItem' },
+ { type: '/internalList' }
+ ]
+ },
+ 'Template generated reflist (div wrapped)': {
+ body: '<p><sup about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;},&quot;attrs&quot;:{}}"><a href="./Main_Page#cite_note-1" style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a></sup></p>' +
+ '<div about="#mwt3" typeof="mw:Transclusion" data-mw="{&quot;parts&quot;:[{&quot;template&quot;:{&quot;target&quot;:{&quot;wt&quot;:&quot;reflist&quot;,&quot;href&quot;:&quot;./Template:Reflist&quot;},&quot;params&quot;:{},&quot;i&quot;:0}}]}">' +
+ '<div typeof="mw:Extension/references" about="#mwt5" data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{}}">' +
+ '<ol class="mw-references references">' +
+ '<li about="#cite_note-1" id="cite_note-1"><a href="./Main_Page#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">Foo</span></li>' +
+ '</ol>' +
+ '</div>' +
+ '</div>',
+ fromDataBody: '<p><sup typeof="mw:Extension/ref" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;},&quot;attrs&quot;:{}}"></sup></p>' +
+ '<span typeof="mw:Transclusion" data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{}}"></span>',
+ clipboardBody: '<p><sup typeof="mw:Extension/ref" data-mw="{&quot;attrs&quot;:{},&quot;body&quot;:{&quot;id&quot;:&quot;mw-reference-text-cite_note-1&quot;,&quot;html&quot;:&quot;Foo&quot;},&quot;name&quot;:&quot;ref&quot;}" class="mw-ref"><a style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a></sup></p>' +
+ '<div typeof="mw:Extension/references" data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{}}">' +
+ '<ol class="mw-references references">' +
+ '<li><a rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a><span class="reference-text"><span class="ve-ce-branchNode ve-ce-internalItemNode">Foo</span></span></li>' +
+ '</ol>' +
+ '</div>',
+ data: [
+ { type: 'paragraph' },
+ {
+ type: 'mwReference',
+ attributes: {
+ contentsUsed: true,
+ listGroup: 'mwReference/',
+ listIndex: 0,
+ listKey: 'auto/0',
+ mw: {
+ attrs: {},
+ body: {
+ id: 'mw-reference-text-cite_note-1'
+ },
+ name: 'ref'
+ },
+ originalMw: '{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}',
+ refGroup: '',
+ refListItemId: 'mw-reference-text-cite_note-1'
+ }
+ },
+ { type: '/mwReference' },
+ { type: '/paragraph' },
+ {
+ type: 'mwReferencesList',
+ attributes: {
+ mw: {
+ name: 'references',
+ attrs: {}
+ },
+ originalMw: '{"name":"references","attrs":{}}',
+ refGroup: '',
+ listGroup: 'mwReference/',
+ isResponsive: true,
+ templateGenerated: true
+ }
+ },
+ { type: '/mwReferencesList' },
+ { type: 'internalList' },
+ { type: 'internalItem', attributes: { originalHtml: 'Foo' } },
+ {
+ internal: {
+ generated: 'wrapper'
+ },
+ type: 'paragraph'
+ },
+ 'F', 'o', 'o',
+ { 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: '',
+ isResponsive: true,
+ templateGenerated: false
+ }
+ },
+ { 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: 'paragraph' },
+ 'F', [ 'o', [ ve.dm.example.bold ] ], [ 'o', [ ve.dm.example.italic ] ],
+ // 4
+ { type: 'mwReference', attributes: {
+ about: '#mwt1',
+ listIndex: 0,
+ listGroup: 'mwReference/',
+ listKey: 'auto/0',
+ refGroup: ''
+ } },
+ // 5
+ { type: '/mwReference' },
+ // 6
+ { type: '/paragraph' },
+ // 7
+ { type: 'internalList' },
+ // 8
+ { type: 'internalItem' },
+ // 9
+ { type: 'paragraph', internal: { generated: 'wrapper' } },
+ 'R', [ 'e', [ ve.dm.example.bold ] ], 'f',
+ // 13
+ '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' },
+ // 24
+ { type: 'preformatted' },
+ // 25
+ { type: 'mwEntity', attributes: { character: '€' } },
+ // 26
+ { type: '/mwEntity' },
+ '2', '5', '0',
+ // 30
+ { type: '/preformatted' },
+ // 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;
diff --git a/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.DiffElement.test.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.DiffElement.test.js
new file mode 100644
index 00000000..79b4497b
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.DiffElement.test.js
@@ -0,0 +1,67 @@
+/*!
+ * VisualEditor Cite-specific DiffElement tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team and others; see http://ve.mit-license.org
+ */
+
+QUnit.module( 've.ui.DiffElement (Cite)' );
+
+QUnit.test( 'Diffing', function ( assert ) {
+ var i, len,
+ // spacer = '<div class="ve-ui-diffElement-spacer">⋮</div>',
+ ref = function ( text, num, name ) {
+ var dataMw = {
+ name: 'ref',
+ body: { html: text }
+ };
+ if ( name ) {
+ dataMw.attrs = { name: name };
+ }
+ return '<sup typeof="mw:Extension/ref" data-mw="' + JSON.stringify( dataMw ).replace( /"/g, '&quot;' ) + '" class="mw-ref">' +
+ '<a style="counter-reset: mw-Ref ' + num + ';"><span class="mw-reflink-text">[' + num + ']</span></a>' +
+ '</sup>';
+ },
+ cases = [
+ {
+ msg: 'Simple ref change',
+ oldDoc:
+ '<p>' + ref( 'Foo' ) + ref( 'Bar' ) + ref( 'Baz' ) + '</p>' +
+ '<h2>Notes</h2>' +
+ '<div typeof="mw:Extension/references" data-mw="{&quot;name&quot;:&quot;references&quot;}"></div>',
+ newDoc:
+ '<p>' + ref( 'Foo' ) + ref( 'Bar ish' ) + ref( 'Baz' ) + '</p>' +
+ '<h2>Notes</h2>' +
+ '<div typeof="mw:Extension/references" data-mw="{&quot;name&quot;:&quot;references&quot;}"></div>',
+ expected:
+ '<div class="ve-ui-diffElement-doc-child-change">' +
+ '<p>' +
+ ref( 'Foo', '1' ) +
+ '<span data-diff-action="change-remove">' +
+ ref( 'Bar', '2', ':0' ) +
+ '</span>' +
+ '<span data-diff-action="change-insert">' +
+ ref( 'Bar ish', '2', ':0' ) +
+ '</span>' +
+ ref( 'Baz', '3' ) +
+ '</p>' +
+ '</div>' +
+ '<h2 data-diff-action="none">Notes</h2>' +
+ '<div class="ve-ui-diffElement-doc-child-change" data-diff-move="undefined">' +
+ '<ol start="1">' +
+ '<li><p data-diff-action="none">Foo</p></li>' +
+ '</ol>' +
+ '<ol start="2">' +
+ '<li><div class="ve-ui-diffElement-doc-child-change">Bar<ins data-diff-action="insert"> ish</ins></div></li>' +
+ '</ol>' +
+ '<ol start="3">' +
+ '<li><p data-diff-action="none">Baz</p></li>' +
+ '</ol>' +
+ '</div>'
+ }
+ ];
+
+ for ( i = 0, len = cases.length; i < len; i++ ) {
+ ve.test.utils.runDiffElementTest( assert, cases[ i ] );
+ }
+
+} );
diff --git a/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.MWWikitextStringTransferHandler.test.js b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.MWWikitextStringTransferHandler.test.js
new file mode 100644
index 00000000..210dd49c
--- /dev/null
+++ b/www/wiki/extensions/Cite/modules/ve-cite/tests/ve.ui.MWWikitextStringTransferHandler.test.js
@@ -0,0 +1,95 @@
+/*!
+ * VisualEditor UserInterface MWWikitextStringTransferHandler tests.
+ *
+ * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
+ * @license MIT
+ */
+
+window.MWWIKITEXT_MOCK_API = true;
+
+QUnit.module( 've.ui.MWWikitextStringTransferHandler (Cite)', QUnit.newMwEnvironment( {
+ beforeEach: function () {
+ // Mock XHR for mw.Api()
+ this.server = window.MWWIKITEXT_MOCK_API ? this.sandbox.useFakeServer() : null;
+ ve.test.utils.mwEnvironment.setup.call( this );
+ },
+ afterEach: ve.test.utils.mwEnvironment.teardown
+} ) );
+
+/* Tests */
+
+QUnit.test( 'convert', function ( assert ) {
+ var i,
+ cases = [
+ {
+ msg: 'Simple reference',
+ pasteString: '<ref>Foo</ref>',
+ pasteType: 'text/plain',
+ parsoidResponse: '<p><span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw=\'{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}\'>[1]</span></p>' +
+ '<ol class="mw-references" typeof="mw:Extension/references" about="#mwt3" data-mw=\'{"name":"references","attrs":{},"autoGenerated":true}\'>' +
+ '<li about="#cite_note-1" id="cite_note-1">↑ <span id="mw-reference-text-cite_note-1" class="mw-reference-text">Foo</span></li>' +
+ '</ol>',
+ annotations: [],
+ expectedData: [
+ {
+ type: 'mwReference',
+ attributes: {
+ mw: {
+ attrs: {},
+ body: {
+ id: 'mw-reference-text-cite_note-1'
+ },
+ name: 'ref'
+ },
+ contentsUsed: true,
+ listGroup: 'mwReference/',
+ listIndex: 0,
+ listKey: 'auto/0',
+ originalMw: '{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}',
+ refGroup: '',
+ refListItemId: 'mw-reference-text-cite_note-1'
+ }
+ },
+ { type: '/mwReference' },
+ { type: 'internalList' },
+ { type: 'internalItem' },
+ { type: 'paragraph', internal: { generated: 'wrapper' } },
+ 'F', 'o', 'o',
+ { type: '/paragraph' },
+ { type: '/internalItem' },
+ { type: '/internalList' }
+ ]
+ },
+ {
+ msg: 'Reference template with autoGenerated content',
+ pasteString: '{{reference}}',
+ pasteType: 'text/plain',
+ parsoidResponse: '<p><span typeof="mw:Transclusion">[1]</span></p>' +
+ '<ol class="mw-references" typeof="mw:Extension/references" about="#mwt3" data-mw=\'{"name":"references","attrs":{},"autoGenerated":true}\'>' +
+ '<li>Reference list</li>' +
+ '</ol>',
+ annotations: [],
+ expectedData: [
+ {
+ type: 'mwTransclusionInline',
+ attributes: {
+ mw: {},
+ originalMw: null
+ }
+ },
+ {
+ type: '/mwTransclusionInline'
+ },
+ { type: 'internalList' },
+ { type: '/internalList' }
+ ]
+ }
+ ];
+
+ for ( i = 0; i < cases.length; i++ ) {
+ ve.test.utils.runWikitextStringHandlerTest(
+ assert, this.server, cases[ i ].pasteString, cases[ i ].pasteType, cases[ i ].parsoidResponse,
+ cases[ i ].expectedData, cases[ i ].annotations, cases[ i ].assertDom, cases[ i ].msg
+ );
+ }
+} );