resourceloader: Omit empty parameters from mw.loader.implement calls
[lhc/web/wiklou.git] / tests / phpunit / includes / resourceloader / ResourceLoaderTest.php
1 <?php
2
3 class ResourceLoaderTest extends ResourceLoaderTestCase {
4
5 protected function setUp() {
6 parent::setUp();
7
8 // $wgResourceLoaderLESSFunctions, $wgResourceLoaderLESSImportPaths; $wgResourceLoaderLESSVars;
9
10 $this->setMwGlobals( array(
11 'wgResourceLoaderLESSFunctions' => array(
12 'test-sum' => function ( $frame, $less ) {
13 $sum = 0;
14 foreach ( $frame[2] as $arg ) {
15 $sum += (int)$arg[1];
16 }
17 return $sum;
18 },
19 ),
20 'wgResourceLoaderLESSImportPaths' => array(
21 dirname( dirname( __DIR__ ) ) . '/data/less/common',
22 ),
23 'wgResourceLoaderLESSVars' => array(
24 'foo' => '2px',
25 'Foo' => '#eeeeee',
26 'bar' => 5,
27 ),
28 ) );
29 }
30
31 /* Provider Methods */
32 public static function provideValidModules() {
33 return array(
34 array( 'TEST.validModule1', new ResourceLoaderTestModule() ),
35 );
36 }
37
38 /* Test Methods */
39
40 /**
41 * Ensures that the ResourceLoaderRegisterModules hook is called when a new
42 * ResourceLoader object is constructed.
43 * @covers ResourceLoader::__construct
44 */
45 public function testCreatingNewResourceLoaderCallsRegistrationHook() {
46 $resourceLoaderRegisterModulesHook = false;
47
48 $this->setMwGlobals( 'wgHooks', array(
49 'ResourceLoaderRegisterModules' => array(
50 function ( &$resourceLoader ) use ( &$resourceLoaderRegisterModulesHook ) {
51 $resourceLoaderRegisterModulesHook = true;
52 }
53 )
54 ) );
55
56 $resourceLoader = new ResourceLoader();
57 $this->assertTrue(
58 $resourceLoaderRegisterModulesHook,
59 'Hook ResourceLoaderRegisterModules called'
60 );
61
62 return $resourceLoader;
63 }
64
65 /**
66 * @dataProvider provideValidModules
67 * @depends testCreatingNewResourceLoaderCallsRegistrationHook
68 * @covers ResourceLoader::register
69 * @covers ResourceLoader::getModule
70 */
71 public function testRegisteredValidModulesAreAccessible(
72 $name, ResourceLoaderModule $module, ResourceLoader $resourceLoader
73 ) {
74 $resourceLoader->register( $name, $module );
75 $this->assertEquals( $module, $resourceLoader->getModule( $name ) );
76 }
77
78 /**
79 * @covers ResourceLoaderFileModule::compileLessFile
80 */
81 public function testLessFileCompilation() {
82 $context = $this->getResourceLoaderContext();
83 $basePath = __DIR__ . '/../../data/less/module';
84 $module = new ResourceLoaderFileModule( array(
85 'localBasePath' => $basePath,
86 'styles' => array( 'styles.less' ),
87 ) );
88 $styles = $module->getStyles( $context );
89 $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
90 }
91
92 /**
93 * Strip @noflip annotations from CSS code.
94 * @param string $css
95 * @return string
96 */
97 private function stripNoflip( $css ) {
98 return str_replace( '/*@noflip*/ ', '', $css );
99 }
100
101 /**
102 * What happens when you mix @embed and @noflip?
103 * This really is an integration test, but oh well.
104 */
105 public function testMixedCssAnnotations( ) {
106 $basePath = __DIR__ . '/../../data/css';
107 $testModule = new ResourceLoaderFileModule( array(
108 'localBasePath' => $basePath,
109 'styles' => array( 'test.css' ),
110 ) );
111 $expectedModule = new ResourceLoaderFileModule( array(
112 'localBasePath' => $basePath,
113 'styles' => array( 'expected.css' ),
114 ) );
115
116 $contextLtr = $this->getResourceLoaderContext( 'en', 'ltr' );
117 $contextRtl = $this->getResourceLoaderContext( 'he', 'rtl' );
118
119 // Since we want to compare the effect of @noflip+@embed against the effect of just @embed, and
120 // the @noflip annotations are always preserved, we need to strip them first.
121 $this->assertEquals(
122 $expectedModule->getStyles( $contextLtr ),
123 $this->stripNoflip( $testModule->getStyles( $contextLtr ) ),
124 "/*@noflip*/ with /*@embed*/ gives correct results in LTR mode"
125 );
126 $this->assertEquals(
127 $expectedModule->getStyles( $contextLtr ),
128 $this->stripNoflip( $testModule->getStyles( $contextRtl ) ),
129 "/*@noflip*/ with /*@embed*/ gives correct results in RTL mode"
130 );
131 }
132
133 /**
134 * @dataProvider providePackedModules
135 * @covers ResourceLoader::makePackedModulesString
136 */
137 public function testMakePackedModulesString( $desc, $modules, $packed ) {
138 $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
139 }
140
141 /**
142 * @dataProvider providePackedModules
143 * @covers ResourceLoaderContext::expandModuleNames
144 */
145 public function testexpandModuleNames( $desc, $modules, $packed ) {
146 $this->assertEquals( $modules, ResourceLoaderContext::expandModuleNames( $packed ), $desc );
147 }
148
149 public static function providePackedModules() {
150 return array(
151 array(
152 'Example from makePackedModulesString doc comment',
153 array( 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' ),
154 'foo.bar,baz|bar.baz,quux',
155 ),
156 array(
157 'Example from expandModuleNames doc comment',
158 array( 'jquery.foo', 'jquery.bar', 'jquery.ui.baz', 'jquery.ui.quux' ),
159 'jquery.foo,bar|jquery.ui.baz,quux',
160 ),
161 array(
162 'Regression fixed in r88706 with dotless names',
163 array( 'foo', 'bar', 'baz' ),
164 'foo,bar,baz',
165 ),
166 array(
167 'Prefixless modules after a prefixed module',
168 array( 'single.module', 'foobar', 'foobaz' ),
169 'single.module|foobar,foobaz',
170 ),
171 );
172 }
173
174 public static function provideAddSource() {
175 return array(
176 array( 'examplewiki', '//example.org/w/load.php', 'examplewiki' ),
177 array( 'example2wiki', array( 'loadScript' => '//example.com/w/load.php' ), 'example2wiki' ),
178 array(
179 array( 'foowiki' => '//foo.org/w/load.php', 'bazwiki' => '//baz.org/w/load.php' ),
180 null,
181 array( 'foowiki', 'bazwiki' )
182 ),
183 array(
184 array( 'foowiki' => '//foo.org/w/load.php' ),
185 null,
186 false,
187 ),
188 );
189 }
190
191 /**
192 * @dataProvider provideAddSource
193 * @covers ResourceLoader::addSource
194 */
195 public function testAddSource( $name, $info, $expected ) {
196 $rl = new ResourceLoader;
197 if ( $expected === false ) {
198 $this->setExpectedException( 'MWException', 'ResourceLoader duplicate source addition error' );
199 $rl->addSource( $name, $info );
200 }
201 $rl->addSource( $name, $info );
202 if ( is_array( $expected ) ) {
203 foreach ( $expected as $source ) {
204 $this->assertArrayHasKey( $source, $rl->getSources() );
205 }
206 } else {
207 $this->assertArrayHasKey( $expected, $rl->getSources() );
208 }
209 }
210
211 public static function fakeSources() {
212 return array(
213 'examplewiki' => array(
214 'loadScript' => '//example.org/w/load.php',
215 'apiScript' => '//example.org/w/api.php',
216 ),
217 'example2wiki' => array(
218 'loadScript' => '//example.com/w/load.php',
219 'apiScript' => '//example.com/w/api.php',
220 ),
221 );
222 }
223
224 public static function provideLoaderImplement() {
225 return array(
226 array( array(
227 'title' => 'Implement scripts, styles and messages',
228
229 'name' => 'test.example',
230 'scripts' => 'mw.example();',
231 'styles' => array( 'css' => array( '.mw-example {}' ) ),
232 'messages' => array( 'example' => '' ),
233 'templates' => array(),
234
235 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
236 mw.example();
237 }, {
238 "css": [
239 ".mw-example {}"
240 ]
241 }, {
242 "example": ""
243 } );',
244 ) ),
245 array( array(
246 'title' => 'Implement scripts',
247
248 'name' => 'test.example',
249 'scripts' => 'mw.example();',
250 'styles' => array(),
251 'messages' => new XmlJsCode( '{}' ),
252 'templates' => array(),
253 'title' => 'scripts, styles and messags',
254
255 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
256 mw.example();
257 } );',
258 ) ),
259 array( array(
260 'title' => 'Implement styles',
261
262 'name' => 'test.example',
263 'scripts' => array(),
264 'styles' => array( 'css' => array( '.mw-example {}' ) ),
265 'messages' => new XmlJsCode( '{}' ),
266 'templates' => array(),
267
268 'expected' => 'mw.loader.implement( "test.example", [], {
269 "css": [
270 ".mw-example {}"
271 ]
272 } );',
273 ) ),
274 array( array(
275 'title' => 'Implement scripts and messages',
276
277 'name' => 'test.example',
278 'scripts' => 'mw.example();',
279 'styles' => array(),
280 'messages' => array( 'example' => '' ),
281 'templates' => array(),
282
283 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
284 mw.example();
285 }, {}, {
286 "example": ""
287 } );',
288 ) ),
289 array( array(
290 'title' => 'Implement scripts and templates',
291
292 'name' => 'test.example',
293 'scripts' => 'mw.example();',
294 'styles' => array(),
295 'messages' => new XmlJsCode( '{}' ),
296 'templates' => array( 'example.html' => '' ),
297
298 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
299 mw.example();
300 }, {}, {}, {
301 "example.html": ""
302 } );',
303 ) ),
304 );
305 }
306
307 /**
308 * @dataProvider provideLoaderImplement
309 * @covers ResourceLoader::makeLoaderImplementScript
310 */
311 public function testMakeLoaderImplementScript( $case ) {
312 $this->assertEquals(
313 $case['expected'],
314 ResourceLoader::makeLoaderImplementScript(
315 $case['name'],
316 $case['scripts'],
317 $case['styles'],
318 $case['messages'],
319 $case['templates']
320 )
321 );
322 }
323
324 /**
325 * @covers ResourceLoader::getLoadScript
326 */
327 public function testGetLoadScript() {
328 $this->setMwGlobals( 'wgResourceLoaderSources', array() );
329 $rl = new ResourceLoader();
330 $sources = self::fakeSources();
331 $rl->addSource( $sources );
332 foreach ( array( 'examplewiki', 'example2wiki' ) as $name ) {
333 $this->assertEquals( $rl->getLoadScript( $name ), $sources[$name]['loadScript'] );
334 }
335
336 try {
337 $rl->getLoadScript( 'thiswasneverreigstered' );
338 $this->assertTrue( false, 'ResourceLoader::getLoadScript should have thrown an exception' );
339 } catch ( MWException $e ) {
340 $this->assertTrue( true );
341 }
342 }
343 }