2016-09-21 16:33:42 +00:00
|
|
|
/*!
|
|
|
|
* VisualEditor DataModel MWInternalLinkAnnotation tests.
|
|
|
|
*
|
2020-01-08 17:13:04 +00:00
|
|
|
* @copyright 2011-2020 VisualEditor Team and others; see http://ve.mit-license.org
|
2016-09-21 16:33:42 +00:00
|
|
|
*/
|
|
|
|
|
2022-03-10 00:02:35 +00:00
|
|
|
QUnit.module( 've.dm.MWInternalLinkAnnotation', ve.test.utils.newMwEnvironment() );
|
2016-09-21 16:33:42 +00:00
|
|
|
|
2021-04-30 09:33:22 +00:00
|
|
|
QUnit.test( 'toDataElement', ( assert ) => {
|
2023-01-30 14:57:56 +00:00
|
|
|
// The expected data depends on site configuration, so we need to generate the cases several times.
|
|
|
|
const getCases = () => {
|
|
|
|
const
|
|
|
|
externalLink = ( href ) => {
|
2022-03-10 00:02:35 +00:00
|
|
|
const link = document.createElement( 'a' );
|
|
|
|
link.setAttribute( 'href', href );
|
|
|
|
return link;
|
2023-01-30 14:57:56 +00:00
|
|
|
},
|
|
|
|
internalLink = ( pageTitle, params ) => {
|
|
|
|
const link = document.createElement( 'a' );
|
|
|
|
link.setAttribute( 'href', mw.Title.newFromText( pageTitle ).getUrl( params ) );
|
|
|
|
return link;
|
|
|
|
},
|
|
|
|
parsoidLink = ( href ) => {
|
2022-03-10 00:02:35 +00:00
|
|
|
const link = document.createElement( 'a' );
|
2023-01-30 14:57:56 +00:00
|
|
|
if ( mw.config.get( 'wgArticlePath' ).includes( '?' ) ) {
|
|
|
|
href = href.replace( './', './index.php?title=' );
|
|
|
|
}
|
|
|
|
link.setAttribute( 'href', href );
|
2022-03-10 00:02:35 +00:00
|
|
|
return link;
|
|
|
|
};
|
2023-01-30 14:57:56 +00:00
|
|
|
return [
|
2020-02-07 00:15:49 +00:00
|
|
|
{
|
|
|
|
msg: 'Not an internal link',
|
|
|
|
element: externalLink( 'http://example.com/' ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwExternal',
|
|
|
|
attributes: {
|
|
|
|
href: 'http://example.com/'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2016-09-21 16:33:42 +00:00
|
|
|
{
|
|
|
|
msg: 'Simple',
|
|
|
|
element: internalLink( 'Foo' ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo',
|
|
|
|
normalizedTitle: 'Foo',
|
|
|
|
title: 'Foo'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2022-03-10 00:02:35 +00:00
|
|
|
{
|
|
|
|
msg: 'Relative path',
|
2023-01-30 14:57:56 +00:00
|
|
|
element: parsoidLink( './Foo' ),
|
2022-03-10 00:02:35 +00:00
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo',
|
|
|
|
normalizedTitle: 'Foo',
|
|
|
|
title: 'Foo'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-03-26 14:42:44 +00:00
|
|
|
{
|
|
|
|
msg: 'History link',
|
|
|
|
element: internalLink( 'Foo', { action: 'history' } ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwExternal',
|
|
|
|
attributes: {
|
2023-01-30 14:57:56 +00:00
|
|
|
href: mw.Title.newFromText( 'Foo' ).getUrl( { action: 'history' } )
|
2020-03-26 14:42:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Diff link',
|
|
|
|
element: internalLink( 'Foo', { diff: '3', oldid: '2' } ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwExternal',
|
|
|
|
attributes: {
|
2023-01-30 14:57:56 +00:00
|
|
|
href: mw.Title.newFromText( 'Foo' ).getUrl( { diff: '3', oldid: '2' } )
|
2020-03-26 14:42:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-02-07 00:15:49 +00:00
|
|
|
{
|
|
|
|
msg: 'Red link',
|
|
|
|
element: internalLink( 'Foo', { action: 'edit', redlink: '1' } ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo',
|
|
|
|
normalizedTitle: 'Foo',
|
|
|
|
title: 'Foo'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2016-09-21 16:33:42 +00:00
|
|
|
{
|
|
|
|
// Because percent-encoded URLs aren't valid titles, but what they decode to might be
|
|
|
|
msg: 'Percent encoded characters',
|
|
|
|
element: internalLink( 'Foo?' ),
|
2023-01-30 14:57:56 +00:00
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo?',
|
|
|
|
normalizedTitle: 'Foo?',
|
|
|
|
title: 'Foo?'
|
2016-09-21 16:33:42 +00:00
|
|
|
}
|
2023-01-30 14:57:56 +00:00
|
|
|
}
|
2018-05-18 18:55:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// The fragment should make it into some parts of this, and not others
|
|
|
|
msg: 'Fragments',
|
|
|
|
element: internalLink( 'Foo#bar' ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo',
|
|
|
|
normalizedTitle: 'Foo#bar',
|
|
|
|
title: 'Foo#bar'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Question marks in the fragment shouldn't confuse this
|
|
|
|
msg: 'Question marks in fragments',
|
|
|
|
element: internalLink( 'Foo#bar?' ),
|
|
|
|
expected: {
|
|
|
|
type: 'link/mwInternal',
|
|
|
|
attributes: {
|
|
|
|
lookupTitle: 'Foo',
|
2021-09-12 00:40:08 +00:00
|
|
|
normalizedTitle: 'Foo#' + mw.util.escapeIdForLink( 'bar?' ),
|
|
|
|
title: 'Foo#' + mw.util.escapeIdForLink( 'bar?' )
|
2018-05-18 18:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-21 16:33:42 +00:00
|
|
|
}
|
2023-01-30 14:57:56 +00:00
|
|
|
];
|
|
|
|
};
|
2016-09-21 16:33:42 +00:00
|
|
|
|
2023-01-30 14:57:56 +00:00
|
|
|
const converter = new ve.dm.Converter( ve.dm.modelRegistry, ve.dm.nodeFactory, ve.dm.annotationFactory, ve.dm.metaItemFactory );
|
2016-09-21 16:33:42 +00:00
|
|
|
|
2022-03-10 00:02:35 +00:00
|
|
|
const articlePaths = [
|
|
|
|
{
|
2023-01-30 14:57:56 +00:00
|
|
|
// MediaWiki config settings:
|
|
|
|
// $wgScriptPath = '/w';
|
|
|
|
// $wgUsePathInfo = false;
|
2022-03-10 00:02:35 +00:00
|
|
|
msg: 'query string URL',
|
2023-01-30 14:57:56 +00:00
|
|
|
config: {
|
|
|
|
wgServer: 'http://example.org',
|
|
|
|
wgScript: '/w/index.php',
|
|
|
|
wgArticlePath: '/w/index.php?title=$1'
|
|
|
|
},
|
|
|
|
// Parsoid-generated <base href> given these MediaWiki config settings:
|
|
|
|
base: 'http://example.org/w/'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// MediaWiki config settings:
|
|
|
|
// $wgScriptPath = '/w';
|
|
|
|
// $wgUsePathInfo = true;
|
|
|
|
msg: 'short URL using pathinfo',
|
|
|
|
config: {
|
|
|
|
wgServer: 'http://example.org',
|
|
|
|
wgScript: '/w/index.php',
|
|
|
|
wgArticlePath: '/w/index.php/$1'
|
|
|
|
},
|
|
|
|
// Parsoid-generated <base href> given these MediaWiki config settings:
|
|
|
|
base: 'http://example.org/w/index.php/'
|
2022-03-10 00:02:35 +00:00
|
|
|
},
|
|
|
|
{
|
2023-01-30 14:57:56 +00:00
|
|
|
// MediaWiki config settings:
|
|
|
|
// $wgScriptPath = '/w';
|
|
|
|
// $wgArticlePath = '/wiki/$1';
|
|
|
|
msg: 'proper short URL',
|
|
|
|
config: {
|
|
|
|
wgServer: 'http://example.org',
|
|
|
|
wgScript: '/w/index.php',
|
|
|
|
wgArticlePath: '/wiki/$1'
|
|
|
|
},
|
|
|
|
// Parsoid-generated <base href> given these MediaWiki config settings:
|
|
|
|
base: 'http://example.org/wiki/'
|
2022-03-10 00:02:35 +00:00
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2023-01-30 14:57:56 +00:00
|
|
|
articlePaths.forEach( function ( pathData ) {
|
|
|
|
// Set up global state (site configuration)
|
|
|
|
mw.config.set( pathData.config );
|
|
|
|
|
|
|
|
const doc = ve.dm.mwExample.createExampleDocumentFromData( [], null, pathData.base );
|
|
|
|
// toDataElement is called during a converter run, so we need to fake up a bit of state to test it.
|
|
|
|
// This would normally be done by ve.dm.converter.getModelFromDom.
|
|
|
|
converter.doc = doc.getHtmlDocument();
|
|
|
|
converter.targetDoc = doc.getHtmlDocument();
|
|
|
|
converter.store = doc.getStore();
|
|
|
|
converter.internalList = doc.getInternalList();
|
|
|
|
converter.contextStack = [];
|
|
|
|
converter.fromClipboard = true;
|
|
|
|
|
|
|
|
// Generate test cases for this site configuration
|
|
|
|
const cases = getCases();
|
|
|
|
for ( let i = 0; i < cases.length; i++ ) {
|
2022-03-10 00:02:35 +00:00
|
|
|
assert.deepEqual(
|
2023-01-30 14:57:56 +00:00
|
|
|
ve.dm.MWInternalLinkAnnotation.static.toDataElement( [ cases[ i ].element ], converter ),
|
|
|
|
cases[ i ].expected,
|
2022-03-10 00:02:35 +00:00
|
|
|
cases[ i ].msg + ': ' + pathData.msg
|
|
|
|
);
|
2023-01-30 14:57:56 +00:00
|
|
|
}
|
|
|
|
} );
|
2016-09-21 16:33:42 +00:00
|
|
|
} );
|
2017-04-27 16:21:55 +00:00
|
|
|
|
2021-04-30 09:33:22 +00:00
|
|
|
QUnit.test( 'getFragment', ( assert ) => {
|
2021-04-29 14:42:18 +00:00
|
|
|
const cases = [
|
2017-04-27 16:21:55 +00:00
|
|
|
{
|
|
|
|
msg: 'No fragment returns null',
|
|
|
|
original: 'Foo',
|
|
|
|
expected: null
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Invalid title returns null',
|
|
|
|
original: 'A%20B',
|
|
|
|
expected: null
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Blank fragment returns empty string',
|
|
|
|
original: 'Foo#',
|
|
|
|
expected: ''
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Extant fragment returns same string',
|
|
|
|
original: 'Foo#bar',
|
|
|
|
expected: 'bar'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Hash-bang works returns full string',
|
|
|
|
original: 'Foo#!bar',
|
|
|
|
expected: '!bar'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Double-hash returns everything after the first hash',
|
|
|
|
original: 'Foo##bar',
|
|
|
|
expected: '#bar'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msg: 'Multi-fragment returns everything after the first hash',
|
|
|
|
original: 'Foo#bar#baz#bat',
|
|
|
|
expected: 'bar#baz#bat'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2021-04-29 14:42:18 +00:00
|
|
|
for ( let i = 0; i < cases.length; i++ ) {
|
2018-06-19 15:00:28 +00:00
|
|
|
assert.strictEqual( ve.dm.MWInternalLinkAnnotation.static.getFragment( cases[ i ].original ), cases[ i ].expected, cases[ i ].msg );
|
2017-04-27 16:21:55 +00:00
|
|
|
}
|
|
|
|
} );
|