Update: don't prompt to create User pages
Suppress the redlink drawer for User namespace pages. The redlink drawer prompts the user to create a missing page but this hinders the usual workflow for User page visits specifically. A User page is connection to an account's contributions, age, and other activities and encouraging the creation of a missing User page when trying to view these connections is a hindrance, especially if the missing User page is not associated with the current user. Bug: T201339 Change-Id: I784493a8ecf28176b5a393cb52d7bfa9fa9b1309
This commit is contained in:
parent
4c10e0524d
commit
88dd2530e7
|
@ -122,11 +122,15 @@ class MinervaHooks {
|
|||
"resources/skins.minerva.scripts/page-issues/page/PageIssueLink.js",
|
||||
"resources/skins.minerva.scripts/page-issues/page/pageIssueFormatter.js",
|
||||
'resources/skins.minerva.scripts/pageIssues.js',
|
||||
'resources/skins.minerva.scripts/UriUtil.js',
|
||||
'resources/skins.minerva.scripts/TitleUtil.js',
|
||||
// test files
|
||||
'tests/qunit/skins.minerva.scripts/downloadPageAction.test.js',
|
||||
'tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js',
|
||||
'tests/qunit/skins.minerva.scripts/AB.test.js',
|
||||
'tests/qunit/skins.minerva.scripts/pageIssues.test.js',
|
||||
'tests/qunit/skins.minerva.scripts/UriUtil.test.js',
|
||||
'tests/qunit/skins.minerva.scripts/TitleUtil.test.js',
|
||||
'tests/qunit/skins.minerva.notifications.badge/NotificationBadge.test.js'
|
||||
],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
// Someone has to maintain this wherever it lives. If it live in Core, it becomes a public API.
|
||||
// If it lives in some client-side target of mediawiki-title that accepts a MediaWiki config instead
|
||||
// of a SiteInfo, it still becomes a public API. If it lives where used, it becomes a copy and paste
|
||||
// implementation where each copy can deviate but deletion is easy. See additional discussion in
|
||||
// T218358 and I95b08e77eece5cd4dae62f6f237d492d6b0fe42b.
|
||||
( function ( M ) {
|
||||
var UriUtil = M.require( 'skins.minerva.scripts/UriUtil' );
|
||||
|
||||
/**
|
||||
* Returns the decoded wiki page title referenced by the passed link as a string when parsable.
|
||||
* The title query parameter is returned, if present. Otherwise, a heuristic is used to attempt
|
||||
* to extract the title from the path.
|
||||
*
|
||||
* The API is the source of truth for page titles. This function should only be used in
|
||||
* circumstances where the API cannot be consulted.
|
||||
*
|
||||
* Assuming the current page is on metawiki, consider the following example links and
|
||||
* `newFromUri()` outputs:
|
||||
*
|
||||
* https://meta.wikimedia.org/wiki/Foo → Foo (path title)
|
||||
* http://meta.wikimedia.org/wiki/Foo → Foo (mismatching protocol)
|
||||
* /wiki/Foo → Foo (relative URI)
|
||||
* /w/index.php?title=Foo → Foo (title query parameter)
|
||||
* /wiki/Talk:Foo → Talk:Foo (non-main namespace URI)
|
||||
* /wiki/Foo bar → Foo_bar (name with spaces)
|
||||
* /wiki/Foo%20bar → Foo_bar (name with percent encoded spaces)
|
||||
* /wiki/Foo+bar → Foo+bar (name with +)
|
||||
* /w/index.php?title=Foo%2bbar → Foo+bar (query parameter with +)
|
||||
* / → null (mismatching article path)
|
||||
* /wiki/index.php?title=Foo → null (mismatching script path)
|
||||
* https://archive.org/ → null (mismatching host)
|
||||
* https://foo.wikimedia.org/ → null (mismatching host)
|
||||
* https://en.wikipedia.org/wiki/Bar → null (mismatching host)
|
||||
*
|
||||
* This function invokes `Uri.isInternal()` to validate that this link is assuredly a local
|
||||
* wiki link and that the internal usage of both the title query parameter and value of
|
||||
* wgArticlePath are relevant.
|
||||
*
|
||||
* This function doesn't throw. `null` is returned for any unparseable input.
|
||||
*
|
||||
* @param {mw.Uri|Object|string} [uri] Passed to Uri.
|
||||
* @param {Object|boolean} [options] Passed to Uri.
|
||||
* @param {Object|boolean} [options.validateReadOnlyLink] If true, only links that would show a
|
||||
* page for reading are considered. E.g., `/wiki/Foo` and `/w/index.php?title=Foo` would
|
||||
* validate but `/w/index.php?title=Foo&action=bar` would not.
|
||||
* @return {mw.Title|null} A Title or `null`.
|
||||
*/
|
||||
function newFromUri( uri, options ) {
|
||||
var
|
||||
mwUri,
|
||||
regExp,
|
||||
matches,
|
||||
title;
|
||||
|
||||
try {
|
||||
// uri may or may not be a Uri but the Uri constructor accepts a Uri parameter.
|
||||
mwUri = new mw.Uri( uri, options );
|
||||
} catch ( e ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !UriUtil.isInternal( mwUri ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ( options || {} ).validateReadOnlyLink && !isReadOnlyUri( mwUri ) ) {
|
||||
// An unknown query parameter is used. This may not be a read-only link.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( mwUri.query.title ) {
|
||||
// True if input starts with wgScriptPath.
|
||||
regExp = new RegExp( '^' + mw.RegExp.escape( mw.config.get( 'wgScriptPath' ) ) + '/' );
|
||||
|
||||
// URL has a nonempty `title` query parameter like `/w/index.php?title=Foo`. The script
|
||||
// path should match.
|
||||
matches = regExp.test( mwUri.path );
|
||||
if ( !matches ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The parameter was already decoded at Uri construction.
|
||||
title = mwUri.query.title;
|
||||
} else {
|
||||
// True if input starts with wgArticlePath and ends with a nonempty page title. The
|
||||
// first matching group (index 1) is the page title.
|
||||
regExp = new RegExp( '^' + mw.RegExp.escape( mw.config.get( 'wgArticlePath' ) ).replace( '\\$1', '(.+)' ) );
|
||||
|
||||
// No title query parameter is present so the URL may be "pretty" like `/wiki/Foo`.
|
||||
// `Uri.path` should not contain query parameters or a fragment, as is assumed in
|
||||
// `Uri.getRelativePath()`. Try to isolate the title.
|
||||
matches = regExp.exec( mwUri.path );
|
||||
if ( !matches || !matches[ 1 ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// `Uri.path` was not previously decoded, as is assumed in `Uri.getRelativePath()`,
|
||||
// and decoding may now fail. Do not use `Uri.decode()` which is designed to be
|
||||
// paired with `Uri.encode()` and replaces `+` characters with spaces.
|
||||
title = decodeURIComponent( matches[ 1 ] );
|
||||
} catch ( e ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the fragment, if present.
|
||||
title += mwUri.fragment ? '#' + mwUri.fragment : '';
|
||||
|
||||
return mw.Title.newFromText( title );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the passed link is for reading.
|
||||
*
|
||||
* The following links return true:
|
||||
* /wiki/Foo
|
||||
* /w/index.php?title=Foo
|
||||
* /w/index.php?oldid=123
|
||||
*
|
||||
* The following links return false:
|
||||
* /w/index.php?title=Foo&action=bar
|
||||
*
|
||||
* @private
|
||||
* @static
|
||||
* @method isReadOnlyUri
|
||||
* @param {mw.Uri} uri A Uri to an internal wiki page.
|
||||
* @return {boolean} True if uri has no query parameters or only known parameters for reading.
|
||||
*/
|
||||
function isReadOnlyUri( uri ) {
|
||||
var length = Object.keys( uri.query ).length;
|
||||
return length === ( ( 'oldid' in uri.query ? 1 : 0 ) + ( 'title' in uri.query ? 1 : 0 ) );
|
||||
}
|
||||
|
||||
M.define( 'skins.minerva.scripts/TitleUtil', {
|
||||
newFromUri: newFromUri
|
||||
} );
|
||||
}( mw.mobileFrontend ) );
|
|
@ -0,0 +1,34 @@
|
|||
( function ( M ) {
|
||||
/**
|
||||
* Compares the default Uri host, usually `window.location.host`, and `mw.Uri.host`. Equivalence
|
||||
* tests internal linkage, a mismatch may indicate an external link. Interwiki links are
|
||||
* considered external.
|
||||
*
|
||||
* This function only indicates internal in the sense of being on the same host or not. It has
|
||||
* no knowledge of [[Link]] vs [Link] links.
|
||||
*
|
||||
* On https://meta.wikimedia.org/wiki/Foo, the following links would be considered *internal*
|
||||
* and return `true`:
|
||||
*
|
||||
* https://meta.wikimedia.org/
|
||||
* https://meta.wikimedia.org/wiki/Bar
|
||||
* https://meta.wikimedia.org/w/index.php?title=Bar
|
||||
*
|
||||
* Similarly, the following links would be considered *not* internal and return `false`:
|
||||
*
|
||||
* https://archive.org/
|
||||
* https://foo.wikimedia.org/
|
||||
* https://en.wikipedia.org/
|
||||
* https://en.wikipedia.org/wiki/Bar
|
||||
*
|
||||
* @param {mw.Uri} uri
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isInternal( uri ) {
|
||||
return uri.host === mw.Uri().host;
|
||||
}
|
||||
|
||||
M.define( 'skins.minerva.scripts/UriUtil', {
|
||||
isInternal: isInternal
|
||||
} );
|
||||
}( mw.mobileFrontend ) );
|
|
@ -5,6 +5,7 @@
|
|||
toast = mobile.toast,
|
||||
time = mobile.time,
|
||||
skin = M.require( 'mobile.init/skin' ),
|
||||
TitleUtil = M.require( 'skins.minerva.scripts/TitleUtil' ),
|
||||
issues = M.require( 'skins.minerva.scripts/pageIssues' ),
|
||||
downloadPageAction = M.require( 'skins.minerva.scripts/downloadPageAction' ),
|
||||
loader = mobile.rlModuleLoader,
|
||||
|
@ -16,9 +17,11 @@
|
|||
Anchor = mobile.Anchor,
|
||||
overlayManager = OverlayManager.getSingleton(),
|
||||
page = M.getCurrentPage(),
|
||||
redLinks = page.getRedLinks(),
|
||||
api = new mw.Api(),
|
||||
thumbs = page.getThumbnails(),
|
||||
eventBus = mobile.eventBusSingleton;
|
||||
eventBus = mobile.eventBusSingleton,
|
||||
namespaceIDs = mw.config.get( 'wgNamespaceIds' );
|
||||
|
||||
/**
|
||||
* Event handler for clicking on an image thumbnail
|
||||
|
@ -255,16 +258,78 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a URL to determine if it links to a local User namespace page or not.
|
||||
*
|
||||
* Assuming the current page visited is hosted on metawiki, the following examples would return
|
||||
* true:
|
||||
*
|
||||
* https://meta.wikimedia.org/wiki/User:Foo
|
||||
* /wiki/User:Foo
|
||||
* /wiki/User:Nonexistent_user_page
|
||||
*
|
||||
* The following examples return false:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/User:Foo
|
||||
* /wiki/Foo
|
||||
* /wiki/User_talk:Foo
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isUserUri( url ) {
|
||||
var
|
||||
title = TitleUtil.newFromUri( url ),
|
||||
namespace = title ? title.getNamespaceId() : undefined;
|
||||
return namespace === namespaceIDs.user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the edit action from red links to nonexistent User namespace pages.
|
||||
* @return {void}
|
||||
*/
|
||||
function initUserRedLinks() {
|
||||
redLinks.filter( function ( _, element ) {
|
||||
// Filter out non-User namespace pages.
|
||||
return isUserUri( element.href );
|
||||
} ).each( function ( _, element ) {
|
||||
var uri = new mw.Uri( element.href );
|
||||
if ( uri.query.action !== 'edit' ) {
|
||||
// Nothing to strip.
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip the action.
|
||||
delete uri.query.action;
|
||||
|
||||
// Update the element with the new link.
|
||||
element.href = uri.toString();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize red links call-to-action
|
||||
*
|
||||
* Upon clicking a red link, show an interstitial CTA explaining that the page doesn't exist
|
||||
* with a button to create it, rather than directly navigate to the edit form.
|
||||
*
|
||||
* Special case T201339: following a red link to a user or user talk page should not prompt for
|
||||
* its creation. The reasoning is that user pages should be created by their owners and it's far
|
||||
* more common that non-owners follow a user's red linked user page to consider their
|
||||
* contributions, account age, or other activity.
|
||||
*
|
||||
* For example, a user adds a section to a Talk page and signs their contribution (which creates
|
||||
* a link to their user page whether exists or not). If the user page does not exist, that link
|
||||
* will be red. In both cases, another user follows this link, not to edit create a page for
|
||||
* that user but to obtain information on them.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
function initRedlinksCta() {
|
||||
page.getRedLinks().on( 'click', function ( ev ) {
|
||||
redLinks.filter( function ( _, element ) {
|
||||
// Filter out local User namespace pages.
|
||||
return !isUserUri( element.href );
|
||||
} ).on( 'click', function ( ev ) {
|
||||
var drawerOptions = {
|
||||
progressiveButton: new Button( {
|
||||
progressive: true,
|
||||
|
@ -337,6 +402,7 @@
|
|||
initHistoryLink( $( '.last-modifier-tagline a' ) );
|
||||
appendDownloadButton();
|
||||
initRedlinksCta();
|
||||
initUserRedLinks();
|
||||
initEditLink();
|
||||
// Setup the issues banner on the page
|
||||
// Pages which dont exist (id 0) cannot have issues
|
||||
|
|
|
@ -380,7 +380,9 @@
|
|||
"skins.minerva.icons.images.scripts",
|
||||
"mediawiki.util",
|
||||
"mediawiki.router",
|
||||
"mediawiki.RegExp",
|
||||
"mediawiki.Title",
|
||||
"mediawiki.Uri",
|
||||
"mobile.startup",
|
||||
"mediawiki.user",
|
||||
"mediawiki.storage",
|
||||
|
@ -427,6 +429,8 @@
|
|||
"resources/skins.minerva.scripts/page-issues/page/PageIssueLink.js",
|
||||
"resources/skins.minerva.scripts/page-issues/page/pageIssueFormatter.js",
|
||||
"resources/skins.minerva.scripts/pageIssues.js",
|
||||
"resources/skins.minerva.scripts/UriUtil.js",
|
||||
"resources/skins.minerva.scripts/TitleUtil.js",
|
||||
"resources/skins.minerva.scripts/init.js",
|
||||
"resources/skins.minerva.scripts/initLogging.js",
|
||||
"resources/skins.minerva.scripts/mobileRedirect.js",
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
( function ( M ) {
|
||||
var TitleUtil = M.require( 'skins.minerva.scripts/TitleUtil' );
|
||||
|
||||
QUnit.module( 'Minerva TitleUtil', QUnit.newMwEnvironment( {
|
||||
setup: function () {
|
||||
this.mwUriOrg = mw.Uri;
|
||||
mw.Uri = mw.UriRelative( 'https://meta.wikimedia.org/w/index.php' );
|
||||
},
|
||||
teardown: function () {
|
||||
mw.Uri = this.mwUriOrg;
|
||||
delete this.mwUriOrg;
|
||||
},
|
||||
config: {
|
||||
wgArticlePath: '/wiki/$1',
|
||||
wgScriptPath: '/w'
|
||||
}
|
||||
} ) );
|
||||
|
||||
QUnit.test( '.newFromUri()', function ( assert ) {
|
||||
[ '', 'https://meta.wikimedia.org' ].forEach( function ( authority, index ) {
|
||||
var
|
||||
indexMsg = 'case ' + index + ' ',
|
||||
authorityMsg = ' authority="' + authority + '"',
|
||||
validateReadOnlyLink = { validateReadOnlyLink: true };
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title' ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'title is in query parameter' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Title' ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'title is in path' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/foo/bar/wiki/Title' ),
|
||||
null,
|
||||
indexMsg + 'title is not in nonmatching path' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/%E6%B8%AC%E8%A9%A6' ).getPrefixedDb(),
|
||||
'測試',
|
||||
indexMsg + 'title is decoded' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Foo bar' ).getPrefixedDb(),
|
||||
'Foo_bar',
|
||||
indexMsg + 'title with space is decoded' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Foo%20bar' ).getPrefixedDb(),
|
||||
'Foo_bar',
|
||||
indexMsg + 'title with encoded space is decoded' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title#fragment' ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'fragment is omitted from query title' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Title#fragment' ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'fragment is omitted from path title' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title#fragment' ).getFragment(),
|
||||
'fragment',
|
||||
indexMsg + 'fragment is present after query parameter' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Title#fragment' ).getFragment(),
|
||||
'fragment',
|
||||
indexMsg + 'fragment is present after path' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title#foo%20bar' ).getFragment(),
|
||||
'foo bar',
|
||||
indexMsg + 'fragment is decoded' + authorityMsg
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title', validateReadOnlyLink ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'query title is read-only' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/wiki/Title', validateReadOnlyLink ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'path title is read-only' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title&oldid=123', validateReadOnlyLink ).getPrefixedDb(),
|
||||
'Title',
|
||||
indexMsg + 'query title with revision is read-only' + authorityMsg
|
||||
);
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( authority + '/w/index.php?title=Title¶m', validateReadOnlyLink ),
|
||||
null,
|
||||
indexMsg + 'query title with unknown parameter is not read-only' + authorityMsg
|
||||
);
|
||||
} );
|
||||
|
||||
// Bad or odd inputs.
|
||||
[
|
||||
'%', null, undefined, '', ' ', '/', {}, '\\', '/wiki/%', '/w/index.php?title=%'
|
||||
].forEach( function ( input, index ) {
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( input ),
|
||||
null,
|
||||
'Case ' + index + ' no Title in bad input input="' + input + '"'
|
||||
);
|
||||
} );
|
||||
|
||||
// Parameters are passed to Uri's constructor.
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( { protocol: 'https',
|
||||
host: 'meta.wikimedia.org',
|
||||
path: '/wiki/Title' } ).getPrefixedDb(),
|
||||
'Title',
|
||||
'title is in Uri parameters'
|
||||
);
|
||||
|
||||
// A Uri itself can be passed.
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( new mw.Uri( 'https://meta.wikimedia.org/wiki/Title' ) ).getPrefixedDb(),
|
||||
'Title',
|
||||
'title is in Uri'
|
||||
);
|
||||
|
||||
// JSDoc examples.
|
||||
// https://meta.wikimedia.org/wiki/Foo → Foo (path title)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( 'https://meta.wikimedia.org/wiki/Foo' ).getPrefixedDb(),
|
||||
'Foo',
|
||||
'path title'
|
||||
);
|
||||
|
||||
// http://meta.wikimedia.org/wiki/Foo → Foo (mismatching protocol)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( 'http://meta.wikimedia.org/wiki/Foo' ).getPrefixedDb(),
|
||||
'Foo',
|
||||
'mismatching protocol'
|
||||
);
|
||||
|
||||
// /wiki/Foo → Foo (relative URI)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/Foo' ).getPrefixedDb(),
|
||||
'Foo',
|
||||
'relative URI'
|
||||
);
|
||||
|
||||
// /w/index.php?title=Foo → Foo (title query parameter)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/w/index.php?title=Foo' ).getPrefixedDb(),
|
||||
'Foo',
|
||||
'title query parameter'
|
||||
);
|
||||
|
||||
// /wiki/Talk:Foo → Talk:Foo (non-main namespace URI)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/Talk:Foo' ).getPrefixedDb(),
|
||||
'Talk:Foo',
|
||||
'non-main namespace URI'
|
||||
);
|
||||
|
||||
// /wiki/Foo bar → Foo_bar (name with spaces)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/Foo bar' ).getPrefixedDb(),
|
||||
'Foo_bar',
|
||||
'name with spaces'
|
||||
);
|
||||
|
||||
// /wiki/Foo%20bar → Foo_bar (name with percent encoded spaces)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/Foo%20bar' ).getPrefixedDb(),
|
||||
'Foo_bar',
|
||||
'name with percent encoded spaces'
|
||||
);
|
||||
|
||||
// /wiki/Foo+bar → Foo+bar (name with +)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/Foo+bar' ).getPrefixedDb(),
|
||||
'Foo+bar',
|
||||
'name with +'
|
||||
);
|
||||
|
||||
// /w/index.php?title=Foo%2bbar → Foo+bar (query parameter with +)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/w/index.php?title=Foo%2bbar' ).getPrefixedDb(),
|
||||
'Foo+bar',
|
||||
'query parameter with +'
|
||||
);
|
||||
|
||||
// / → null (mismatching article path)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/' ),
|
||||
null,
|
||||
'mismatching article path'
|
||||
);
|
||||
|
||||
// /wiki/index.php?title=Foo → null (mismatching script path)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( '/wiki/index.php?title=Foo' ),
|
||||
null,
|
||||
'mismatching script path'
|
||||
);
|
||||
|
||||
// https://archive.org/ → null (mismatching host)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( 'https://archive.org/' ),
|
||||
null,
|
||||
'mismatching host (0)'
|
||||
);
|
||||
|
||||
// https://foo.wikimedia.org/ → null (mismatching host)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( 'https://foo.wikimedia.org/' ),
|
||||
null,
|
||||
'mismatching host (1)'
|
||||
);
|
||||
|
||||
// https://en.wikipedia.org/wiki/Bar → null (mismatching host)
|
||||
assert.strictEqual(
|
||||
TitleUtil.newFromUri( 'https://en.wikipedia.org/wiki/Bar' ),
|
||||
null,
|
||||
'mismatching host (2)'
|
||||
);
|
||||
} );
|
||||
}( mw.mobileFrontend ) );
|
|
@ -0,0 +1,47 @@
|
|||
( function ( M ) {
|
||||
var UriUtil = M.require( 'skins.minerva.scripts/UriUtil' );
|
||||
|
||||
QUnit.module( 'Minerva UriUtil', QUnit.newMwEnvironment( {
|
||||
setup: function () {
|
||||
this.mwUriOrg = mw.Uri;
|
||||
mw.Uri = mw.UriRelative( 'https://meta.wikimedia.org/w/index.php' );
|
||||
},
|
||||
teardown: function () {
|
||||
mw.Uri = this.mwUriOrg;
|
||||
delete this.mwUriOrg;
|
||||
}
|
||||
} ) );
|
||||
|
||||
QUnit.test( '.isInternal()', function ( assert ) {
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( '/relative' ) ),
|
||||
true,
|
||||
'relative URLs are internal'
|
||||
);
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( 'http://meta.wikimedia.org/' ) ),
|
||||
true,
|
||||
'matching hosts are internal'
|
||||
);
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( 'https:/meta.wikimedia.org/' ) ),
|
||||
true,
|
||||
'protocol is irrelevant'
|
||||
);
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( 'https://meta.wikimedia.org/path' ) ),
|
||||
true,
|
||||
'path is irrelevant'
|
||||
);
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( 'https://archive.org/' ) ),
|
||||
false,
|
||||
'external links are not internal'
|
||||
);
|
||||
assert.strictEqual(
|
||||
UriUtil.isInternal( new mw.Uri( 'https://www.meta.wikimedia.org/' ) ),
|
||||
false,
|
||||
'differing subdomains are not internal'
|
||||
);
|
||||
} );
|
||||
}( mw.mobileFrontend ) );
|
Loading…
Reference in New Issue