Merge "Better detection for old MS Office files"
[lhc/web/wiklou.git] / tests / phpunit / includes / resourceloader / ResourceLoaderStartUpModuleTest.php
1 <?php
2
3 class ResourceLoaderStartUpModuleTest extends ResourceLoaderTestCase {
4
5 protected static function expandPlaceholders( $text ) {
6 return strtr( $text, [
7 '{blankVer}' => self::BLANK_VERSION
8 ] );
9 }
10
11 public function provideGetModuleRegistrations() {
12 return [
13 [ [
14 'msg' => 'Empty registry',
15 'modules' => [],
16 'out' => '
17 mw.loader.addSource( {
18 "local": "/w/load.php"
19 } );
20 mw.loader.register( [] );'
21 ] ],
22 [ [
23 'msg' => 'Basic registry',
24 'modules' => [
25 'test.blank' => new ResourceLoaderTestModule(),
26 ],
27 'out' => '
28 mw.loader.addSource( {
29 "local": "/w/load.php"
30 } );
31 mw.loader.register( [
32 [
33 "test.blank",
34 "{blankVer}"
35 ]
36 ] );',
37 ] ],
38 [ [
39 'msg' => 'Omit raw modules from registry',
40 'modules' => [
41 'test.raw' => new ResourceLoaderTestModule( [ 'isRaw' => true ] ),
42 'test.blank' => new ResourceLoaderTestModule(),
43 ],
44 'out' => '
45 mw.loader.addSource( {
46 "local": "/w/load.php"
47 } );
48 mw.loader.register( [
49 [
50 "test.blank",
51 "{blankVer}"
52 ]
53 ] );',
54 ] ],
55 [ [
56 'msg' => 'Version falls back gracefully if getVersionHash throws',
57 'modules' => [
58 'test.fail' => (
59 ( $mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
60 ->setMethods( [ 'getVersionHash' ] )->getMock() )
61 && $mock->method( 'getVersionHash' )->will(
62 $this->throwException( new Exception )
63 )
64 ) ? $mock : $mock
65 ],
66 'out' => '
67 mw.loader.addSource( {
68 "local": "/w/load.php"
69 } );
70 mw.loader.register( [
71 [
72 "test.fail",
73 ""
74 ]
75 ] );
76 mw.loader.state( {
77 "test.fail": "error"
78 } );',
79 ] ],
80 [ [
81 'msg' => 'Use version from getVersionHash',
82 'modules' => [
83 'test.version' => (
84 ( $mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
85 ->setMethods( [ 'getVersionHash' ] )->getMock() )
86 && $mock->method( 'getVersionHash' )->willReturn( '1234567' )
87 ) ? $mock : $mock
88 ],
89 'out' => '
90 mw.loader.addSource( {
91 "local": "/w/load.php"
92 } );
93 mw.loader.register( [
94 [
95 "test.version",
96 "1234567"
97 ]
98 ] );',
99 ] ],
100 [ [
101 'msg' => 'Re-hash version from getVersionHash if too long',
102 'modules' => [
103 'test.version' => (
104 ( $mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
105 ->setMethods( [ 'getVersionHash' ] )->getMock() )
106 && $mock->method( 'getVersionHash' )->willReturn( '12345678' )
107 ) ? $mock : $mock
108 ],
109 'out' => '
110 mw.loader.addSource( {
111 "local": "/w/load.php"
112 } );
113 mw.loader.register( [
114 [
115 "test.version",
116 "016es8l"
117 ]
118 ] );',
119 ] ],
120 [ [
121 'msg' => 'Group signature',
122 'modules' => [
123 'test.blank' => new ResourceLoaderTestModule(),
124 'test.group.foo' => new ResourceLoaderTestModule( [ 'group' => 'x-foo' ] ),
125 'test.group.bar' => new ResourceLoaderTestModule( [ 'group' => 'x-bar' ] ),
126 ],
127 'out' => '
128 mw.loader.addSource( {
129 "local": "/w/load.php"
130 } );
131 mw.loader.register( [
132 [
133 "test.blank",
134 "{blankVer}"
135 ],
136 [
137 "test.group.foo",
138 "{blankVer}",
139 [],
140 "x-foo"
141 ],
142 [
143 "test.group.bar",
144 "{blankVer}",
145 [],
146 "x-bar"
147 ]
148 ] );'
149 ] ],
150 [ [
151 'msg' => 'Different target (non-test should not be registered)',
152 'modules' => [
153 'test.blank' => new ResourceLoaderTestModule(),
154 'test.target.foo' => new ResourceLoaderTestModule( [ 'targets' => [ 'x-foo' ] ] ),
155 ],
156 'out' => '
157 mw.loader.addSource( {
158 "local": "/w/load.php"
159 } );
160 mw.loader.register( [
161 [
162 "test.blank",
163 "{blankVer}"
164 ]
165 ] );'
166 ] ],
167 [ [
168 'msg' => 'Safemode disabled (default; register all modules)',
169 'modules' => [
170 // Default origin: ORIGIN_CORE_SITEWIDE
171 'test.blank' => new ResourceLoaderTestModule(),
172 'test.core-generated' => new ResourceLoaderTestModule( [
173 'origin' => ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
174 ] ),
175 'test.sitewide' => new ResourceLoaderTestModule( [
176 'origin' => ResourceLoaderModule::ORIGIN_USER_SITEWIDE
177 ] ),
178 'test.user' => new ResourceLoaderTestModule( [
179 'origin' => ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL
180 ] ),
181 ],
182 'out' => '
183 mw.loader.addSource( {
184 "local": "/w/load.php"
185 } );
186 mw.loader.register( [
187 [
188 "test.blank",
189 "{blankVer}"
190 ],
191 [
192 "test.core-generated",
193 "{blankVer}"
194 ],
195 [
196 "test.sitewide",
197 "{blankVer}"
198 ],
199 [
200 "test.user",
201 "{blankVer}"
202 ]
203 ] );'
204 ] ],
205 [ [
206 'msg' => 'Safemode enabled (filter modules with user/site origin)',
207 'extraQuery' => [ 'safemode' => '1' ],
208 'modules' => [
209 // Default origin: ORIGIN_CORE_SITEWIDE
210 'test.blank' => new ResourceLoaderTestModule(),
211 'test.core-generated' => new ResourceLoaderTestModule( [
212 'origin' => ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
213 ] ),
214 'test.sitewide' => new ResourceLoaderTestModule( [
215 'origin' => ResourceLoaderModule::ORIGIN_USER_SITEWIDE
216 ] ),
217 'test.user' => new ResourceLoaderTestModule( [
218 'origin' => ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL
219 ] ),
220 ],
221 'out' => '
222 mw.loader.addSource( {
223 "local": "/w/load.php"
224 } );
225 mw.loader.register( [
226 [
227 "test.blank",
228 "{blankVer}"
229 ],
230 [
231 "test.core-generated",
232 "{blankVer}"
233 ]
234 ] );'
235 ] ],
236 [ [
237 'msg' => 'Foreign source',
238 'sources' => [
239 'example' => [
240 'loadScript' => 'http://example.org/w/load.php',
241 'apiScript' => 'http://example.org/w/api.php',
242 ],
243 ],
244 'modules' => [
245 'test.blank' => new ResourceLoaderTestModule( [ 'source' => 'example' ] ),
246 ],
247 'out' => '
248 mw.loader.addSource( {
249 "local": "/w/load.php",
250 "example": "http://example.org/w/load.php"
251 } );
252 mw.loader.register( [
253 [
254 "test.blank",
255 "{blankVer}",
256 [],
257 null,
258 "example"
259 ]
260 ] );'
261 ] ],
262 [ [
263 'msg' => 'Conditional dependency function',
264 'modules' => [
265 'test.x.core' => new ResourceLoaderTestModule(),
266 'test.x.polyfill' => new ResourceLoaderTestModule( [
267 'skipFunction' => 'return true;'
268 ] ),
269 'test.y.polyfill' => new ResourceLoaderTestModule( [
270 'skipFunction' =>
271 'return !!(' .
272 ' window.JSON &&' .
273 ' JSON.parse &&' .
274 ' JSON.stringify' .
275 ');'
276 ] ),
277 'test.z.foo' => new ResourceLoaderTestModule( [
278 'dependencies' => [
279 'test.x.core',
280 'test.x.polyfill',
281 'test.y.polyfill',
282 ],
283 ] ),
284 ],
285 'out' => '
286 mw.loader.addSource( {
287 "local": "/w/load.php"
288 } );
289 mw.loader.register( [
290 [
291 "test.x.core",
292 "{blankVer}"
293 ],
294 [
295 "test.x.polyfill",
296 "{blankVer}",
297 [],
298 null,
299 null,
300 "return true;"
301 ],
302 [
303 "test.y.polyfill",
304 "{blankVer}",
305 [],
306 null,
307 null,
308 "return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);"
309 ],
310 [
311 "test.z.foo",
312 "{blankVer}",
313 [
314 0,
315 1,
316 2
317 ]
318 ]
319 ] );',
320 ] ],
321 [ [
322 // This may seem like an edge case, but a plain MediaWiki core install
323 // with a few extensions installed is likely far more complex than this
324 // even, not to mention an install like Wikipedia.
325 // TODO: Make this even more realistic.
326 'msg' => 'Advanced (everything combined)',
327 'sources' => [
328 'example' => [
329 'loadScript' => 'http://example.org/w/load.php',
330 'apiScript' => 'http://example.org/w/api.php',
331 ],
332 ],
333 'modules' => [
334 'test.blank' => new ResourceLoaderTestModule(),
335 'test.x.core' => new ResourceLoaderTestModule(),
336 'test.x.util' => new ResourceLoaderTestModule( [
337 'dependencies' => [
338 'test.x.core',
339 ],
340 ] ),
341 'test.x.foo' => new ResourceLoaderTestModule( [
342 'dependencies' => [
343 'test.x.core',
344 ],
345 ] ),
346 'test.x.bar' => new ResourceLoaderTestModule( [
347 'dependencies' => [
348 'test.x.core',
349 'test.x.util',
350 ],
351 ] ),
352 'test.x.quux' => new ResourceLoaderTestModule( [
353 'dependencies' => [
354 'test.x.foo',
355 'test.x.bar',
356 'test.x.util',
357 'test.x.unknown',
358 ],
359 ] ),
360 'test.group.foo.1' => new ResourceLoaderTestModule( [
361 'group' => 'x-foo',
362 ] ),
363 'test.group.foo.2' => new ResourceLoaderTestModule( [
364 'group' => 'x-foo',
365 ] ),
366 'test.group.bar.1' => new ResourceLoaderTestModule( [
367 'group' => 'x-bar',
368 ] ),
369 'test.group.bar.2' => new ResourceLoaderTestModule( [
370 'group' => 'x-bar',
371 'source' => 'example',
372 ] ),
373 'test.target.foo' => new ResourceLoaderTestModule( [
374 'targets' => [ 'x-foo' ],
375 ] ),
376 'test.target.bar' => new ResourceLoaderTestModule( [
377 'source' => 'example',
378 'targets' => [ 'x-foo' ],
379 ] ),
380 ],
381 'out' => '
382 mw.loader.addSource( {
383 "local": "/w/load.php",
384 "example": "http://example.org/w/load.php"
385 } );
386 mw.loader.register( [
387 [
388 "test.blank",
389 "{blankVer}"
390 ],
391 [
392 "test.x.core",
393 "{blankVer}"
394 ],
395 [
396 "test.x.util",
397 "{blankVer}",
398 [
399 1
400 ]
401 ],
402 [
403 "test.x.foo",
404 "{blankVer}",
405 [
406 1
407 ]
408 ],
409 [
410 "test.x.bar",
411 "{blankVer}",
412 [
413 2
414 ]
415 ],
416 [
417 "test.x.quux",
418 "{blankVer}",
419 [
420 3,
421 4,
422 "test.x.unknown"
423 ]
424 ],
425 [
426 "test.group.foo.1",
427 "{blankVer}",
428 [],
429 "x-foo"
430 ],
431 [
432 "test.group.foo.2",
433 "{blankVer}",
434 [],
435 "x-foo"
436 ],
437 [
438 "test.group.bar.1",
439 "{blankVer}",
440 [],
441 "x-bar"
442 ],
443 [
444 "test.group.bar.2",
445 "{blankVer}",
446 [],
447 "x-bar",
448 "example"
449 ]
450 ] );'
451 ] ],
452 ];
453 }
454
455 /**
456 * @dataProvider provideGetModuleRegistrations
457 * @covers ResourceLoaderStartUpModule::getModuleRegistrations
458 * @covers ResourceLoaderStartUpModule::compileUnresolvedDependencies
459 * @covers ResourceLoader::makeLoaderRegisterScript
460 */
461 public function testGetModuleRegistrations( $case ) {
462 if ( isset( $case['sources'] ) ) {
463 $this->setMwGlobals( 'wgResourceLoaderSources', $case['sources'] );
464 }
465
466 $extraQuery = $case['extraQuery'] ?? [];
467 $context = $this->getResourceLoaderContext( $extraQuery );
468 $rl = $context->getResourceLoader();
469 $rl->register( $case['modules'] );
470 $module = new ResourceLoaderStartUpModule();
471 $out = ltrim( $case['out'], "\n" );
472
473 // Disable log from getModuleRegistrations via MWExceptionHandler
474 // for case where getVersionHash() is expected to throw.
475 $this->setLogger( 'exception', new Psr\Log\NullLogger() );
476
477 $this->assertEquals(
478 self::expandPlaceholders( $out ),
479 $module->getModuleRegistrations( $context ),
480 $case['msg']
481 );
482 }
483
484 public static function provideRegistrations() {
485 return [
486 [ [
487 'test.blank' => new ResourceLoaderTestModule(),
488 'test.min' => new ResourceLoaderTestModule( [
489 'skipFunction' =>
490 'return !!(' .
491 ' window.JSON &&' .
492 ' JSON.parse &&' .
493 ' JSON.stringify' .
494 ');',
495 'dependencies' => [
496 'test.blank',
497 ],
498 ] ),
499 ] ]
500 ];
501 }
502
503 /**
504 * @covers ResourceLoaderStartUpModule::getModuleRegistrations
505 * @dataProvider provideRegistrations
506 */
507 public function testRegistrationsMinified( $modules ) {
508 $this->setMwGlobals( 'wgResourceLoaderDebug', false );
509
510 $context = $this->getResourceLoaderContext();
511 $rl = $context->getResourceLoader();
512 $rl->register( $modules );
513 $module = new ResourceLoaderStartUpModule();
514 $out = 'mw.loader.addSource({"local":"/w/load.php"});' . "\n"
515 . 'mw.loader.register(['
516 . '["test.blank","{blankVer}"],'
517 . '["test.min","{blankVer}",[0],null,null,'
518 . '"return!!(window.JSON\u0026\u0026JSON.parse\u0026\u0026JSON.stringify);"'
519 . ']]);';
520
521 $this->assertEquals(
522 self::expandPlaceholders( $out ),
523 $module->getModuleRegistrations( $context ),
524 'Minified output'
525 );
526 }
527
528 /**
529 * @covers ResourceLoaderStartUpModule::getModuleRegistrations
530 * @dataProvider provideRegistrations
531 */
532 public function testRegistrationsUnminified( $modules ) {
533 $context = $this->getResourceLoaderContext();
534 $rl = $context->getResourceLoader();
535 $rl->register( $modules );
536 $module = new ResourceLoaderStartUpModule();
537 $out =
538 'mw.loader.addSource( {
539 "local": "/w/load.php"
540 } );
541 mw.loader.register( [
542 [
543 "test.blank",
544 "{blankVer}"
545 ],
546 [
547 "test.min",
548 "{blankVer}",
549 [
550 0
551 ],
552 null,
553 null,
554 "return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);"
555 ]
556 ] );';
557
558 $this->assertEquals(
559 self::expandPlaceholders( $out ),
560 $module->getModuleRegistrations( $context ),
561 'Unminified output'
562 );
563 }
564
565 /**
566 * @covers ResourceLoaderStartupModule::getDefinitionSummary
567 */
568 public function testGetVersionHash_varyConfig() {
569 $context = $this->getResourceLoaderContext();
570
571 $this->setMwGlobals( 'wgArticlePath', '/w1' );
572 $module = new ResourceLoaderStartupModule();
573 $version1 = $module->getVersionHash( $context );
574 $module = new ResourceLoaderStartupModule();
575 $version2 = $module->getVersionHash( $context );
576
577 $this->setMwGlobals( 'wgArticlePath', '/w3' );
578 $module = new ResourceLoaderStartupModule();
579 $version3 = $module->getVersionHash( $context );
580
581 $this->assertEquals(
582 $version1,
583 $version2,
584 'Deterministic version hash'
585 );
586
587 $this->assertNotEquals(
588 $version1,
589 $version3,
590 'Config change impacts version hash'
591 );
592 }
593
594 /**
595 * @covers ResourceLoaderStartupModule
596 */
597 public function testGetVersionHash_varyModule() {
598 $context1 = $this->getResourceLoaderContext();
599 $rl1 = $context1->getResourceLoader();
600 $rl1->register( [
601 'test.a' => new ResourceLoaderTestModule(),
602 'test.b' => new ResourceLoaderTestModule(),
603 ] );
604 $module = new ResourceLoaderStartupModule();
605 $version1 = $module->getVersionHash( $context1 );
606
607 $context2 = $this->getResourceLoaderContext();
608 $rl2 = $context2->getResourceLoader();
609 $rl2->register( [
610 'test.b' => new ResourceLoaderTestModule(),
611 'test.c' => new ResourceLoaderTestModule(),
612 ] );
613 $module = new ResourceLoaderStartupModule();
614 $version2 = $module->getVersionHash( $context2 );
615
616 $context3 = $this->getResourceLoaderContext();
617 $rl3 = $context3->getResourceLoader();
618 $rl3->register( [
619 'test.a' => new ResourceLoaderTestModule(),
620 'test.b' => new ResourceLoaderTestModule( [ 'script' => 'different' ] ),
621 ] );
622 $module = new ResourceLoaderStartupModule();
623 $version3 = $module->getVersionHash( $context3 );
624
625 // Module name *is* significant (T201686)
626 $this->assertNotEquals(
627 $version1,
628 $version2,
629 'Module name is significant'
630 );
631
632 $this->assertNotEquals(
633 $version1,
634 $version3,
635 'Hash change of any module impacts startup hash'
636 );
637 }
638
639 /**
640 * @covers ResourceLoaderStartupModule
641 */
642 public function testGetVersionHash_varyDeps() {
643 $context = $this->getResourceLoaderContext();
644 $rl = $context->getResourceLoader();
645 $rl->register( [
646 'test.a' => new ResourceLoaderTestModule( [ 'dependencies' => [ 'x', 'y' ] ] ),
647 ] );
648 $module = new ResourceLoaderStartupModule();
649 $version1 = $module->getVersionHash( $context );
650
651 $context = $this->getResourceLoaderContext();
652 $rl = $context->getResourceLoader();
653 $rl->register( [
654 'test.a' => new ResourceLoaderTestModule( [ 'dependencies' => [ 'x', 'z' ] ] ),
655 ] );
656 $module = new ResourceLoaderStartupModule();
657 $version2 = $module->getVersionHash( $context );
658
659 // Dependencies *are* significant (T201686)
660 $this->assertNotEquals(
661 $version1,
662 $version2,
663 'Dependencies are significant'
664 );
665 }
666
667 }