Jdlrobson has uploaded a new change for review. https://gerrit.wikimedia.org/r/167342
Change subject: Add RL template module with HTML markup language ...................................................................... Add RL template module with HTML markup language Preparation work for templating in core. RL should allow us to ship HTML / template markup from server to client. Use in Special:Upload and mediawiki.feedback as a proof of concept. Separation of concerns etc... See Also: Ia63d6b6868f23a773e4a41daa0036d4bf2cd6724 Change-Id: Ia0c5c8ec960aa6dff12c9626cee41ae9a3286b76 --- M includes/resourceloader/ResourceLoader.php M includes/resourceloader/ResourceLoaderFileModule.php M includes/resourceloader/ResourceLoaderModule.php M maintenance/jsduck/categories.json M resources/Resources.php M resources/src/mediawiki.action/mediawiki.action.view.postEdit.js A resources/src/mediawiki.action/templates/postEdit.html M resources/src/mediawiki.special/mediawiki.special.upload.js M resources/src/mediawiki.special/mediawiki.special.userlogin.common.js A resources/src/mediawiki.special/templates/captcha.html A resources/src/mediawiki.special/templates/thumbnail.html M resources/src/mediawiki/mediawiki.feedback.js M resources/src/mediawiki/mediawiki.js A resources/src/mediawiki/mediawiki.templates.js A resources/src/mediawiki/templates/dialog.html M tests/phpunit/includes/OutputPageTest.php A tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php A tests/phpunit/includes/resourceloader/templates/template.html A tests/phpunit/includes/resourceloader/templates/template2.html A tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars M tests/qunit/QUnitTestResources.php A tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js 22 files changed, 439 insertions(+), 74 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/42/167342/1 diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index 57deb00..a0b4852 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -974,12 +974,20 @@ case 'messages': $out .= self::makeMessageSetScript( new XmlJsCode( $messagesBlob ) ); break; + case 'templates': + $out .= Xml::encodeJsCall( + 'mw.templates.set', + array( (object)$module->getTemplates() ), + ResourceLoader::inDebugMode() + ); + break; default: $out .= self::makeLoaderImplementScript( $name, $scripts, $styles, - new XmlJsCode( $messagesBlob ) + new XmlJsCode( $messagesBlob ), + $module->getTemplates() ); break; } @@ -1044,10 +1052,14 @@ * @param mixed $messages List of messages associated with this module. May either be an * associative array mapping message key to value, or a JSON-encoded message blob containing * the same data, wrapped in an XmlJsCode object. + * @param array $templates where keys are name of templates and values are the source of + * the template. * @throws MWException * @return string */ - public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) { + public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages, + $templates = array() + ) { if ( is_string( $scripts ) ) { $scripts = new XmlJsCode( "function ( $, jQuery ) {\n{$scripts}\n}" ); } elseif ( !is_array( $scripts ) ) { @@ -1064,7 +1076,8 @@ // PHP/json_encode() consider empty arrays to be numerical arrays and // output javascript "[]" instead of "{}". This fixes that. (object)$styles, - (object)$messages + (object)$messages, + (object)$templates, ), ResourceLoader::inDebugMode() ); diff --git a/includes/resourceloader/ResourceLoaderFileModule.php b/includes/resourceloader/ResourceLoaderFileModule.php index 7bbc9bb..6544813 100644 --- a/includes/resourceloader/ResourceLoaderFileModule.php +++ b/includes/resourceloader/ResourceLoaderFileModule.php @@ -34,6 +34,9 @@ /** @var string Remote base path, see __construct() */ protected $remoteBasePath = ''; + /** @var array Saves a list of the templates named by the modules. */ + protected $templates = array(); + /** * @var array List of paths to JavaScript files to always include * @par Usage: @@ -267,6 +270,10 @@ sort( $option ); $this->{$member} = $option; + break; + // templates + case 'templates': + $this->{$member} = (array) $option; break; // Single strings case 'group': @@ -544,6 +551,9 @@ $files[] = $this->skipFunction; } $files = array_map( array( $this, 'getLocalPath' ), $files ); + // Templates + $templateFiles = array_map( array( $this, 'getLocalPath' ), $this->templates ); + $files = array_merge( $files, $templateFiles ); // File deps need to be treated separately because they're already prefixed $files = array_merge( $files, $this->getFileDependencies( $context->getSkin() ) ); @@ -959,4 +969,26 @@ protected function getLessCompiler( ResourceLoaderContext $context = null ) { return ResourceLoader::getLessCompiler( $this->getConfig() ); } + + /** + * Takes named templates by the module and adds them to the JavaScript output + * + * @return array of templates mapping template alias to content + */ + function getTemplates() { + $templates = array(); + + foreach( $this->templates as $alias => $templatePath ) { + // Alias is optional + if ( is_int( $alias ) ) { + $alias = $templatePath; + } + $localPath = $this->getLocalPath( $templatePath ); + if ( file_exists( $localPath ) ) { + $content = file_get_contents( $localPath ); + $templates[ $alias ] = $content; + } + } + return $templates; + } } diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 45eb70f..3f53ca9 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -135,6 +135,16 @@ } /** + * Returns JavaScript relating to adding templates to the client. + * + * @return string JavaScript code + */ + public function getTemplates() { + // Stub, override expected. + return array(); + } + + /** * @return Config * @since 1.24 */ diff --git a/maintenance/jsduck/categories.json b/maintenance/jsduck/categories.json index d6163bd..145749a 100644 --- a/maintenance/jsduck/categories.json +++ b/maintenance/jsduck/categories.json @@ -13,7 +13,8 @@ "mw.html", "mw.html.Cdata", "mw.html.Raw", - "mw.hook" + "mw.hook", + "mw.template" ] }, { diff --git a/resources/Resources.php b/resources/Resources.php index acc937e..e893de6 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -767,7 +767,10 @@ /* MediaWiki */ 'mediawiki' => array( - 'scripts' => 'resources/src/mediawiki/mediawiki.js', + 'scripts' => array( + 'resources/src/mediawiki/mediawiki.js', + 'resources/src/mediawiki/mediawiki.templates.js', + ), 'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js', 'raw' => true, 'targets' => array( 'desktop', 'mobile' ), @@ -851,6 +854,9 @@ 'position' => 'bottom', ), 'mediawiki.feedback' => array( + 'templates' => array( + 'dialog.html' => 'resources/src/mediawiki/templates/dialog.html', + ), 'scripts' => 'resources/src/mediawiki/mediawiki.feedback.js', 'styles' => 'resources/src/mediawiki/mediawiki.feedback.css', 'dependencies' => array( @@ -1063,6 +1069,9 @@ ), ), 'mediawiki.action.view.postEdit' => array( + 'templates' => array( + 'postEdit.html' => 'resources/src/mediawiki.action/templates/postEdit.html', + ), 'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.postEdit.js', 'styles' => 'resources/src/mediawiki.action/mediawiki.action.view.postEdit.css', 'dependencies' => array( @@ -1337,6 +1346,9 @@ 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.undelete.js', ), 'mediawiki.special.upload' => array( + 'templates' => array( + 'thumbnail.html' => 'resources/src/mediawiki.special/templates/thumbnail.html', + ), 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.upload.js', 'messages' => array( 'widthheight', @@ -1378,6 +1390,9 @@ 'position' => 'top', ), 'mediawiki.special.userlogin.common.js' => array( + 'templates' => array( + 'captcha.html' => 'resources/src/mediawiki.special/templates/captcha.html', + ), 'scripts' => array( 'resources/src/mediawiki.special/mediawiki.special.userlogin.common.js', ), diff --git a/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js b/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js index 4d2c47a..ee2246d 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.postEdit.js @@ -30,14 +30,7 @@ data.message = $.parseHTML( mw.message( 'postedit-confirmation-saved', data.user || mw.user ).escaped() ); } - $div = $( - '<div class="postedit-container">' + - '<div class="postedit">' + - '<div class="postedit-icon postedit-icon-checkmark postedit-content"></div>' + - '<a href="#" class="postedit-close">×</a>' + - '</div>' + - '</div>' - ); + $div = $( mw.template.get( 'mediawiki.action.view.postEdit', 'postEdit.html' ).render() ); if ( typeof data.message === 'string' ) { $div.find( '.postedit-content' ).text( data.message ); diff --git a/resources/src/mediawiki.action/templates/postEdit.html b/resources/src/mediawiki.action/templates/postEdit.html new file mode 100644 index 0000000..dbb482a --- /dev/null +++ b/resources/src/mediawiki.action/templates/postEdit.html @@ -0,0 +1,6 @@ +<div class="postedit-container"> + <div class="postedit"> + <div class="postedit-icon postedit-icon-checkmark postedit-content"></div> + <a href="#" class="postedit-close">×</a> + </div> +</div> diff --git a/resources/src/mediawiki.special/mediawiki.special.upload.js b/resources/src/mediawiki.special/mediawiki.special.upload.js index 04bc978..ab851b3 100644 --- a/resources/src/mediawiki.special/mediawiki.special.upload.js +++ b/resources/src/mediawiki.special/mediawiki.special.upload.js @@ -294,12 +294,7 @@ ctx, meta, previewSize = 180, - thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' + - '<div class="thumbinner">' + - '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' + - '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' + - '</div>' + - '</div>' ); + thumb = $( mw.template.get( 'mediawiki.special.upload', 'thumbnail.html' ).render() ); thumb.find( '.filename' ).text( file.name ).end() .find( '.fileinfo' ).text( prettySize( file.size ) ).end(); diff --git a/resources/src/mediawiki.special/mediawiki.special.userlogin.common.js b/resources/src/mediawiki.special/mediawiki.special.userlogin.common.js index 247f814..ab02e4e 100644 --- a/resources/src/mediawiki.special/mediawiki.special.userlogin.common.js +++ b/resources/src/mediawiki.special/mediawiki.special.userlogin.common.js @@ -9,6 +9,7 @@ function adjustFancyCaptcha( $content, buttonSubmit ) { var $submit = $content.find( buttonSubmit ), tabIndex, + $el, $captchaStuff, $captchaImageContainer, // JavaScript can't yet parse the message 'createacct-imgcaptcha-help' when it @@ -38,18 +39,12 @@ // Insert another div before the submit button that will include the // repositioned FancyCaptcha div, an input field, and possible help. - $submit.closest( 'div' ).before( [ - '<div>', - '<label for="wpCaptchaWord">' + mw.message( 'createacct-captcha' ).escaped() + '</label>', - '<div class="mw-createacct-captcha-container">', - '<div class="mw-createacct-captcha-and-reload" />', - '<input id="wpCaptchaWord" class="mw-ui-input" name="wpCaptchaWord" type="text" placeholder="' + - mw.message( 'createacct-imgcaptcha-ph' ).escaped() + - '" tabindex="' + tabIndex + '" autocapitalize="off" autocorrect="off">', - helpHtml, - '</div>', - '</div>' - ].join( '' ) ); + $el = $submit.closest( 'div' ).before( + mw.template.get( 'mediawiki.special.userlogin.common.js', 'captcha.html' ).render() ); + $el.find( 'label' ).text( mw.msg( 'createacct-captcha' ) ); + $el.find( '#wpCaptchaWord' ).attr( 'tabindex', tabIndex ). + attr( 'placeholder', mw.msg( 'createacct-imgcaptcha-ph' ) ); + $el.find( 'span' ).html( helpHtml ); // Stick the FancyCaptcha container inside our bordered and framed parents. $captchaImageContainer diff --git a/resources/src/mediawiki.special/templates/captcha.html b/resources/src/mediawiki.special/templates/captcha.html new file mode 100644 index 0000000..d30f191 --- /dev/null +++ b/resources/src/mediawiki.special/templates/captcha.html @@ -0,0 +1,9 @@ +<div> + <label for="wpCaptchaWord"></label> + <div class="mw-createacct-captcha-container"> + <div class="mw-createacct-captcha-and-reload" /> + <input id="wpCaptchaWord" class="mw-ui-input" name="wpCaptchaWord" + type="text" autocapitalize="off" autocorrect="off"> + <span/> + </div> +</div> diff --git a/resources/src/mediawiki.special/templates/thumbnail.html b/resources/src/mediawiki.special/templates/thumbnail.html new file mode 100644 index 0000000..73042f2 --- /dev/null +++ b/resources/src/mediawiki.special/templates/thumbnail.html @@ -0,0 +1,9 @@ +<div id="mw-upload-thumbnail" class="thumb tright"> + <div class="thumbinner"> + <div class="mw-small-spinner" style="width: 180px; height: 180px"></div> + <div class="thumbcaption"> + <div class="filename"></div> + <div class="fileinfo"></div> + </div> + </div> +</div> diff --git a/resources/src/mediawiki/mediawiki.feedback.js b/resources/src/mediawiki/mediawiki.feedback.js index 1c0d833..01a75f2 100644 --- a/resources/src/mediawiki/mediawiki.feedback.js +++ b/resources/src/mediawiki/mediawiki.feedback.js @@ -100,47 +100,20 @@ target: '_blank' } ); - // TODO: Use a stylesheet instead of these inline styles - this.$dialog = - $( '<div style="position: relative;"></div>' ).append( - $( '<div class="feedback-mode feedback-form"></div>' ).append( - $( '<small>' ).append( - $( '<p>' ).msg( - 'feedback-bugornote', - $bugNoteLink, - fb.title.getNameText(), - $feedbackPageLink.clone() - ) - ), - $( '<div style="margin-top: 1em;"></div>' ) - .msg( 'feedback-subject' ) - .append( - $( '<br>' ), - $( '<input type="text" class="feedback-subject" name="subject" maxlength="60" style="width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;"/>' ) - ), - $( '<div style="margin-top: 0.4em;"></div>' ) - .msg( 'feedback-message' ) - .append( - $( '<br>' ), - $( '<textarea name="message" class="feedback-message" rows="5" cols="60"></textarea>' ) - ) - ), - $( '<div class="feedback-mode feedback-bugs"></div>' ).append( - $( '<p>' ).msg( 'feedback-bugcheck', $bugsListLink ) - ), - $( '<div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;"></div>' ) - .msg( 'feedback-adding' ) - .append( - $( '<br>' ), - $( '<span class="feedback-spinner"></span>' ) - ), - $( '<div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div>' ).msg( - 'feedback-thanks', fb.title.getNameText(), $feedbackPageLink.clone() - ), - $( '<div class="feedback-mode feedback-error" style="position: relative;"></div>' ).append( - $( '<div class="feedback-error-msg style="color: #990000; margin-top: 0.4em;"></div>' ) - ) - ); + // TODO: Use a stylesheet instead of these inline styles in the template + this.$dialog = $( mw.template.get( 'mediawiki.feedback', 'dialog.html' ).render() ); + this.$dialog.find( '.feedback-mode small p' ).msg( + 'feedback-bugornote', + $bugNoteLink, + fb.title.getNameText(), + $feedbackPageLink.clone() + ); + this.$dialog.find( '.feedback-form .subject span' ).msg( 'feedback-subject' ); + this.$dialog.find( '.feedback-form .message span' ).msg( 'feedback-message' ); + this.$dialog.find( '.feedback-bugs p' ).msg( 'feedback-bugcheck', $bugsListLink ); + this.$dialog.find( '.feedback-submitting span' ).msg( 'feedback-adding' ); + this.$dialog.find( '.feedback-thanks' ).msg( 'feedback-thanks', fb.title.getNameText(), + $feedbackPageLink.clone() ); this.$dialog.dialog( { width: 500, diff --git a/resources/src/mediawiki/mediawiki.js b/resources/src/mediawiki/mediawiki.js index e29c734..a070986 100644 --- a/resources/src/mediawiki/mediawiki.js +++ b/resources/src/mediawiki/mediawiki.js @@ -483,6 +483,12 @@ */ messages: new Map(), + /** + * Templates associated with a module + * @property {mw.Map} + */ + templates: new Map(), + /* Public Methods */ /** @@ -1089,6 +1095,7 @@ */ function execute( module ) { var key, value, media, i, urls, cssHandle, checkCssHandles, + templates = {}, cssHandlesRegistered = false; if ( registry[module] === undefined ) { @@ -1168,6 +1175,12 @@ // Add localizations to message system if ( $.isPlainObject( registry[module].messages ) ) { mw.messages.set( registry[module].messages ); + } + + // Initialise templates + if ( !$.isEmptyObject( registry[module].templates ) ) { + templates[module] = registry[module].templates; + mw.templates.set( templates ); } if ( $.isReady || registry[module].async ) { @@ -1660,8 +1673,9 @@ * whether it's safe to extend the stylesheet (see #canExpandStylesheetWith). * * @param {Object} msgs List of key/value pairs to be added to mw#messages. + * @param {Object} templates List of key/value pairs to be added to mw#templates (optional) */ - implement: function ( module, script, style, msgs ) { + implement: function ( module, script, style, msgs, templates ) { // Validate input if ( typeof module !== 'string' ) { throw new Error( 'module must be a string, not a ' + typeof module ); @@ -1675,6 +1689,9 @@ if ( !$.isPlainObject( msgs ) ) { throw new Error( 'msgs must be an object, not a ' + typeof msgs ); } + if ( templates && !$.isPlainObject( templates ) ) { + throw new Error( 'templates must be an object, not a ' + typeof templates ); + } // Automatically register module if ( registry[module] === undefined ) { mw.loader.register( module ); @@ -1687,6 +1704,7 @@ registry[module].script = script; registry[module].style = style; registry[module].messages = msgs; + registry[module].templates = templates; // The module may already have been marked as erroneous if ( $.inArray( registry[module].state, ['error', 'missing'] ) === -1 ) { registry[module].state = 'loaded'; diff --git a/resources/src/mediawiki/mediawiki.templates.js b/resources/src/mediawiki/mediawiki.templates.js new file mode 100644 index 0000000..a6d26e6 --- /dev/null +++ b/resources/src/mediawiki/mediawiki.templates.js @@ -0,0 +1,120 @@ +/** + * @class mw.template + * @singleton + */ +( function ( mw ) { + var compiledTemplates = {}, + compilers = {}; + + mw.template = { + /** + * Register a new compiler and template + * @method + * @param {String} name of compiler. Should also match with any file extensions of templates that want to use it. + * @param {Function} compiler which must implement a compile function + */ + registerCompiler: function ( name, compiler ) { + if ( compiler.compile ) { + compilers[name] = compiler; + } else { + throw new Error( 'Template compiler must implement compile function.' ); + } + }, + /** + * Work out which compiler is associated with the template based on its suffix + * @method + * @param {String} templateName Name of template to add including file extension + * @return {Function} compiler + */ + getCompilerFromName: function ( templateName ) { + var templateParts = templateName.split( '.' ), compiler, + ext; + + if ( templateParts.length > 1 ) { + ext = templateParts[ templateParts.length - 1 ]; + if ( compilers[ ext ] ) { + compiler = compilers[ ext ]; + } else { + throw new Error( 'Template compiler not found for: ' + ext ); + } + } else { + throw new Error( 'Template has no suffix. Unable to identify compiler.' ); + } + return compiler; + }, + /** + * Define a template. Compiles newly added templates based on + * the file extension of name and the available compilers. + * @method + * @param {String} moduleName Name of RL module to get template from + * @param {String} templateName Name of template to add including file extension + * @param {String} markup Associated markup (html) + * @return {Function} compiled template + */ + add: function ( moduleName, templateName, markup ) { + var compiledTemplate, + compiler = this.getCompilerFromName( templateName ); + + // check module has a compiled template cache + compiledTemplates[moduleName] = compiledTemplates[moduleName] || {}; + + compiledTemplate = compiler.compile( markup ); + compiledTemplates[moduleName][ templateName ] = compiledTemplate; + return compiledTemplate; + }, + /** + * Retrieve defined template + * + * @method + * @param {string} name Name of template to be retrieved + * @return {Object} template compiler + * accepts template data object as its argument. + */ + get: function ( moduleName, templateName ) { + var moduleTemplates; + + // check if the template has already been compiled, compile it if not + if ( !compiledTemplates[ moduleName ] || !compiledTemplates[ moduleName ][ templateName ] ) { + moduleTemplates = mw.templates.get( moduleName ); + if ( !moduleTemplates ) { + throw new Error( 'No templates associated with module: ' + moduleName ); + } + + if ( moduleTemplates[ templateName ] ) { + // add compiled version + this.add( moduleName, templateName, moduleTemplates[ templateName ] ); + } else { + throw new Error( 'Template in module ' + moduleName + ' not found: ' + templateName ); + } + } + return compiledTemplates[ moduleName ][ templateName ]; + }, + /** + * Wraps our template engine of choice + * @method + * @param {string} templateBody Template body. + * @param {string} compilerName The name of a registered compiler + * @return {Object} template interface + * accepts template data object as its argument. + */ + compile: function ( templateBody, compilerName ) { + var compiler = compilers[ compilerName ]; + if ( !compiler ) { + throw new Error( 'Unknown compiler ' + compilerName ); + } + return compiler.compile( templateBody ); + } + }; + + // Register basic html compiler + mw.template.registerCompiler( 'html', { + compile: function ( src ) { + return { + render: function () { + return src; + } + }; + } + } ); + +}( mediaWiki ) ); diff --git a/resources/src/mediawiki/templates/dialog.html b/resources/src/mediawiki/templates/dialog.html new file mode 100644 index 0000000..e116f3e --- /dev/null +++ b/resources/src/mediawiki/templates/dialog.html @@ -0,0 +1,25 @@ +<div style="position: relative; display: block;" class="ui-dialog-content ui-widget-content"> + <div class="feedback-mode feedback-form"> + <small><p></p></small> + <div class="subject" style="margin-top: 1em;"> + <span></span><br> + <input type="text" class="feedback-subject" name="subject" maxlength="60" + style="width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;"> + </div> + <div class="message" style="margin-top: 0.4em;"> + <span></span><br> + <textarea name="message" class="feedback-message" rows="5" cols="60"></textarea> + </div> + </div> + <div class="feedback-mode feedback-bugs"> + <p></p> + </div> + <div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;"> + <span></span><br> + <span class="feedback-spinner"></span> + </div> + <div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div> + <div class="feedback-mode feedback-error" style="position: relative;"> + <div class="feedback-error-msg" style=" color:#990000; margin-top:0.4em;"></div> + </div> +</div> diff --git a/tests/phpunit/includes/OutputPageTest.php b/tests/phpunit/includes/OutputPageTest.php index d7e8cd3..89d1de7 100644 --- a/tests/phpunit/includes/OutputPageTest.php +++ b/tests/phpunit/includes/OutputPageTest.php @@ -172,7 +172,7 @@ array( array( 'test.quux', ResourceLoaderModule::TYPE_COMBINED ), '<script>if(window.mw){ -mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n"]},{}); +mw.loader.implement("test.quux",function($,jQuery){mw.test.baz({token:123});},{"css":[".mw-icon{transition:none}\n"]},{},{}); }</script> ' diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php new file mode 100644 index 0000000..474a01b --- /dev/null +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @group ResourceLoader + */ +class ResourceLoaderFileModuleTest extends MediaWikiTestCase { + static function getModules() { + $base = array( + 'localBasePath' => realpath( dirname( __FILE__ ) ), + ); + + return array( + 'noTemplateModule' => array(), + + 'htmlTemplateModule' => $base + array( + 'templates' => array( + 'templates/template.html', + 'templates/template2.html', + ) + ), + + 'aliasedHtmlTemplateModule' => $base + array( + 'templates' => array( + 'foo.html' => 'templates/template.html', + 'bar.html' => 'templates/template2.html', + ) + ), + + 'templateModuleHandlebars' => $base + array( + 'templates' => array( + 'templates/template_awesome.handlebars', + ), + ), + ); + } + + public function providerGetTemplates() { + $modules = self::getModules(); + + return array( + array( + $modules['noTemplateModule'], + array(), + ), + array( + $modules['templateModuleHandlebars'], + array( + 'templates/template_awesome.handlebars' => "wow\n", + ), + ), + array( + $modules['htmlTemplateModule'], + array( + 'templates/template.html' => "<strong>hello</strong>\n", + 'templates/template2.html' => "<div>goodbye</div>\n", + ), + ), + array( + $modules['aliasedHtmlTemplateModule'], + array( + 'foo.html' => "<strong>hello</strong>\n", + 'bar.html' => "<div>goodbye</div>\n", + ), + ), + ); + } + + public function providerGetModifiedTime() { + $modules = self::getModules(); + + return array( + // Check the default value when no templates present in module is 1 + array( $modules['noTemplateModule'], 1 ), + ); + } + + // tests + /** + * @dataProvider providerGetTemplates + */ + public function testGetTemplates( $module, $expected ) { + $rl = new ResourceLoaderFileModule( $module ); + + $this->assertEquals( $rl->getTemplates(), $expected ); + } + + /** + * @dataProvider providerGetModifiedTime + */ + public function testGetModifiedTime( $module, $expected ) { + $rl = new ResourceLoaderFileModule( $module ); + $ts = $rl->getModifiedTime( new ResourceLoaderContext( + new ResourceLoader, new WebRequest() ) ); + $this->assertEquals( $ts, $expected ); + } +} diff --git a/tests/phpunit/includes/resourceloader/templates/template.html b/tests/phpunit/includes/resourceloader/templates/template.html new file mode 100644 index 0000000..1f6a7d2 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template.html @@ -0,0 +1 @@ +<strong>hello</strong> diff --git a/tests/phpunit/includes/resourceloader/templates/template2.html b/tests/phpunit/includes/resourceloader/templates/template2.html new file mode 100644 index 0000000..a322f67 --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template2.html @@ -0,0 +1 @@ +<div>goodbye</div> diff --git a/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars b/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars new file mode 100644 index 0000000..5f5c07d --- /dev/null +++ b/tests/phpunit/includes/resourceloader/templates/template_awesome.handlebars @@ -0,0 +1 @@ +wow diff --git a/tests/qunit/QUnitTestResources.php b/tests/qunit/QUnitTestResources.php index 34007ed..44d6efc 100644 --- a/tests/qunit/QUnitTestResources.php +++ b/tests/qunit/QUnitTestResources.php @@ -66,6 +66,7 @@ 'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js', diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js new file mode 100644 index 0000000..96413f1 --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js @@ -0,0 +1,51 @@ +( function ( mw, $ ) { + + QUnit.module( 'Templates', { + setup: function () { + var abcCompiler = { + registerPartial: $.noop, + compile: function () { + return 'abc default compiler'; + } + }; + // Register some template compiler languages + mw.template.registerCompiler( 'abc', abcCompiler ); + mw.template.registerCompiler( 'xyz', { + registerPartial: $.noop, + compile: function () { + return 'xyz compiler'; + } + } ); + + // register some templates + mw.templates.set( { + 'test.mediawiki.templates': { + 'test_templates_foo.xyz': 'goodbye', + 'test_templates_foo.abc': 'thankyou' + } + } ); + } + } ); + + QUnit.test( 'Template, getCompiler - default case', 4, function ( assert ) { + assert.throws( function () { + mw.template.add( 'module', 'test_templates_foo', 'hello' ); + }, 'When no prefix throw exception.' ); + assert.throws( function () { + mw.template.compile( '{{foo}}', 'rainbow' ); + }, 'Unknown compiler names throw exceptions.' ); + assert.strictEqual( mw.template.get( 'test.mediawiki.templates', 'test_templates_foo.xyz' ), 'xyz compiler' ); + assert.strictEqual( mw.template.get( 'test.mediawiki.templates', 'test_templates_foo.abc' ), 'abc default compiler' ); + } ); + + QUnit.test( 'Template, get module that is not loaded.', 2, function ( assert ) { + assert.throws( function () { + mw.template.get( 'this.should.not.exist', 'hello' ); + }, 'When bad module name given throw error.' ); + + assert.throws( function () { + mw.template.get( 'mediawiki.templates', 'hello' ); + }, 'The template hello should not exist in the mediawiki.templates module and should throw an exception.' ); + } ); + +}( mediaWiki, jQuery ) ); -- To view, visit https://gerrit.wikimedia.org/r/167342 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia0c5c8ec960aa6dff12c9626cee41ae9a3286b76 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Jdlrobson <jrob...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits