3 * Sanity checks for making sure registered resources are sane.
6 * @author Antoine Musso
7 * @author Niklas Laxström
8 * @author Santhosh Thottingal
10 * @copyright © 2012, Antoine Musso
11 * @copyright © 2012, Niklas Laxström
12 * @copyright © 2012, Santhosh Thottingal
13 * @copyright © 2012, Timo Tijhof
15 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
17 class ResourcesTest
extends MediaWikiTestCase
{
20 * @dataProvider provideResourceFiles
22 public function testFileExistence( $filename, $module, $resource ) {
23 $this->assertFileExists( $filename,
24 "File '$resource' referenced by '$module' must exist."
29 * @dataProvider provideMediaStylesheets
31 public function testStyleMedia( $moduleName, $media, $filename, $css ) {
32 $cssText = CSSMin
::minify( $css->cssText
);
35 strpos( $cssText, '@media' ) === false,
36 'Stylesheets should not both specify "media" and contain @media'
41 * Verify that nothing explicitly depends on the 'jquery' and 'mediawiki' modules.
42 * They are always loaded, depending on them is unsupported and leads to unexpected behaviour.
44 public function testIllegalDependencies() {
45 $data = self
::getAllModules();
46 $illegalDeps = array( 'jquery', 'mediawiki' );
48 /** @var ResourceLoaderModule $module */
49 foreach ( $data['modules'] as $moduleName => $module ) {
50 foreach ( $illegalDeps as $illegalDep ) {
51 $this->assertNotContains(
53 $module->getDependencies(),
54 "Module '$moduleName' must not depend on '$illegalDep'"
61 * Verify that all modules specified as dependencies of other modules actually exist.
63 public function testMissingDependencies() {
64 $data = self
::getAllModules();
65 $validDeps = array_keys( $data['modules'] );
67 /** @var ResourceLoaderModule $module */
68 foreach ( $data['modules'] as $moduleName => $module ) {
69 foreach ( $module->getDependencies() as $dep ) {
70 $this->assertContains(
73 "The module '$dep' required by '$moduleName' must exist"
80 * Verify that all dependencies of all modules are always satisfiable with the 'targets' defined
81 * for the involved modules.
83 * Example: A depends on B. A has targets: mobile, desktop. B has targets: desktop. Therefore the
84 * dependency is sometimes unsatisfiable: it's impossible to load module A on mobile.
86 public function testUnsatisfiableDependencies() {
87 $data = self
::getAllModules();
88 $validDeps = array_keys( $data['modules'] );
90 /** @var ResourceLoaderModule $module */
91 foreach ( $data['modules'] as $moduleName => $module ) {
92 $moduleTargets = $module->getTargets();
93 foreach ( $module->getDependencies() as $dep ) {
94 $targets = $data['modules'][$dep]->getTargets();
95 foreach ( $moduleTargets as $moduleTarget ) {
96 $this->assertContains(
99 "The module '$moduleName' must not have target '$moduleTarget' "
100 . "because its dependency '$dep' does not have it"
108 * Get all registered modules from ResouceLoader.
111 protected static function getAllModules() {
112 global $wgEnableJavaScriptTest;
114 // Test existance of test suite files as well
115 // (can't use setUp or setMwGlobals because providers are static)
116 $org_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
117 $wgEnableJavaScriptTest = true;
119 // Initialize ResourceLoader
120 $rl = new ResourceLoader();
124 foreach ( $rl->getModuleNames() as $moduleName ) {
125 $modules[$moduleName] = $rl->getModule( $moduleName );
129 $wgEnableJavaScriptTest = $org_wgEnableJavaScriptTest;
132 'modules' => $modules,
133 'resourceloader' => $rl,
134 'context' => new ResourceLoaderContext( $rl, new FauxRequest() )
139 * Get all stylesheet files from modules that are an instance of
140 * ResourceLoaderFileModule (or one of its subclasses).
142 public static function provideMediaStylesheets() {
143 $data = self
::getAllModules();
146 foreach ( $data['modules'] as $moduleName => $module ) {
147 if ( !$module instanceof ResourceLoaderFileModule
) {
151 $reflectedModule = new ReflectionObject( $module );
153 $getStyleFiles = $reflectedModule->getMethod( 'getStyleFiles' );
154 $getStyleFiles->setAccessible( true );
156 $readStyleFile = $reflectedModule->getMethod( 'readStyleFile' );
157 $readStyleFile->setAccessible( true );
159 $styleFiles = $getStyleFiles->invoke( $module, $data['context'] );
161 $flip = $module->getFlip( $data['context'] );
163 foreach ( $styleFiles as $media => $files ) {
164 if ( $media && $media !== 'all' ) {
165 foreach ( $files as $file ) {
170 // XXX: Wrapped in an object to keep it out of PHPUnit output
171 (object)array( 'cssText' => $readStyleFile->invoke( $module, $file, $flip ) ),
182 * Get all resource files from modules that are an instance of
183 * ResourceLoaderFileModule (or one of its subclasses).
185 * Since the raw data is stored in protected properties, we have to
186 * overrride this through ReflectionObject methods.
188 public static function provideResourceFiles() {
189 $data = self
::getAllModules();
192 // See also ResourceLoaderFileModule::__construct
193 $filePathProps = array(
194 // Lists of file paths
202 // Collated lists of file paths
203 'nested-lists' => array(
210 foreach ( $data['modules'] as $moduleName => $module ) {
211 if ( !$module instanceof ResourceLoaderFileModule
) {
215 $reflectedModule = new ReflectionObject( $module );
219 foreach ( $filePathProps['lists'] as $propName ) {
220 $property = $reflectedModule->getProperty( $propName );
221 $property->setAccessible( true );
222 $list = $property->getValue( $module );
223 foreach ( $list as $key => $value ) {
224 // 'scripts' are numeral arrays.
225 // 'styles' can be numeral or associative.
226 // In case of associative the key is the file path
227 // and the value is the 'media' attribute.
228 if ( is_int( $key ) ) {
236 foreach ( $filePathProps['nested-lists'] as $propName ) {
237 $property = $reflectedModule->getProperty( $propName );
238 $property->setAccessible( true );
239 $lists = $property->getValue( $module );
240 foreach ( $lists as $list ) {
241 foreach ( $list as $key => $value ) {
242 // We need the same filter as for 'lists',
243 // due to 'skinStyles'.
244 if ( is_int( $key ) ) {
253 // Get method for resolving the paths to full paths
254 $method = $reflectedModule->getMethod( 'getLocalPath' );
255 $method->setAccessible( true );
258 foreach ( $files as $file ) {
260 $method->invoke( $module, $file ),
262 ( $file instanceof ResourceLoaderFilePath ?
$file->getPath() : $file ),