mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-12-12 06:27:01 +00:00
461c76981f
At first I was going for a more minimal replacement of mw.Uri with URL, until I discovered that this code depends on a mw.Uri bug that would be difficult to replicate: // Expected: Relative URLs are accepted new mw.Uri( '/foo' ).toString() // => 'https://localhost/foo' // Expected: Protocol is optional new mw.Uri( 'example.com/foo' ).toString() // => 'https://example.com/foo' // Unexpected: Treated as empty domain with no protocol rather than relative URL new mw.Uri( './foo' ).toString() // => 'https://./foo' So I went for a bigger rewrite to preserve the intent rather than the exact logic. I had to change some test cases to use more realistic fake data. They previously relied on bugs in our URL handling to pass despite the base URLs being incorrect, particularly for non-short URLs (see T270219). In my testing non-short URLs behave the same as before in practice. Depends-On: I07a8c097dba0f5572c0aedf4febdf1434063ea6f Bug: T325249 Change-Id: I232361266c1dda795b88018c3aaa3d9ecbe42b93
273 lines
7.3 KiB
JavaScript
273 lines
7.3 KiB
JavaScript
/*!
|
|
* VisualEditor UserInterface MWWikitextStringTransferHandler tests.
|
|
*
|
|
* @copyright 2011-2020 VisualEditor Team and others; see http://ve.mit-license.org
|
|
*/
|
|
|
|
QUnit.module( 've.ui.MWWikitextStringTransferHandler', ve.test.utils.newMwEnvironment( {
|
|
beforeEach() {
|
|
// Mock XHR for mw.Api()
|
|
this.server = this.sandbox.useFakeServer();
|
|
// Random number, chosen by a fair dice roll.
|
|
// Used to make #mwt ID deterministic
|
|
this.randomStub = sinon.stub( Math, 'random' ).returns( 0.04 );
|
|
},
|
|
afterEach() {
|
|
this.randomStub.restore();
|
|
}
|
|
} ) );
|
|
|
|
/* Tests */
|
|
|
|
ve.test.utils.runWikitextStringHandlerTest = ( assert, server, string, mimeType, expectedResponse, expectedData, annotations, assertDom, base, msg ) => {
|
|
const done = assert.async(),
|
|
item = ve.ui.DataTransferItem.static.newFromString( string, mimeType ),
|
|
doc = ve.dm.Document.static.newBlankDocument(),
|
|
mockSurface = {
|
|
getModel: () => {
|
|
return {
|
|
getDocument: () => doc
|
|
};
|
|
},
|
|
createProgress: () => ve.createDeferred().promise()
|
|
};
|
|
|
|
ve.fixBase( doc.getHtmlDocument(), doc.getHtmlDocument(), base );
|
|
|
|
// Preprocess the expectedData array
|
|
for ( let i = 0; i < expectedData.length; i++ ) {
|
|
if ( Array.isArray( expectedData[ i ] ) ) {
|
|
for ( let j = 0; j < expectedData[ i ][ 1 ].length; j++ ) {
|
|
if ( typeof expectedData[ i ][ 1 ][ j ] === 'number' ) {
|
|
expectedData[ i ][ 1 ][ j ] = annotations[ expectedData[ i ][ 1 ][ j ] ];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check we match the wikitext string handler
|
|
const name = ve.ui.dataTransferHandlerFactory.getHandlerNameForItem( item );
|
|
assert.strictEqual( name, 'wikitextString', msg + ': triggers match function' );
|
|
|
|
// Invoke the handler
|
|
const handler = ve.ui.dataTransferHandlerFactory.create( 'wikitextString', mockSurface, item );
|
|
|
|
handler.getInsertableData().done( ( docOrData ) => {
|
|
let actualData, store;
|
|
if ( docOrData instanceof ve.dm.Document ) {
|
|
actualData = docOrData.getData();
|
|
store = docOrData.getStore();
|
|
} else {
|
|
actualData = docOrData;
|
|
store = new ve.dm.HashValueStore();
|
|
}
|
|
ve.dm.example.postprocessAnnotations( actualData, store );
|
|
if ( assertDom ) {
|
|
assert.equalLinearDataWithDom( store, actualData, expectedData, msg + ': data match (with DOM)' );
|
|
} else {
|
|
assert.equalLinearData( actualData, expectedData, msg + ': data match' );
|
|
}
|
|
done();
|
|
} );
|
|
|
|
if ( server && expectedResponse ) {
|
|
server.respond( [ 200, { 'Content-Type': 'application/json' }, JSON.stringify( {
|
|
visualeditor: {
|
|
result: 'success',
|
|
content: expectedResponse
|
|
}
|
|
} ) ] );
|
|
}
|
|
};
|
|
|
|
QUnit.test( 'convert', function ( assert ) {
|
|
const cases = [
|
|
{
|
|
msg: 'Simple link',
|
|
// Put link in the middle of text to verify that the
|
|
// start-of-line and end-of-line anchors on the heading
|
|
// identification pattern don't affect link identification
|
|
pasteString: 'some [[Foo]] text',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: '<p>some <a rel="mw:WikiLink" href="./Foo" title="Foo">Foo</a> text</p>',
|
|
annotations: [ {
|
|
type: 'link/mwInternal',
|
|
attributes: {
|
|
lookupTitle: 'Foo',
|
|
normalizedTitle: 'Foo',
|
|
title: 'Foo'
|
|
}
|
|
} ],
|
|
expectedData: [
|
|
{ type: 'paragraph' },
|
|
's',
|
|
'o',
|
|
'm',
|
|
'e',
|
|
' ',
|
|
[ 'F', [ 0 ] ],
|
|
[ 'o', [ 0 ] ],
|
|
[ 'o', [ 0 ] ],
|
|
' ',
|
|
't',
|
|
'e',
|
|
'x',
|
|
't',
|
|
{ type: '/paragraph' },
|
|
{ type: 'internalList' },
|
|
{ type: '/internalList' }
|
|
]
|
|
},
|
|
{
|
|
msg: 'Simple link with no p-wrapping',
|
|
pasteString: '*[[Foo]]',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: '<ul><li><a rel="mw:WikiLink" href="./Foo" title="Foo">Foo</a></li></ul>',
|
|
annotations: [ {
|
|
type: 'link/mwInternal',
|
|
attributes: {
|
|
lookupTitle: 'Foo',
|
|
normalizedTitle: 'Foo',
|
|
title: 'Foo'
|
|
}
|
|
} ],
|
|
expectedData: [
|
|
{
|
|
type: 'list',
|
|
attributes: { style: 'bullet' }
|
|
},
|
|
{ type: 'listItem' },
|
|
{
|
|
type: 'paragraph',
|
|
internal: { generated: 'wrapper' }
|
|
},
|
|
[ 'F', [ 0 ] ],
|
|
[ 'o', [ 0 ] ],
|
|
[ 'o', [ 0 ] ],
|
|
{ type: '/paragraph' },
|
|
{ type: '/listItem' },
|
|
{ type: '/list' },
|
|
{ type: 'internalList' },
|
|
{ type: '/internalList' }
|
|
]
|
|
},
|
|
{
|
|
msg: 'Simple template',
|
|
pasteString: '{{Template}}',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: '<div typeof="mw:Transclusion" about="#mwt1">Template</div>',
|
|
assertDom: true,
|
|
expectedData: [
|
|
{
|
|
type: 'mwTransclusionBlock',
|
|
attributes: {
|
|
mw: {}
|
|
},
|
|
originalDomElements: $.parseHTML( '<div typeof="mw:Transclusion" about="#mwt40000000">Template</div>' )
|
|
},
|
|
{ type: '/mwTransclusionBlock' },
|
|
{ type: 'internalList' },
|
|
{ type: '/internalList' }
|
|
]
|
|
},
|
|
{
|
|
msg: 'Headings, only RESTBase IDs stripped',
|
|
pasteString: '==heading==',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: '<h2 id="mwAB">foo</h2><h2 id="mw-meaningful-id">bar</h2>',
|
|
annotations: [],
|
|
assertDom: true,
|
|
expectedData: [
|
|
{ type: 'mwHeading', attributes: { level: 2 }, originalDomElements: $.parseHTML( '<h2>foo</h2>' ) },
|
|
'f', 'o', 'o',
|
|
{ type: '/mwHeading' },
|
|
{ type: 'mwHeading', attributes: { level: 2 }, originalDomElements: $.parseHTML( '<h2 id="mw-meaningful-id">bar</h2>' ) },
|
|
'b', 'a', 'r',
|
|
{ type: '/mwHeading' },
|
|
{ type: 'internalList' },
|
|
{ type: '/internalList' }
|
|
]
|
|
},
|
|
{
|
|
msg: 'Headings, parsoid fallback ids don\'t interfere with whitespace stripping',
|
|
pasteString: '== Tudnivalók ==',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: '<h2 id="Tudnivalók"><span id="Tudnival.C3.B3k" typeof="mw:FallbackId"></span> Tudnivalók </h2>',
|
|
annotations: [],
|
|
assertDom: true,
|
|
expectedData: [
|
|
{ type: 'mwHeading', attributes: { level: 2 }, originalDomElements: $.parseHTML( '<h2 id="Tudnivalók"> Tudnivalók </h2>' ) },
|
|
'T', 'u', 'd', 'n', 'i', 'v', 'a', 'l', 'ó', 'k',
|
|
{ type: '/mwHeading' },
|
|
{ type: 'internalList' },
|
|
{ type: '/internalList' }
|
|
]
|
|
},
|
|
{
|
|
msg: 'Magic link (RFC)',
|
|
pasteString: 'RFC 1234',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: false,
|
|
annotations: [],
|
|
expectedData: [
|
|
{
|
|
type: 'link/mwMagic',
|
|
attributes: {
|
|
content: 'RFC 1234'
|
|
}
|
|
},
|
|
{
|
|
type: '/link/mwMagic'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
msg: 'Magic link (PMID)',
|
|
pasteString: 'PMID 1234',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: false,
|
|
annotations: [],
|
|
expectedData: [
|
|
{
|
|
type: 'link/mwMagic',
|
|
attributes: {
|
|
content: 'PMID 1234'
|
|
}
|
|
},
|
|
{
|
|
type: '/link/mwMagic'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
msg: 'Magic link (ISBN)',
|
|
pasteString: 'ISBN 123456789X',
|
|
pasteType: 'text/plain',
|
|
parsoidResponse: false,
|
|
annotations: [],
|
|
expectedData: [
|
|
{
|
|
type: 'link/mwMagic',
|
|
attributes: {
|
|
content: 'ISBN 123456789X'
|
|
}
|
|
},
|
|
{
|
|
type: '/link/mwMagic'
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
mw.config.set( {
|
|
wgArticlePath: '/wiki/$1'
|
|
} );
|
|
for ( let 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, ve.dm.mwExample.baseUri,
|
|
cases[ i ].msg
|
|
);
|
|
}
|
|
} );
|