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