Anomie has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/347442 )

Change subject: ResourceLoader: Add wildcard modules
......................................................................

ResourceLoader: Add wildcard modules

It may be that something needs to register an unbounded set of RL
modules, for example to use the deduplication mechanism added in
Ibc3fc3723 TemplateStyles needs to register a module for every revision
on the wiki using its content type. Actually enumerating and registering
these modules in the usual way would be crazy.

Thus, wildcard modules. TemplateStyles can now register
"ext.templatestyles.rev.*" and ResourceLoader will synthesize a
registration for ext.templatestyles.rev.1, ext.templatestyles.rev.2,
ext.templatestyles.rev.8817900, and so on if those modules are
requested.

Change-Id: I088acfd1e461be6204d1fc62b85a0eb2b8d49be7
---
M RELEASE-NOTES-1.29
M includes/resourceloader/ResourceLoader.php
M resources/src/mediawiki/mediawiki.inspect.js
M resources/src/mediawiki/mediawiki.js
M tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
M tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
6 files changed, 178 insertions(+), 86 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/42/347442/1

diff --git a/RELEASE-NOTES-1.29 b/RELEASE-NOTES-1.29
index 8b6ebac..ed20c27 100644
--- a/RELEASE-NOTES-1.29
+++ b/RELEASE-NOTES-1.29
@@ -67,6 +67,7 @@
 * (T160563) The PHP parser now supports embedding ResourceLoader style modules
   inline in the parsed HTML, with deduplication if the same ResourceLoader
   module is embedded multiple times.
+* ResourceLoader now supports wildcard modules.
 
 === External library changes in 1.29 ===
 
