* Change default message format in mediawiki.js from plain to text (this includes behavior of mw.msg shorthand).
* Code changes are primarily in jqueryMsg, with minor supporting changes in mediawiki.
* Organize tests by public entrypoint (mediawiki or mediawiki.jqueryMsg).
* Additional tests and assertions for all public entry points, for new and existing behavior.
* Convenience test assertion methods
* Add explanatory comments for new and existing code.
* Add setting to jqueryMsg for format, defaulting to parse, to preserve existing behavior for direct callers to jqueryMsg's public API.
* Since there are now two ways of constructing the abstract syntax tree, add a new parameter to the cache's key scheme.
* Minor formatting
Change-Id: I0d220692262356a12e2f1c0ce30cf6f090428332
'SITENAME' : mw.config.get( 'wgSiteName' )
},
messages : mw.messages,
- language : mw.language
+ language : mw.language,
+
+ // Same meaning as in mediawiki.js.
+ //
+ // Only 'text', 'parse', and 'escaped' are supported, and the
+ // actual escaping for 'escaped' is done by other code (generally
+ // through jqueryMsg).
+ //
+ // However, note that this default only
+ // applies to direct calls to jqueryMsg. The default for mediawiki.js itself
+ // is 'text', including when it uses jqueryMsg.
+ format: 'parse'
+
};
/**
* @return {Function} function suitable for assigning to window.gM
*/
mw.jqueryMsg.getMessageFunction = function ( options ) {
- var failableParserFn = getFailableParserFn( options );
+ var failableParserFn = getFailableParserFn( options ),
+ format;
+
+ if ( options && options.format !== undefined ) {
+ format = options.format;
+ } else {
+ format = parserDefaults.format;
+ }
+
/**
* N.B. replacements are variadic arguments or an array in second parameter. In other words:
* somefunction(a, b, c, d)
* @return {string} Rendered HTML.
*/
return function () {
- return failableParserFn( arguments ).html();
+ var failableResult = failableParserFn( arguments );
+ if ( format === 'text' || format === 'escaped' ) {
+ return failableResult.text();
+ } else {
+ return failableResult.html();
+ }
};
};
*/
mw.jqueryMsg.parser = function ( options ) {
this.settings = $.extend( {}, parserDefaults, options );
+ this.settings.onlyCurlyBraceTransform = ( this.settings.format === 'text' || this.settings.format === 'escaped' );
+
this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic );
};
mw.jqueryMsg.parser.prototype = {
- // cache, map of mediaWiki message key to the AST of the message. In most cases, the message is a string so this is identical.
- // (This is why we would like to move this functionality server-side).
+ /**
+ * Cache mapping MediaWiki message keys and the value onlyCurlyBraceTransform, to the AST of the message.
+ *
+ * In most cases, the message is a string so this is identical.
+ * (This is why we would like to move this functionality server-side).
+ *
+ * The two parts of the key are separated by colon. For example:
+ *
+ * "message-key:true": ast
+ *
+ * if they key is "message-key" and onlyCurlyBraceTransform is true.
+ *
+ * This cache is shared by all instances of mw.jqueryMsg.parser.
+ *
+ * @static
+ */
astCache: {},
/**
* @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
*/
getAst: function ( key ) {
- if ( this.astCache[ key ] === undefined ) {
- var wikiText = this.settings.messages.get( key );
+ var cacheKey = [key, this.settings.onlyCurlyBraceTransform].join( ':' ), wikiText;
+
+ if ( this.astCache[ cacheKey ] === undefined ) {
+ wikiText = this.settings.messages.get( key );
if ( typeof wikiText !== 'string' ) {
wikiText = '\\[' + key + '\\]';
}
- this.astCache[ key ] = this.wikiTextToAst( wikiText );
+ this.astCache[ cacheKey ] = this.wikiTextToAst( wikiText );
}
- return this.astCache[ key ];
+ return this.astCache[ cacheKey ];
},
- /*
+
+ /**
* Parses the input wikiText into an abstract syntax tree, essentially an s-expression.
*
* CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already.
*/
wikiTextToAst: function ( input ) {
var pos,
- regularLiteral, regularLiteralWithoutBar, regularLiteralWithoutSpace, backslash, anyCharacter,
- escapedOrLiteralWithoutSpace, escapedOrLiteralWithoutBar, escapedOrRegularLiteral,
+ regularLiteral, regularLiteralWithoutBar, regularLiteralWithoutSpace, regularLiteralWithSquareBrackets,
+ backslash, anyCharacter, escapedOrLiteralWithoutSpace, escapedOrLiteralWithoutBar, escapedOrRegularLiteral,
whitespace, dollar, digits,
openExtlink, closeExtlink, wikilinkPage, wikilinkContents, openLink, closeLink, templateName, pipe, colon,
templateContents, openTemplate, closeTemplate,
- nonWhitespaceExpression, paramExpression, expression, result;
+ nonWhitespaceExpression, paramExpression, expression, curlyBraceTransformExpression, result;
// Indicates current position in input as we parse through it.
// Shared among all parsing functions below.
regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/);
regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/);
+ regularLiteralWithSquareBrackets = makeRegexParser( /^[^{}$\\]/ );
backslash = makeStringParser( '\\' );
anyCharacter = makeRegexParser( /^./ );
function escapedLiteral() {
] );
// Used to define "literals" without spaces, in space-delimited situations
function literalWithoutSpace() {
- var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
- return result === null ? null : result.join('');
+ var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
+ return result === null ? null : result.join('');
}
// Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
// it is not a literal in the parameter
function literalWithoutBar() {
- var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
- return result === null ? null : result.join('');
+ var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
+ return result === null ? null : result.join('');
}
// Used for wikilink page names. Like literalWithoutBar, but
}
function literal() {
- var result = nOrMore( 1, escapedOrRegularLiteral )();
- return result === null ? null : result.join('');
+ var result = nOrMore( 1, escapedOrRegularLiteral )();
+ return result === null ? null : result.join('');
}
+
+ function curlyBraceTransformExpressionLiteral() {
+ var result = nOrMore( 1, regularLiteralWithSquareBrackets )();
+ return result === null ? null : result.join('');
+ }
+
whitespace = makeRegexParser( /^\s+/ );
dollar = makeStringParser( '$' );
digits = makeRegexParser( /^\d+/ );
literal
] );
- function start() {
- var result = nOrMore( 0, expression )();
+ // Used when only {{-transformation is wanted, for 'text'
+ // or 'escaped' formats
+ curlyBraceTransformExpression = choice( [
+ template,
+ replacement,
+ curlyBraceTransformExpressionLiteral
+ ] );
+
+
+ /**
+ * Starts the parse
+ *
+ * @param {Function} rootExpression root parse function
+ */
+ function start( rootExpression ) {
+ var result = nOrMore( 0, rootExpression )();
if ( result === null ) {
return null;
}
// everything above this point is supposed to be stateless/static, but
// I am deferring the work of turning it into prototypes & objects. It's quite fast enough
// finally let's do some actual work...
- result = start();
+
+ // If you add another possible rootExpression, you must update the astCache key scheme.
+ result = start( this.settings.onlyCurlyBraceTransform ? curlyBraceTransformExpression : expression );
/*
* For success, the p must have gotten to the end of the input
// Replace the default message parser with jqueryMsg
oldParser = mw.Message.prototype.parser;
mw.Message.prototype.parser = function () {
+ var messageFunction;
+
// TODO: should we cache the message function so we don't create a new one every time? Benchmark this maybe?
// Caching is somewhat problematic, because we do need different message functions for different maps, so
// we'd have to cache the parser as a member of this.map, which sounds a bit ugly.
// Fall back to mw.msg's simple parser
return oldParser.apply( this );
}
- var messageFunction = mw.jqueryMsg.getMessageFunction( { 'messages': this.map } );
+
+ messageFunction = mw.jqueryMsg.getMessageFunction( {
+ 'messages': this.map,
+ // For format 'escaped', escaping part is handled by mediawiki.js
+ 'format': this.format
+ } );
return messageFunction( this.key, this.parameters );
};
* @return Message
*/
function Message( map, key, parameters ) {
- this.format = 'plain';
+ this.format = 'text';
this.map = map;
this.key = key;
this.parameters = parameters === undefined ? [] : slice.call( parameters );
Message.prototype = {
/**
- * Simple message parser, does $N replacement and nothing else.
+ * Simple message parser, does $N replacement, HTML-escaping (only for
+ * 'escaped' format), and nothing else.
+ *
* This may be overridden to provide a more complex message parser.
*
+ * The primary override is in mediawiki.jqueryMsg.
+ *
* This function will not be called for nonexistent messages.
*/
parser: function () {
if ( !this.exists() ) {
// Use <key> as text if key does not exist
- if ( this.format !== 'plain' ) {
- // format 'escape' and 'parse' need to have the brackets and key html escaped
+ if ( this.format === 'escaped' || this.format === 'parse' ) {
+ // format 'escaped' and 'parse' need to have the brackets and key html escaped
return mw.html.escape( '<' + this.key + '>' );
}
return '<' + this.key + '>';
}
- if ( this.format === 'plain' || this.format === 'parse' ) {
+ if ( this.format === 'plain' || this.format === 'text' || this.format === 'parse' ) {
text = this.parser();
}
},
/**
- * Changes format to parse and converts message to string
+ * Changes format to 'parse' and converts message to string
+ *
+ * If jqueryMsg is loaded, this parses the message text from wikitext
+ * (where supported) to HTML
+ *
+ * Otherwise, it is equivalent to plain.
*
* @return {string} String form of parsed message
*/
},
/**
- * Changes format to plain and converts message to string
+ * Changes format to 'plain' and converts message to string
+ *
+ * This substitutes parameters, but otherwise does not change the
+ * message text.
*
* @return {string} String form of plain message
*/
},
/**
- * Changes the format to html escaped and converts message to string
+ * Changes format to 'text' and converts message to string
+ *
+ * If jqueryMsg is loaded, {{-transformation is done where supported
+ * (such as {{plural:}}, {{gender:}}, {{int:}}).
+ *
+ * Otherwise, it is equivalent to plain.
+ */
+ text: function () {
+ this.format = 'text';
+ return this.toString();
+ },
+
+ /**
+ * Changes the format to 'escaped' and converts message to string
+ *
+ * This is equivalent to using the 'text' format (see text method), then
+ * HTML-escaping the output.
*
* @return {string} String form of html escaped message
*/
( function ( mw, $ ) {
-var mwLanguageCache = {}, oldGetOuterHtml, formatnumTests, specialCharactersPageName;
+var mwLanguageCache = {}, oldGetOuterHtml, formatnumTests, specialCharactersPageName,
+ expectedListUsers, expectedEntrypoints;
QUnit.module( 'mediawiki.jqueryMsg', QUnit.newMwEnvironment( {
setup: function () {
};
// Messages that are reused in multiple tests
- // They are also all part of regression tests based on actual extensions. The actual messages have the same key,
- // but without jquerymsg-test-.
mw.messages.set( {
- 'jquerymsg-test-pagetriage-del-talk-page-notify-summary': 'Notifying author of deletion nomination for [[$1]]',
- 'jquerymsg-test-categorytree-collapse-bullet': '[<b>−</b>]',
- 'jquerymsg-test-wikieditor-toolbar-help-content-signature-result': '<a href=\'#\' title=\'{{#special:mypage}}\'>Username</a> (<a href=\'#\' title=\'{{#special:mytalk}}\'>talk</a>)'
+ // The values for gender are not significant,
+ // what matters is which of the values is choosen by the parser
+ 'gender-msg': '$1: {{GENDER:$2|blue|pink|green}}',
+
+ 'plural-msg': 'Found $1 {{PLURAL:$1|item|items}}',
+
+ // Assume the grammar form grammar_case_foo is not valid in any language
+ 'grammar-msg': 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}',
+
+ 'formatnum-msg': '{{formatnum:$1}}',
+
+ 'portal-url': 'Project:Community portal',
+ 'see-portal-url': '{{Int:portal-url}} is an important community page.',
+
+ 'jquerymsg-test-statistics-users': '注册[[Special:ListUsers|用户]]',
+
+ 'jquerymsg-test-version-entrypoints-index-php': '[https://www.mediawiki.org/wiki/Manual:index.php index.php]',
+
+ 'external-link-replace': 'Foo [$1 bar]'
} );
specialCharactersPageName = '"Who" wants to be a millionaire & live on \'Exotic Island\'?';
+
+ expectedListUsers = '注册' + $( '<a>' ).attr( {
+ title: 'Special:ListUsers',
+ href: mw.util.wikiGetlink( 'Special:ListUsers' )
+ } ).text( '用户' ).getOuterHtml();
+
+ expectedEntrypoints = '<a href="https://www.mediawiki.org/wiki/Manual:index.php">index.php</a>';
},
teardown: function () {
mw.language = this.orgMwLangauge;
'HTMLElement[] arrays are preserved as raw html'
);
- mw.messages.set( 'external-link-replace', 'Foo [$1 bar]' );
assert.equal(
parser( 'external-link-replace', 'http://example.org/?x=y&z' ),
'Foo <a href="http://example.org/?x=y&z">bar</a>',
QUnit.test( 'Plural', 3, function ( assert ) {
var parser = mw.jqueryMsg.getMessageFunction();
- mw.messages.set( 'plural-msg', 'Found $1 {{PLURAL:$1|item|items}}' );
assert.equal( parser( 'plural-msg', 0 ), 'Found 0 items', 'Plural test for english with zero as count' );
assert.equal( parser( 'plural-msg', 1 ), 'Found 1 item', 'Singular test for english' );
assert.equal( parser( 'plural-msg', 2 ), 'Found 2 items', 'Plural test for english' );
var user = mw.user,
parser = mw.jqueryMsg.getMessageFunction();
- // The values here are not significant,
- // what matters is which of the values is choosen by the parser
- mw.messages.set( 'gender-msg', '$1: {{GENDER:$2|blue|pink|green}}' );
-
user.options.set( 'gender', 'male' );
assert.equal(
parser( 'gender-msg', 'Bob', 'male' ),
QUnit.test( 'Grammar', 2, function ( assert ) {
var parser = mw.jqueryMsg.getMessageFunction();
- // Assume the grammar form grammar_case_foo is not valid in any language
- mw.messages.set( 'grammar-msg', 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}' );
assert.equal( parser( 'grammar-msg' ), 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar Test with sitename' );
mw.messages.set( 'grammar-msg-wrong-syntax', 'Przeszukaj {{GRAMMAR:grammar_case_xyz}}' );
QUnit.test( 'Links', 6, function ( assert ) {
var parser = mw.jqueryMsg.getMessageFunction(),
- expectedListUsers,
expectedDisambiguationsText,
expectedMultipleBars,
expectedSpecialCharacters;
the bold was removed because it is not yet implemented.
*/
- mw.messages.set( 'jquerymsg-test-statistics-users', '注册[[Special:ListUsers|用户]]' );
-
- expectedListUsers = '注册' + $( '<a>' ).attr( {
- title: 'Special:ListUsers',
- href: mw.util.wikiGetlink( 'Special:ListUsers' )
- } ).text( '用户' ).getOuterHtml();
-
assert.equal(
parser( 'jquerymsg-test-statistics-users' ),
expectedListUsers,
'Wikilink without pipe'
);
- mw.messages.set( 'jquerymsg-test-version-entrypoints-index-php', '[https://www.mediawiki.org/wiki/Manual:index.php index.php]' );
assert.equal(
parser( 'jquerymsg-test-version-entrypoints-index-php' ),
- '<a href="https://www.mediawiki.org/wiki/Manual:index.php">index.php</a>',
+ expectedEntrypoints,
'External link'
);
);
});
-// Output for format plain when calling main (mediawiki.js) API.
-// We're testing here to ensure our monkey-patching of mw.Message.prototype.parser doesn't
-// cause breakage.
+// Tests that {{-transformation vs. general parsing are done as requested
+QUnit.test( 'Curly brace transformation', 14, function ( assert ) {
+ var formatText, formatParse, oldUserLang;
+
+ oldUserLang = mw.config.get( 'wgUserLanguage' ) ;
+
+ formatText= mw.jqueryMsg.getMessageFunction( {
+ format: 'text'
+ } );
+
+ formatParse = mw.jqueryMsg.getMessageFunction( {
+ format: 'parse'
+ } );
+
+ // When the expected result is the same in both modes
+ function assertBothModes( parserArguments, expectedResult, assertMessage) {
+ assert.equal( formatText.apply( null, parserArguments ), expectedResult, assertMessage + ' when format is \'text\'' );
+ assert.equal( formatParse.apply( null, parserArguments ), expectedResult, assertMessage + ' when format is \'parse\'' );
+ }
+
+ assertBothModes( ['gender-msg', 'Bob', 'male'], 'Bob: blue', 'gender is resolved' );
+
+ assertBothModes( ['plural-msg', 5], 'Found 5 items', 'plural is resolved' );
+
+ assertBothModes( ['grammar-msg'], 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'grammar is resolved' );
+
+ mw.config.set( 'wgUserLanguage', 'en' ) ;
+ assertBothModes( ['formatnum-msg', '987654321.654321'], '987654321.654321', 'formatnum is resolved' );
-// Some of the tests use mw.msg, while others have mw.message(...).plain(). These two
-// syntaxes should have identical behavior.
-QUnit.test( 'Plain', 4, function ( assert ) {
+ // Test non-{{ wikitext, where behavior differs
+
+ // Wikilink
assert.equal(
- mw.message( 'jquerymsg-test-pagetriage-del-talk-page-notify-summary' ).plain(),
- 'Notifying author of deletion nomination for [[$1]]',
- 'Square brackets in plain with no parameters'
+ formatText( 'jquerymsg-test-statistics-users' ),
+ mw.messages.get( 'jquerymsg-test-statistics-users' ),
+ 'Internal link message unchanged when format is \'text\''
);
-
assert.equal(
- mw.msg( 'jquerymsg-test-pagetriage-del-talk-page-notify-summary', specialCharactersPageName ),
- 'Notifying author of deletion nomination for [[' + specialCharactersPageName + ']]',
- 'Square brackets in plain with one parameter'
+ formatParse( 'jquerymsg-test-statistics-users' ),
+ expectedListUsers,
+ 'Internal link message parsed when format is \'parse\''
);
+ // External link
+ assert.equal(
+ formatText( 'jquerymsg-test-version-entrypoints-index-php' ),
+ mw.messages.get( 'jquerymsg-test-version-entrypoints-index-php' ),
+ 'External link message unchanged when format is \'text\''
+ );
assert.equal(
- mw.msg( 'jquerymsg-test-categorytree-collapse-bullet' ),
- mw.messages.get( 'jquerymsg-test-categorytree-collapse-bullet' ),
- 'Message with single square brackets is not changed'
+ formatParse( 'jquerymsg-test-version-entrypoints-index-php' ),
+ expectedEntrypoints,
+ 'External link message processed when format is \'parse\''
);
+ // External link with parameter
+ assert.equal(
+ formatText( 'external-link-replace', 'http://example.com' ),
+ 'Foo [http://example.com bar]',
+ 'External link message only substitutes parameter when format is \'text\''
+ );
assert.equal(
- mw.message( 'jquerymsg-test-wikieditor-toolbar-help-content-signature-result' ).plain(),
- mw.messages.get( 'jquerymsg-test-wikieditor-toolbar-help-content-signature-result' ),
- 'HTML message with curly braces is not changed'
+ formatParse( 'external-link-replace', 'http://example.com' ),
+ 'Foo <a href="http://example.com">bar</a>',
+ 'External link message processed when format is \'parse\''
);
+
+ mw.config.set( 'wgUserLanguage', oldUserLang );
} );
QUnit.test( 'Int', 4, function ( assert ) {
'Link with nested message'
);
- mw.messages.set( 'portal-url', 'Project:Community portal' );
- mw.messages.set( 'see-portal-url', '{{Int:portal-url}} is an important community page.' );
assert.equal(
parser( 'see-portal-url' ),
'Project:Community portal is an important community page.',
// Tests that getMessageFunction is used for non-plain messages with curly braces or
// square brackets, but not otherwise.
-QUnit.test( 'mw.msg()', 22, function ( assert ) {
+QUnit.test( 'mw.Message.prototype.parser monkey-patch', 22, function ( assert ) {
var oldGMF, outerCalled, innerCalled;
mw.messages.set( {
];
QUnit.test( 'formatnum', formatnumTests.length, function ( assert ) {
- mw.messages.set( 'formatnum-msg', '{{formatnum:$1}}' );
mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' );
$.each( formatnumTests, function ( i, test ) {
QUnit.stop();
( function ( mw, $ ) {
-QUnit.module( 'mediawiki', QUnit.newMwEnvironment() );
+var specialCharactersPageName;
+
+
+// Since QUnitTestResources.php loads both mediawiki and mediawiki.jqueryMsg as
+// dependencies, this only tests the monkey-patched behavior with the two of them combined.
+
+// See mediawiki.jqueryMsg.test.js for unit tests for jqueryMsg-specific functionality.
+
+QUnit.module( 'mediawiki', QUnit.newMwEnvironment( {
+ setup: function () {
+ // Messages used in multiple tests
+ mw.messages.set( {
+ 'other-message': 'Other Message',
+ 'mediawiki-test-pagetriage-del-talk-page-notify-summary': 'Notifying author of deletion nomination for [[$1]]',
+ 'gender-plural-msg': '{{GENDER:$1|he|she|they}} {{PLURAL:$2|is|are}} awesome',
+ 'grammar-msg': 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}',
+ 'formatnum-msg': '{{formatnum:$1}}',
+ 'int-msg': 'Some {{int:other-message}}'
+ } );
+
+ // For formatnum tests
+ mw.config.set( 'wgUserLanguage', 'en' );
+
+ specialCharactersPageName = '"Who" wants to be a millionaire & live on \'Exotic Island\'?';
+ }
+} ) );
QUnit.test( 'Initial check', 8, function ( assert ) {
assert.ok( window.jQuery, 'jQuery defined' );
assert.ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
});
-QUnit.test( 'mw.message & mw.messages', 20, function ( assert ) {
- var goodbye, hello, pluralMessage;
+QUnit.test( 'mw.message & mw.messages', 54, function ( assert ) {
+ var goodbye, hello;
+
+ // Convenience method for asserting the same result for multiple formats
+ function assertMultipleFormats( messageArguments, formats, expectedResult, assertMessage) {
+ var len = formats.length, format, i;
+ for ( i = 0; i < len; i++ ) {
+ format = formats[i];
+ assert.equal( mw.message.apply( null, messageArguments )[format](), expectedResult, assertMessage + ' when format is ' + format);
+ }
+ }
assert.ok( mw.messages, 'messages defined' );
assert.ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
hello = mw.message( 'hello' );
- assert.equal( hello.format, 'plain', 'Message property "format" defaults to "plain"' );
+ // https://bugzilla.wikimedia.org/show_bug.cgi?id=44459
+ assert.equal( hello.format, 'text', 'Message property "format" defaults to "text"' );
+
assert.strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
assert.equal( hello.key, 'hello', 'Message property "key" (currect key)' );
assert.deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
assert.equal( hello.escaped(), 'Hello <b>awesome</b> world', 'Message.escaped returns the escaped message' );
assert.equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
+ assert.ok( mw.messages.set( 'escaped-with-curly-brace', '"{{SITENAME}}" is the home of {{int:other-message}}' ) );
+ assert.equal( mw.message( 'escaped-with-curly-brace' ).escaped(), mw.html.escape( '"' + mw.config.get( 'wgSiteName') + '" is the home of Other Message' ), 'Escaped format works correctly for curly brace message' );
+
+ assert.ok( mw.messages.set( 'escaped-with-square-brackets', 'Visit the [[Project:Community portal|community portal]] & [[Project:Help desk|help desk]]' ) );
+ assert.equal( mw.message( 'escaped-with-square-brackets' ).escaped(), 'Visit the [[Project:Community portal|community portal]] & [[Project:Help desk|help desk]]', 'Escaped format works correctly for square bracket message' );
+
hello.parse();
assert.equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
hello.plain();
assert.equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
+ hello.text();
+ assert.equal( hello.format, 'text', 'Message.text correctly updated the "format" property' );
+
assert.strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
goodbye = mw.message( 'goodbye' );
assert.strictEqual( goodbye.exists(), false, 'Message.exists returns false for nonexistent messages' );
- assert.equal( goodbye.plain(), '<goodbye>', 'Message.toString returns plain <key> if format is "plain" and key does not exist' );
+ assertMultipleFormats( ['goodbye'], ['plain', 'text'], '<goodbye>', 'Message.toString returns <key> if key does not exist' );
// bug 30684
- assert.equal( goodbye.escaped(), '<goodbye>', 'Message.toString returns properly escaped <key> if format is "escaped" and key does not exist' );
+ assertMultipleFormats( ['goodbye'], ['parse', 'escaped'], '<goodbye>', 'Message.toString returns properly escaped <key> if key does not exist' );
+
+ assert.ok( mw.messages.set( 'plural-test-msg', 'There {{PLURAL:$1|is|are}} $1 {{PLURAL:$1|result|results}}' ), 'mw.messages.set: Register' );
+ assertMultipleFormats( ['plural-test-msg', 6], ['text', 'parse', 'escaped'], 'There are 6 results', 'plural get resolved' );
+ assert.equal( mw.message( 'plural-test-msg', 6 ).plain(), 'There {{PLURAL:6|is|are}} 6 {{PLURAL:6|result|results}}', 'Parameter is substituted but plural is not resolved in plain' );
+
+ assertMultipleFormats( ['mediawiki-test-pagetriage-del-talk-page-notify-summary'], ['plain', 'text'], mw.messages.get( 'mediawiki-test-pagetriage-del-talk-page-notify-summary' ), 'Double square brackets with no parameters unchanged' );
+
+ assertMultipleFormats( ['mediawiki-test-pagetriage-del-talk-page-notify-summary', specialCharactersPageName], ['plain', 'text'], 'Notifying author of deletion nomination for [[' + specialCharactersPageName + ']]', 'Double square brackets with one parameter' );
+
+ assert.equal( mw.message( 'mediawiki-test-pagetriage-del-talk-page-notify-summary', specialCharactersPageName ).escaped(), 'Notifying author of deletion nomination for [[' + mw.html.escape( specialCharactersPageName ) + ']]', 'Double square brackets with one parameter, when escaped' );
+
+
+ assert.ok( mw.messages.set( 'mediawiki-test-categorytree-collapse-bullet', '[<b>−</b>]' ), 'mw.messages.set: Register' );
+ assert.equal( mw.message( 'mediawiki-test-categorytree-collapse-bullet' ).plain(), mw.messages.get( 'mediawiki-test-categorytree-collapse-bullet' ), 'Single square brackets unchanged in plain mode' );
- assert.ok( mw.messages.set( 'pluraltestmsg', 'There {{PLURAL:$1|is|are}} $1 {{PLURAL:$1|result|results}}' ), 'mw.messages.set: Register' );
- pluralMessage = mw.message( 'pluraltestmsg' , 6 );
- assert.equal( pluralMessage.plain(), 'There are 6 results', 'plural get resolved when format is plain' );
- assert.equal( pluralMessage.parse(), 'There are 6 results', 'plural get resolved when format is parse' );
+ assert.ok( mw.messages.set( 'mediawiki-test-wikieditor-toolbar-help-content-signature-result', '<a href=\'#\' title=\'{{#special:mypage}}\'>Username</a> (<a href=\'#\' title=\'{{#special:mytalk}}\'>talk</a>)' ) );
+ assert.equal( mw.message( 'mediawiki-test-wikieditor-toolbar-help-content-signature-result' ).plain(), mw.messages.get( 'mediawiki-test-wikieditor-toolbar-help-content-signature-result' ), 'HTML message with curly braces is not changed in plain mode' );
+ assertMultipleFormats( ['gender-plural-msg', 'male', 1], ['text', 'parse', 'escaped'], 'he is awesome', 'Gender and plural are resolved' );
+ assert.equal( mw.message( 'gender-plural-msg', 'male', 1 ).plain(), '{{GENDER:male|he|she|they}} {{PLURAL:1|is|are}} awesome', 'Parameters are substituted, but gender and plural are not resolved in plain mode' );
+
+ assert.equal( mw.message( 'grammar-msg' ).plain(), mw.messages.get( 'grammar-msg' ), 'Grammar is not resolved in plain mode' );
+ assertMultipleFormats( ['grammar-msg'], ['text', 'parse'], 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar is resolved' );
+ assert.equal( mw.message( 'grammar-msg' ).escaped(), 'Przeszukaj ' + mw.html.escape( mw.config.get( 'wgSiteName' ) ), 'Grammar is resolved in escaped mode' );
+
+ assertMultipleFormats( ['formatnum-msg', '987654321.654321'], ['text', 'parse', 'escaped'], '987654321.654321', 'formatnum is resolved' );
+ assert.equal( mw.message( 'formatnum-msg' ).plain(), mw.messages.get( 'formatnum-msg' ), 'formatnum is not resolved in plain mode' );
+
+ assertMultipleFormats( ['int-msg'], ['text', 'parse', 'escaped'], 'Some Other Message', 'int is resolved' );
+ assert.equal( mw.message( 'int-msg' ).plain(), mw.messages.get( 'int-msg' ), 'int is not resolved in plain mode' );
});
-QUnit.test( 'mw.msg', 11, function ( assert ) {
+QUnit.test( 'mw.msg', 14, function ( assert ) {
assert.ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
assert.equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
assert.equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (nonexistent message)' );
assert.equal( mw.msg( 'plural-item', 0 ), 'Found 0 items', 'Apply plural for count 0' );
assert.equal( mw.msg( 'plural-item', 1 ), 'Found 1 item', 'Apply plural for count 1' );
- assert.ok( mw.messages.set('gender-plural-msg' , '{{GENDER:$1|he|she|they}} {{PLURAL:$2|is|are}} awesome' ) );
+ assert.equal( mw.msg( 'mediawiki-test-pagetriage-del-talk-page-notify-summary', specialCharactersPageName ), 'Notifying author of deletion nomination for [[' + specialCharactersPageName + ']]', 'Double square brackets in mw.msg one parameter' );
+
assert.equal( mw.msg( 'gender-plural-msg', 'male', 1 ), 'he is awesome', 'Gender test for male, plural count 1' );
assert.equal( mw.msg( 'gender-plural-msg', 'female', '1' ), 'she is awesome', 'Gender test for female, plural count 1' );
assert.equal( mw.msg( 'gender-plural-msg', 'unknown', 10 ), 'they are awesome', 'Gender test for neutral, plural count 10' );
+ assert.equal( mw.msg( 'grammar-msg' ), 'Przeszukaj ' + mw.config.get( 'wgSiteName'), 'Grammar is resolved' );
+
+ assert.equal( mw.msg( 'formatnum-msg', '987654321.654321' ), '987654321.654321', 'formatnum is resolved' );
+
+ assert.equal( mw.msg( 'int-msg' ), 'Some Other Message', 'int is resolved' );
});
/**