diff --git a/includes/resourceloader/ResourceLoader.php 
b/includes/resourceloader/ResourceLoader.php
index e72eaf2..f00578d 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -477,10 +477,13 @@
        /**
         * Get a list of module names.
         *
-        * @return array List of module names
+        * @return array List of module names. Does not include modules that 
could
+        *  be loaded via wildcards.
         */
        public function getModuleNames() {
-               return array_keys( $this->moduleInfos );
+               return array_keys( array_filter( $this->moduleInfos, function ( 
$info ) {
+                       return empty( $info['fromWildcard'] );
+               } ) );
        }
 
        /**
@@ -509,12 +512,27 @@
        /**
         * Check whether a ResourceLoader module is registered
         *
+        * If it's possible to load it via a wildcard, this returns true.
+        *
         * @since 1.25
         * @param string $name
         * @return bool
         */
        public function isModuleRegistered( $name ) {
-               return isset( $this->moduleInfos[$name] );
+               if ( isset( $this->moduleInfos[$name] ) ) {
+                       return true;
+               }
+
+               $nameParts = explode( '.', $name );
+               if ( array_pop( $nameParts ) !== '*' ) {
+                       $wildcard = implode( '.', $nameParts ) . '.*';
+                       if ( isset( $this->moduleInfos[$wildcard] ) ) {
+                               $this->moduleInfos[$name] = [ 'fromWildcard' => 
true ] + $this->moduleInfos[$wildcard];
+                               return true;
+                       }
+               }
+
+               return false;
        }
 
        /**
@@ -530,7 +548,7 @@
         */
        public function getModule( $name ) {
                if ( !isset( $this->modules[$name] ) ) {
-                       if ( !isset( $this->moduleInfos[$name] ) ) {
+                       if ( !$this->isModuleRegistered( $name ) ) {
                                // No such module
                                return null;
                        }
@@ -565,7 +583,7 @@
         * @return bool
         */
        protected function isFileModule( $name ) {
-               if ( !isset( $this->moduleInfos[$name] ) ) {
+               if ( !$this->isModuleRegistered( $name ) ) {
                        return false;
                }
                $info = $this->moduleInfos[$name];
diff --git a/resources/src/mediawiki/mediawiki.inspect.js 
b/resources/src/mediawiki/mediawiki.inspect.js
index 638fba7..27f5844 100644
--- a/resources/src/mediawiki/mediawiki.inspect.js
+++ b/resources/src/mediawiki/mediawiki.inspect.js
@@ -48,7 +48,7 @@
                                graph = {};
 
                        $.each( modules, function ( moduleIndex, moduleName ) {
-                               var dependencies = mw.loader.moduleRegistry[ 
moduleName ].dependencies || [];
+                               var dependencies = 
mw.loader.moduleRegistry.get( moduleName ).dependencies || [];
 
                                if ( !hasOwn.call( graph, moduleName ) ) {
                                        graph[ moduleName ] = { requiredBy: [] 
};
@@ -72,7 +72,7 @@
                 * @return {number|null} Module size in bytes or null
                 */
                getModuleSize: function ( moduleName ) {
-                       var module = mw.loader.moduleRegistry[ moduleName ],
+                       var module = mw.loader.moduleRegistry.get( moduleName ),
                                args, i, size;
 
                        if ( module.state !== 'ready' ) {
@@ -252,7 +252,7 @@
                                var modules = [];
 
                                $.each( inspect.getLoadedModules(), function ( 
index, name ) {
-                                       var css, stats, module = 
mw.loader.moduleRegistry[ name ];
+                                       var css, stats, module = 
mw.loader.moduleRegistry.get( name );
 
                                        try {
                                                css = module.style.css.join();
@@ -306,7 +306,7 @@
                        }
 
                        return $.grep( inspect.getLoadedModules(), function ( 
moduleName ) {
-                               var module = mw.loader.moduleRegistry[ 
moduleName ];
+                               var module = mw.loader.moduleRegistry.get( 
moduleName );
 
                                // Grep module's JavaScript
                                if ( $.isFunction( module.script ) && 
pattern.test( module.script.toString() ) ) {
diff --git a/resources/src/mediawiki/mediawiki.js 
b/resources/src/mediawiki/mediawiki.js
index 86a9a0a..1400531 100644
--- a/resources/src/mediawiki/mediawiki.js
+++ b/resources/src/mediawiki/mediawiki.js
@@ -825,7 +825,11 @@
                         * @property
                         * @private
                         */
-                       var registry = {},
+                       var registry = {
+                                       data: {},
+                                       wildcards: {}
+                               },
+
                                // Mapping of sources, keyed by source-id, 
values are strings.
                                //
                                // Format:
@@ -872,6 +876,42 @@
                                cssBufferTimer = null,
                                cssCallbacks = $.Callbacks(),
                                rAF = window.requestAnimationFrame || 
setTimeout;
+
+                       /**
+                        * Get a module from the registry
+                        *
+                        * @private
+                        * @param {string} module Module name
+                        * @return {Object|undefined} Module data
+                        */
+                       registry.get = function ( module ) {
+                               var wildcard;
+
+                               if ( !hasOwn.call( registry.data, module ) ) {
+                                       wildcard = module.substr( 0, 
module.lastIndexOf( '.' ) ) + '.*';
+                                       if ( hasOwn.call( registry.wildcards, 
wildcard ) ) {
+                                               registry.data[ module ] = 
$.extend( {}, registry.wildcards[ wildcard ] );
+                                               registry.data[ module 
].fromWildcard = true;
+                                       } else {
+                                               registry.data[ module ] = 
undefined;
+                                       }
+                               }
+                               return registry.data[ module ];
+                       };
+
+                       /**
+                        * Set module data into the registry
+                        *
+                        * @private
+                        * @param {string} module Module name
+                        * @param {Object|undefined} data Module data
+                        */
+                       registry.set = function ( module, data ) {
+                               if ( module.substr( module.lastIndexOf( '.' ) ) 
=== '.*' ) {
+                                       registry.wildcards[ module ] = data;
+                               }
+                               registry.data[ module ] = data;
+                       };
 
                        function getMarker() {
                                if ( !marker ) {
@@ -973,7 +1013,7 @@
                         */
                        function getCombinedVersion( modules ) {
                                var hashes = modules.map( function ( module ) {
-                                       return registry[ module ].version;
+                                       return registry.get( module ).version;
                                } );
                                return fnv132( hashes.join( '' ) );
                        }
@@ -1029,16 +1069,16 @@
                        function handlePending( module ) {
                                var j, job, hasErrors, m, stateChange;
 
-                               if ( registry[ module ].state === 'error' || 
registry[ module ].state === 'missing' ) {
+                               if ( registry.get( module ).state === 'error' 
|| registry.get( module ).state === 'missing' ) {
                                        // If the current module failed, mark 
all dependent modules also as failed.
                                        // Iterate until steady-state to 
propagate the error state upwards in the
                                        // dependency tree.
                                        do {
                                                stateChange = false;
-                                               for ( m in registry ) {
-                                                       if ( registry[ m 
].state !== 'error' && registry[ m ].state !== 'missing' ) {
-                                                               if ( anyFailed( 
registry[ m ].dependencies ) ) {
-                                                                       
registry[ m ].state = 'error';
+                                               for ( m in registry.data ) {
+                                                       if ( registry.data[ m ] 
&& registry.data[ m ].state !== 'error' && registry.data[ m ].state !== 
'missing' ) {
+                                                               if ( anyFailed( 
registry.data[ m ].dependencies ) ) {
+                                                                       
registry.data[ m ].state = 'error';
                                                                        
stateChange = true;
                                                                }
                                                        }
@@ -1072,12 +1112,12 @@
                                        }
                                }
 
-                               if ( registry[ module ].state === 'ready' ) {
+                               if ( registry.get( module ).state === 'ready' ) 
{
                                        // The current module became 'ready'. 
Set it in the module store, and recursively execute all
                                        // dependent modules that are loaded 
and now have all dependencies satisfied.
-                                       mw.loader.store.set( module, registry[ 
module ] );
-                                       for ( m in registry ) {
-                                               if ( registry[ m ].state === 
'loaded' && allReady( registry[ m ].dependencies ) ) {
+                                       mw.loader.store.set( module, 
registry.get( module ) );
+                                       for ( m in registry.data ) {
+                                               if ( registry.data[ m ] && 
registry.data[ m ].state === 'loaded' && allReady( registry.data[ m 
].dependencies ) ) {
                                                        execute( m );
                                                }
                                        }
@@ -1101,29 +1141,29 @@
                        function sortDependencies( module, resolved, unresolved 
) {
                                var i, deps, skip;
 
-                               if ( !hasOwn.call( registry, module ) ) {
+                               if ( registry.get( module ) === undefined ) {
                                        throw new Error( 'Unknown dependency: ' 
+ module );
                                }
 
-                               if ( registry[ module ].skip !== null ) {
+                               if ( registry.get( module ).skip !== null ) {
                                        // eslint-disable-next-line no-new-func
-                                       skip = new Function( registry[ module 
].skip );
-                                       registry[ module ].skip = null;
+                                       skip = new Function( registry.get( 
module ).skip );
+                                       registry.get( module ).skip = null;
                                        if ( skip() ) {
-                                               registry[ module ].skipped = 
true;
-                                               registry[ module ].dependencies 
= [];
-                                               registry[ module ].state = 
'ready';
+                                               registry.get( module ).skipped 
= true;
+                                               registry.get( module 
).dependencies = [];
+                                               registry.get( module ).state = 
'ready';
                                                handlePending( module );
                                                return;
                                        }
                                }
 
                                // Resolves dynamic loader function and 
replaces it with its own results
-                               if ( typeof registry[ module ].dependencies === 
'function' ) {
-                                       registry[ module ].dependencies = 
registry[ module ].dependencies();
+                               if ( typeof registry.get( module ).dependencies 
=== 'function' ) {
+                                       registry.get( module ).dependencies = 
registry.get( module ).dependencies();
                                        // Ensures the module's dependencies 
are always in an array
-                                       if ( typeof registry[ module 
].dependencies !== 'object' ) {
-                                               registry[ module ].dependencies 
= [ registry[ module ].dependencies ];
+                                       if ( typeof registry.get( module 
).dependencies !== 'object' ) {
+                                               registry.get( module 
).dependencies = [ registry.get( module ).dependencies ];
                                        }
                                }
                                if ( $.inArray( module, resolved ) !== -1 ) {
@@ -1135,7 +1175,7 @@
                                        unresolved = new StringSet();
                                }
                                // Tracks down dependencies
-                               deps = registry[ module ].dependencies;
+                               deps = registry.get( module ).dependencies;
                                for ( i = 0; i < deps.length; i++ ) {
                                        if ( $.inArray( deps[ i ], resolved ) 
=== -1 ) {
                                                if ( unresolved.has( deps[ i ] 
) ) {
@@ -1202,10 +1242,10 @@
                                var r = $.Deferred();
 
                                pendingRequests.push( function () {
-                                       if ( moduleName && hasOwn.call( 
registry, moduleName ) ) {
+                                       if ( moduleName && registry.get( 
moduleName ) !== undefined ) {
                                                // Emulate runScript() part of 
execute()
                                                window.require = 
mw.loader.require;
-                                               window.module = registry[ 
moduleName ].module;
+                                               window.module = registry.get( 
moduleName ).module;
                                        }
                                        addScript( src ).always( function () {
                                                // 'module.exports' should not 
persist after the file is executed to
@@ -1260,14 +1300,14 @@
                                var key, value, media, i, urls, cssHandle, 
checkCssHandles, runScript,
                                        cssHandlesRegistered = false;
 
-                               if ( !hasOwn.call( registry, module ) ) {
+                               if ( registry.get( module ) === undefined ) {
                                        throw new Error( 'Module has not been 
registered yet: ' + module );
                                }
-                               if ( registry[ module ].state !== 'loaded' ) {
-                                       throw new Error( 'Module in state "' + 
registry[ module ].state + '" may not be executed: ' + module );
+                               if ( registry.get( module ).state !== 'loaded' 
) {
+                                       throw new Error( 'Module in state "' + 
registry.get( module ).state + '" may not be executed: ' + module );
                                }
 
-                               registry[ module ].state = 'executing';
+                               registry.get( module ).state = 'executing';
 
                                runScript = function () {
                                        var script, markModuleReady, 
nestedAddScript, legacyWait, implicitDependencies,
@@ -1275,9 +1315,9 @@
                                                // and their dependencies from 
the legacyWait (to prevent a circular dependency).
                                                legacyModules = resolve( 
mw.config.get( 'wgResourceLoaderLegacyModules', [] ) );
 
-                                       script = registry[ module ].script;
+                                       script = registry.get( module ).script;
                                        markModuleReady = function () {
-                                               registry[ module ].state = 
'ready';
+                                               registry.get( module ).state = 
'ready';
                                                handlePending( module );
                                        };
                                        nestedAddScript = function ( arr, 
callback, i ) {
@@ -1315,7 +1355,7 @@
                                                        } else if ( typeof 
script === 'function' ) {
                                                                // Pass jQuery 
twice so that the signature of the closure which wraps
                                                                // the script 
can bind both '$' and 'jQuery'.
-                                                               script( $, $, 
mw.loader.require, registry[ module ].module );
+                                                               script( $, $, 
mw.loader.require, registry.get( module ).module );
                                                                
markModuleReady();
 
                                                        } else if ( typeof 
script === 'string' ) {
@@ -1332,7 +1372,7 @@
                                                } catch ( e ) {
                                                        // Use mw.track instead 
of mw.log because these errors are common in production mode
                                                        // (e.g. undefined 
variable), and mw.log is only enabled in debug mode.
-                                                       registry[ module 
].state = 'error';
+                                                       registry.get( module 
).state = 'error';
                                                        mw.track( 
'resourceloader.exception', { exception: e, module: module, source: 
'module-execute' } );
                                                        handlePending( module );
                                                }
@@ -1340,13 +1380,13 @@
                                };
 
                                // Add localizations to message system
-                               if ( registry[ module ].messages ) {
-                                       mw.messages.set( registry[ module 
].messages );
+                               if ( registry.get( module ).messages ) {
+                                       mw.messages.set( registry.get( module 
).messages );
                                }
 
                                // Initialise templates
-                               if ( registry[ module ].templates ) {
-                                       mw.templates.set( module, registry[ 
module ].templates );
+                               if ( registry.get( module ).templates ) {
+                                       mw.templates.set( module, registry.get( 
module ).templates );
                                }
 
                                // Make sure we don't run the scripts until all 
stylesheet insertions have completed.
@@ -1378,9 +1418,9 @@
                                // * back-compat: { <media>: [url, ..] }
                                // * { "css": [css, ..] }
                                // * { "url": { <media>: [url, ..] } }
-                               if ( registry[ module ].style ) {
-                                       for ( key in registry[ module ].style ) 
{
-                                               value = registry[ module 
].style[ key ];
+                               if ( registry.get( module ).style ) {
+                                       for ( key in registry.get( module 
).style ) {
+                                               value = registry.get( module 
).style[ key ];
                                                media = undefined;
 
                                                if ( key !== 'url' && key !== 
'css' ) {
@@ -1466,8 +1506,8 @@
                                        if ( state === 'registered' && 
$.inArray( module, queue ) === -1 ) {
                                                // Private modules must be 
embedded in the page. Don't bother queuing
                                                // these as the server will 
deny them anyway (T101806).
-                                               if ( registry[ module ].group 
=== 'private' ) {
-                                                       registry[ module 
].state = 'error';
+                                               if ( registry.get( module 
).group === 'private' ) {
+                                                       registry.get( module 
).state = 'error';
                                                        handlePending( module );
                                                        return;
                                                }
@@ -1593,8 +1633,8 @@
                                // Split module list by source and by group.
                                splits = {};
                                for ( b = 0; b < batch.length; b++ ) {
-                                       bSource = registry[ batch[ b ] ].source;
-                                       bGroup = registry[ batch[ b ] ].group;
+                                       bSource = registry.get( batch[ b ] 
).source;
+                                       bGroup = registry.get( batch[ b ] 
).group;
                                        if ( !hasOwn.call( splits, bSource ) ) {
                                                splits[ bSource ] = {};
                                        }
@@ -1693,8 +1733,8 @@
                         *  or null if the module does not exist
                         */
                        function getModuleKey( module ) {
-                               return hasOwn.call( registry, module ) ?
-                                       ( module + '@' + registry[ module 
].version ) : null;
+                               return registry.get( module ) !== undefined ?
+                                       ( module + '@' + registry.get( module 
).version ) : null;
                        }
 
                        /**
@@ -1744,12 +1784,12 @@
                                        // Appends a list of modules from the 
queue to the batch
                                        for ( q = 0; q < queue.length; q++ ) {
                                                // Only load modules which are 
registered
-                                               if ( hasOwn.call( registry, 
queue[ q ] ) && registry[ queue[ q ] ].state === 'registered' ) {
+                                               if ( registry.get( queue[ q ] ) 
!== undefined && registry.get( queue[ q ] ).state === 'registered' ) {
                                                        // Prevent duplicate 
entries
                                                        if ( $.inArray( queue[ 
q ], batch ) === -1 ) {
                                                                batch.push( 
queue[ q ] );
                                                                // Mark 
registered modules as loading
-                                                               registry[ 
queue[ q ] ].state = 'loading';
+                                                               registry.get( 
queue[ q ] ).state = 'loading';
                                                        }
                                                }
                                        }
@@ -1795,7 +1835,7 @@
                                                        mw.track( 
'resourceloader.exception', { exception: err, source: 'store-eval' } );
                                                        // Re-add the failed 
ones that are still pending back to the batch
                                                        failed = $.grep( 
sourceModules, function ( module ) {
-                                                               return 
registry[ module ].state === 'loading';
+                                                               return 
registry.get( module ).state === 'loading';
                                                        } );
                                                        batchRequest( failed );
                                                } );
@@ -1867,7 +1907,7 @@
                                                }
                                                return;
                                        }
-                                       if ( hasOwn.call( registry, module ) ) {
+                                       if ( registry.get( module ) !== 
undefined ) {
                                                throw new Error( 'module 
already registered: ' + module );
                                        }
                                        if ( typeof dependencies === 'string' ) 
{
@@ -1878,7 +1918,7 @@
                                                deps = dependencies;
                                        }
                                        // List the module as registered
-                                       registry[ module ] = {
+                                       registry.set( module, {
                                                // Exposed to execute() for 
mw.loader.implement() closures.
                                                // Import happens via require().
                                                module: {
@@ -1890,7 +1930,7 @@
                                                source: typeof source === 
'string' ? source : 'local',
                                                state: 'registered',
                                                skip: typeof skip === 'string' 
? skip : null
-                                       };
+                                       } );
                                },
 
                                /**
@@ -1927,11 +1967,11 @@
                                                name = split.name,
                                                version = split.version;
                                        // Automatically register module
-                                       if ( !hasOwn.call( registry, name ) ) {
+                                       if ( registry.get( name ) === undefined 
) {
                                                mw.loader.register( name );
                                        }
                                        // Check for duplicate implementation
-                                       if ( hasOwn.call( registry, name ) && 
registry[ name ].script !== undefined ) {
+                                       if ( registry.get( name ) !== undefined 
&& registry.get( name ).script !== undefined ) {
                                                throw new Error( 'module 
already implemented: ' + name );
                                        }
                                        if ( version ) {
@@ -1939,17 +1979,17 @@
                                                // requested and received 
module version, then mw.loader.store would
                                                // cache the response under the 
requested key. Thus poisoning the cache
                                                // indefinitely with a stale 
value. (T117587)
-                                               registry[ name ].version = 
version;
+                                               registry.get( name ).version = 
version;
                                        }
                                        // Attach components
-                                       registry[ name ].script = script || 
null;
-                                       registry[ name ].style = style || null;
-                                       registry[ name ].messages = messages || 
null;
-                                       registry[ name ].templates = templates 
|| null;
+                                       registry.get( name ).script = script || 
null;
+                                       registry.get( name ).style = style || 
null;
+                                       registry.get( name ).messages = 
messages || null;
+                                       registry.get( name ).templates = 
templates || null;
                                        // The module may already have been 
marked as erroneous
-                                       if ( $.inArray( registry[ name ].state, 
[ 'error', 'missing' ] ) === -1 ) {
-                                               registry[ name ].state = 
'loaded';
-                                               if ( allReady( registry[ name 
].dependencies ) ) {
+                                       if ( $.inArray( registry.get( name 
).state, [ 'error', 'missing' ] ) === -1 ) {
+                                               registry.get( name ).state = 
'loaded';
+                                               if ( allReady( registry.get( 
name ).dependencies ) ) {
                                                        execute( name );
                                                }
                                        }
@@ -2086,10 +2126,10 @@
                                                }
                                                return;
                                        }
-                                       if ( !hasOwn.call( registry, module ) ) 
{
+                                       if ( registry.get( module ) === 
undefined ) {
                                                mw.loader.register( module );
                                        }
-                                       registry[ module ].state = state;
+                                       registry.get( module ).state = state;
                                        if ( $.inArray( state, [ 'ready', 
'error', 'missing' ] ) !== -1 ) {
                                                // Make sure pending modules 
depending on this one get executed if their
                                                // dependencies are now 
fulfilled!
@@ -2105,10 +2145,10 @@
                                 *  in the registry.
                                 */
                                getVersion: function ( module ) {
-                                       if ( !hasOwn.call( registry, module ) 
|| registry[ module ].version === undefined ) {
+                                       if ( registry.get( module ) === 
undefined || registry.get( module ).version === undefined ) {
                                                return null;
                                        }
-                                       return registry[ module ].version;
+                                       return registry.get( module ).version;
                                },
 
                                /**
@@ -2119,19 +2159,23 @@
                                 *  in the registry.
                                 */
                                getState: function ( module ) {
-                                       if ( !hasOwn.call( registry, module ) 
|| registry[ module ].state === undefined ) {
+                                       if ( registry.get( module ) === 
undefined || registry.get( module ).state === undefined ) {
                                                return null;
                                        }
-                                       return registry[ module ].state;
+                                       return registry.get( module ).state;
                                },
 
                                /**
                                 * Get the names of all registered modules.
                                 *
+                                * Does not include modules that are registered 
via wildcards.
+                                *
                                 * @return {Array}
                                 */
                                getModuleNames: function () {
-                                       return Object.keys( registry );
+                                       return $.map( registry.data, function ( 
data, key ) {
+                                               return data.fromWildcard ? null 
: key;
+                                       } );
                                },
 
                                /**
@@ -2153,7 +2197,7 @@
                                                throw new Error( 'Module "' + 
moduleName + '" is not loaded.' );
                                        }
 
-                                       return registry[ moduleName 
].module.exports;
+                                       return registry.get( moduleName 
).module.exports;
                                },
 
                                /**
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php 
b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
index 2618e78..1467e41 100644
--- a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
+++ b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
@@ -98,6 +98,18 @@
        }
 
        /**
+        * @covers ResourceLoader::register
+        * @covers ResourceLoader::getModule
+        */
+       public function testRegisterWildcard() {
+               $module = new ResourceLoaderTestModule();
+               $resourceLoader = new EmptyResourceLoader();
+               $resourceLoader->register( 'test.foo.*', $module );
+               $this->assertEquals( $module, $resourceLoader->getModule( 
'test.foo.bar' ) );
+               $this->assertEquals( $module, $resourceLoader->getModule( 
'test.foo.baz' ) );
+       }
+
+       /**
         * @covers ResourceLoader::getModuleNames
         */
        public function testGetModuleNames() {
@@ -105,8 +117,10 @@
                $resourceLoader = new EmptyResourceLoader();
                $resourceLoader->register( 'test.foo', new 
ResourceLoaderTestModule() );
                $resourceLoader->register( 'test.bar', new 
ResourceLoaderTestModule() );
+               $resourceLoader->register( 'test.baz.*', new 
ResourceLoaderTestModule() );
+               $resourceLoader->isModuleRegistered( 'test.baz.123' );
                $this->assertEquals(
-                       [ 'test.foo', 'test.bar' ],
+                       [ 'test.foo', 'test.bar', 'test.baz.*' ],
                        $resourceLoader->getModuleNames()
                );
        }
@@ -117,8 +131,12 @@
        public function testIsModuleRegistered() {
                $rl = new EmptyResourceLoader();
                $rl->register( 'test', new ResourceLoaderTestModule() );
+               $rl->register( 'test.foo.*', new ResourceLoaderTestModule() );
                $this->assertTrue( $rl->isModuleRegistered( 'test' ) );
                $this->assertFalse( $rl->isModuleRegistered( 'test.unknown' ) );
+               $this->assertTrue( $rl->isModuleRegistered( 'test.foo.*' ) );
+               $this->assertTrue( $rl->isModuleRegistered( 'test.foo.bar' ) );
+               $this->assertFalse( $rl->isModuleRegistered( 'test.foo.bar.baz' 
) );
        }
 
        /**
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js 
b/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
index 7a0de81..1e16d16 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
@@ -735,9 +735,9 @@
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store
-                               mw.loader.moduleRegistry[ 'test.stale' ].script 
= undefined;
-                               mw.loader.moduleRegistry[ 'test.stale' ].state 
= 'registered';
-                               mw.loader.moduleRegistry[ 'test.stale' 
].version = 'v2';
+                               mw.loader.moduleRegistry.get( 'test.stale' 
).script = undefined;
+                               mw.loader.moduleRegistry.get( 'test.stale' 
).state = 'registered';
+                               mw.loader.moduleRegistry.get( 'test.stale' 
).version = 'v2';
 
                                // Module was stored correctly as v1
                                // On future navigations, it will be ignored 
until evicted
@@ -763,9 +763,9 @@
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store
-                               mw.loader.moduleRegistry[ 'test.stalebc' 
].script = undefined;
-                               mw.loader.moduleRegistry[ 'test.stalebc' 
].state = 'registered';
-                               mw.loader.moduleRegistry[ 'test.stalebc' 
].version = 'v2';
+                               mw.loader.moduleRegistry.get( 'test.stalebc' 
).script = undefined;
+                               mw.loader.moduleRegistry.get( 'test.stalebc' 
).state = 'registered';
+                               mw.loader.moduleRegistry.get( 'test.stalebc' 
).version = 'v2';
 
                                // Legacy behaviour is storing under the 
expected version,
                                // which woudl lead to whitewashing and stale 
values (T117587).
@@ -842,4 +842,15 @@
                } );
        } );
 
+       QUnit.test( 'Wildcard modules are seen as registered', function ( 
assert ) {
+               mw.loader.register( [
+                       [ 'test.wildcard.*', '0' ]
+               ] );
+
+               assert.strictEqual( mw.loader.getState( 'test.wildcard.foo' ), 
'registered', 'Expected "registered" state for test.wildcard.foo' );
+               assert.strictEqual( mw.loader.getState( 'test.wildcard.bar' ), 
'registered', 'Expected "registered" state for test.wildcard.bar' );
+               assert.strictEqual( mw.loader.getState( 'test.wildcard.*' ), 
'registered', 'Expected "registered" state for test.wildcard.*' );
+               assert.strictEqual( mw.loader.getState( 'test.wildcard.baz.baz' 
), null, 'Expected null state for test.wildcard.baz.baz' );
+       } );
+
 }( mediaWiki, jQuery ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/347442
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I088acfd1e461be6204d1fc62b85a0eb2b8d49be7
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to