Merge "Add config for serving main Page from the domain root"
[lhc/web/wiklou.git] / tests / phpunit / includes / content / ContentHandlerTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use Wikimedia\TestingAccessWrapper;
5
6 /**
7 * @group ContentHandler
8 * @group Database
9 */
10 class ContentHandlerTest extends MediaWikiTestCase {
11
12 protected function setUp() {
13 parent::setUp();
14
15 $this->setMwGlobals( [
16 'wgExtraNamespaces' => [
17 12312 => 'Dummy',
18 12313 => 'Dummy_talk',
19 ],
20 // The below tests assume that namespaces not mentioned here (Help, User, MediaWiki, ..)
21 // default to CONTENT_MODEL_WIKITEXT.
22 'wgNamespaceContentModels' => [
23 12312 => 'testing',
24 ],
25 'wgContentHandlers' => [
26 CONTENT_MODEL_WIKITEXT => WikitextContentHandler::class,
27 CONTENT_MODEL_JAVASCRIPT => JavaScriptContentHandler::class,
28 CONTENT_MODEL_JSON => JsonContentHandler::class,
29 CONTENT_MODEL_CSS => CssContentHandler::class,
30 CONTENT_MODEL_TEXT => TextContentHandler::class,
31 'testing' => DummyContentHandlerForTesting::class,
32 'testing-callbacks' => function ( $modelId ) {
33 return new DummyContentHandlerForTesting( $modelId );
34 }
35 ],
36 ] );
37
38 // Reset LinkCache
39 MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
40 }
41
42 protected function tearDown() {
43 // Reset LinkCache
44 MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
45
46 parent::tearDown();
47 }
48
49 public function addDBDataOnce() {
50 $this->insertPage( 'Not_Main_Page', 'This is not a main page' );
51 $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]' );
52 }
53
54 public static function dataGetDefaultModelFor() {
55 return [
56 [ 'Help:Foo', CONTENT_MODEL_WIKITEXT ],
57 [ 'Help:Foo.js', CONTENT_MODEL_WIKITEXT ],
58 [ 'Help:Foo.css', CONTENT_MODEL_WIKITEXT ],
59 [ 'Help:Foo.json', CONTENT_MODEL_WIKITEXT ],
60 [ 'Help:Foo/bar.js', CONTENT_MODEL_WIKITEXT ],
61 [ 'User:Foo', CONTENT_MODEL_WIKITEXT ],
62 [ 'User:Foo.js', CONTENT_MODEL_WIKITEXT ],
63 [ 'User:Foo.css', CONTENT_MODEL_WIKITEXT ],
64 [ 'User:Foo.json', CONTENT_MODEL_WIKITEXT ],
65 [ 'User:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT ],
66 [ 'User:Foo/bar.css', CONTENT_MODEL_CSS ],
67 [ 'User:Foo/bar.json', CONTENT_MODEL_JSON ],
68 [ 'User:Foo/bar.json.nope', CONTENT_MODEL_WIKITEXT ],
69 [ 'User talk:Foo/bar.css', CONTENT_MODEL_WIKITEXT ],
70 [ 'User:Foo/bar.js.xxx', CONTENT_MODEL_WIKITEXT ],
71 [ 'User:Foo/bar.xxx', CONTENT_MODEL_WIKITEXT ],
72 [ 'MediaWiki:Foo.js', CONTENT_MODEL_JAVASCRIPT ],
73 [ 'MediaWiki:Foo.JS', CONTENT_MODEL_WIKITEXT ],
74 [ 'MediaWiki:Foo.css', CONTENT_MODEL_CSS ],
75 [ 'MediaWiki:Foo.css.xxx', CONTENT_MODEL_WIKITEXT ],
76 [ 'MediaWiki:Foo.CSS', CONTENT_MODEL_WIKITEXT ],
77 [ 'MediaWiki:Foo.json', CONTENT_MODEL_JSON ],
78 [ 'MediaWiki:Foo.JSON', CONTENT_MODEL_WIKITEXT ],
79 ];
80 }
81
82 /**
83 * @dataProvider dataGetDefaultModelFor
84 * @covers ContentHandler::getDefaultModelFor
85 */
86 public function testGetDefaultModelFor( $title, $expectedModelId ) {
87 $title = Title::newFromText( $title );
88 $this->assertEquals( $expectedModelId, ContentHandler::getDefaultModelFor( $title ) );
89 }
90
91 /**
92 * @dataProvider dataGetDefaultModelFor
93 * @covers ContentHandler::getForTitle
94 */
95 public function testGetForTitle( $title, $expectedContentModel ) {
96 $title = Title::newFromText( $title );
97 MediaWikiServices::getInstance()->getLinkCache()->addBadLinkObj( $title );
98 $handler = ContentHandler::getForTitle( $title );
99 $this->assertEquals( $expectedContentModel, $handler->getModelID() );
100 }
101
102 public static function dataGetLocalizedName() {
103 return [
104 [ null, null ],
105 [ "xyzzy", null ],
106
107 // XXX: depends on content language
108 [ CONTENT_MODEL_JAVASCRIPT, '/javascript/i' ],
109 ];
110 }
111
112 /**
113 * @dataProvider dataGetLocalizedName
114 * @covers ContentHandler::getLocalizedName
115 */
116 public function testGetLocalizedName( $id, $expected ) {
117 $name = ContentHandler::getLocalizedName( $id );
118
119 if ( $expected ) {
120 $this->assertNotNull( $name, "no name found for content model $id" );
121 $this->assertTrue( preg_match( $expected, $name ) > 0,
122 "content model name for #$id did not match pattern $expected"
123 );
124 } else {
125 $this->assertEquals( $id, $name, "localization of unknown model $id should have "
126 . "fallen back to use the model id directly."
127 );
128 }
129 }
130
131 public static function dataGetPageLanguage() {
132 global $wgLanguageCode;
133
134 return [
135 [ "Main", $wgLanguageCode ],
136 [ "Dummy:Foo", $wgLanguageCode ],
137 [ "MediaWiki:common.js", 'en' ],
138 [ "User:Foo/common.js", 'en' ],
139 [ "MediaWiki:common.css", 'en' ],
140 [ "User:Foo/common.css", 'en' ],
141 [ "User:Foo", $wgLanguageCode ],
142
143 [ CONTENT_MODEL_JAVASCRIPT, 'javascript' ],
144 ];
145 }
146
147 /**
148 * @dataProvider dataGetPageLanguage
149 * @covers ContentHandler::getPageLanguage
150 */
151 public function testGetPageLanguage( $title, $expected ) {
152 if ( is_string( $title ) ) {
153 $title = Title::newFromText( $title );
154 MediaWikiServices::getInstance()->getLinkCache()->addBadLinkObj( $title );
155 }
156
157 $expected = wfGetLangObj( $expected );
158
159 $handler = ContentHandler::getForTitle( $title );
160 $lang = $handler->getPageLanguage( $title );
161
162 $this->assertInstanceOf( Language::class, $lang );
163 $this->assertEquals( $expected->getCode(), $lang->getCode() );
164 }
165
166 public static function dataGetContentText_Null() {
167 return [
168 [ 'fail' ],
169 [ 'serialize' ],
170 [ 'ignore' ],
171 ];
172 }
173
174 /**
175 * @dataProvider dataGetContentText_Null
176 * @covers ContentHandler::getContentText
177 */
178 public function testGetContentText_Null( $contentHandlerTextFallback ) {
179 $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
180
181 $content = null;
182
183 $text = ContentHandler::getContentText( $content );
184 $this->assertSame( '', $text );
185 }
186
187 public static function dataGetContentText_TextContent() {
188 return [
189 [ 'fail' ],
190 [ 'serialize' ],
191 [ 'ignore' ],
192 ];
193 }
194
195 /**
196 * @dataProvider dataGetContentText_TextContent
197 * @covers ContentHandler::getContentText
198 */
199 public function testGetContentText_TextContent( $contentHandlerTextFallback ) {
200 $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
201
202 $content = new WikitextContent( "hello world" );
203
204 $text = ContentHandler::getContentText( $content );
205 $this->assertEquals( $content->getText(), $text );
206 }
207
208 /**
209 * ContentHandler::getContentText should have thrown an exception for non-text Content object
210 * @expectedException MWException
211 * @covers ContentHandler::getContentText
212 */
213 public function testGetContentText_NonTextContent_fail() {
214 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'fail' );
215
216 $content = new DummyContentForTesting( "hello world" );
217
218 ContentHandler::getContentText( $content );
219 }
220
221 /**
222 * @covers ContentHandler::getContentText
223 */
224 public function testGetContentText_NonTextContent_serialize() {
225 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'serialize' );
226
227 $content = new DummyContentForTesting( "hello world" );
228
229 $text = ContentHandler::getContentText( $content );
230 $this->assertEquals( $content->serialize(), $text );
231 }
232
233 /**
234 * @covers ContentHandler::getContentText
235 */
236 public function testGetContentText_NonTextContent_ignore() {
237 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'ignore' );
238
239 $content = new DummyContentForTesting( "hello world" );
240
241 $text = ContentHandler::getContentText( $content );
242 $this->assertNull( $text );
243 }
244
245 public static function dataMakeContent() {
246 return [
247 [ 'hallo', 'Help:Test', null, null, CONTENT_MODEL_WIKITEXT, false ],
248 [ 'hallo', 'MediaWiki:Test.js', null, null, CONTENT_MODEL_JAVASCRIPT, false ],
249 [ serialize( 'hallo' ), 'Dummy:Test', null, null, "testing", false ],
250
251 [
252 'hallo',
253 'Help:Test',
254 null,
255 CONTENT_FORMAT_WIKITEXT,
256 CONTENT_MODEL_WIKITEXT,
257 false
258 ],
259 [
260 'hallo',
261 'MediaWiki:Test.js',
262 null,
263 CONTENT_FORMAT_JAVASCRIPT,
264 CONTENT_MODEL_JAVASCRIPT,
265 false
266 ],
267 [ serialize( 'hallo' ), 'Dummy:Test', null, "testing", "testing", false ],
268
269 [ 'hallo', 'Help:Test', CONTENT_MODEL_CSS, null, CONTENT_MODEL_CSS, false ],
270 [
271 'hallo',
272 'MediaWiki:Test.js',
273 CONTENT_MODEL_CSS,
274 null,
275 CONTENT_MODEL_CSS,
276 false
277 ],
278 [
279 serialize( 'hallo' ),
280 'Dummy:Test',
281 CONTENT_MODEL_CSS,
282 null,
283 CONTENT_MODEL_CSS,
284 false
285 ],
286
287 [ 'hallo', 'Help:Test', CONTENT_MODEL_WIKITEXT, "testing", null, true ],
288 [ 'hallo', 'MediaWiki:Test.js', CONTENT_MODEL_CSS, "testing", null, true ],
289 [ 'hallo', 'Dummy:Test', CONTENT_MODEL_JAVASCRIPT, "testing", null, true ],
290 ];
291 }
292
293 /**
294 * @dataProvider dataMakeContent
295 * @covers ContentHandler::makeContent
296 */
297 public function testMakeContent( $data, $title, $modelId, $format,
298 $expectedModelId, $shouldFail
299 ) {
300 $title = Title::newFromText( $title );
301 MediaWikiServices::getInstance()->getLinkCache()->addBadLinkObj( $title );
302 try {
303 $content = ContentHandler::makeContent( $data, $title, $modelId, $format );
304
305 if ( $shouldFail ) {
306 $this->fail( "ContentHandler::makeContent should have failed!" );
307 }
308
309 $this->assertEquals( $expectedModelId, $content->getModel(), 'bad model id' );
310 $this->assertEquals( $data, $content->serialize(), 'bad serialized data' );
311 } catch ( MWException $ex ) {
312 if ( !$shouldFail ) {
313 $this->fail( "ContentHandler::makeContent failed unexpectedly: " . $ex->getMessage() );
314 } else {
315 // dummy, so we don't get the "test did not perform any assertions" message.
316 $this->assertTrue( true );
317 }
318 }
319 }
320
321 /**
322 * @covers ContentHandler::getAutosummary
323 *
324 * Test if we become a "Created blank page" summary from getAutoSummary if no Content added to
325 * page.
326 */
327 public function testGetAutosummary() {
328 $this->setContentLang( 'en' );
329
330 $content = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT );
331 $title = Title::newFromText( 'Help:Test' );
332 // Create a new content object with no content
333 $newContent = ContentHandler::makeContent( '', $title, CONTENT_MODEL_WIKITEXT, null );
334 // first check, if we become a blank page created summary with the right bitmask
335 $autoSummary = $content->getAutosummary( null, $newContent, 97 );
336 $this->assertEquals( $autoSummary,
337 wfMessage( 'autosumm-newblank' )->inContentLanguage()->text() );
338 // now check, what we become with another bitmask
339 $autoSummary = $content->getAutosummary( null, $newContent, 92 );
340 $this->assertEquals( $autoSummary, '' );
341 }
342
343 /**
344 * Test software tag that is added when content model of the page changes
345 * @covers ContentHandler::getChangeTag
346 */
347 public function testGetChangeTag() {
348 $this->setMwGlobals( 'wgSoftwareTags', [ 'mw-contentmodelchange' => true ] );
349 $wikitextContentHandler = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT );
350 // Create old content object with javascript content model
351 $oldContent = ContentHandler::makeContent( '', null, CONTENT_MODEL_JAVASCRIPT, null );
352 // Create new content object with wikitext content model
353 $newContent = ContentHandler::makeContent( '', null, CONTENT_MODEL_WIKITEXT, null );
354 // Get the tag for this edit
355 $tag = $wikitextContentHandler->getChangeTag( $oldContent, $newContent, EDIT_UPDATE );
356 $this->assertSame( $tag, 'mw-contentmodelchange' );
357 }
358
359 /**
360 * @covers ContentHandler::supportsCategories
361 */
362 public function testSupportsCategories() {
363 $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT );
364 $this->assertTrue( $handler->supportsCategories(), 'content model supports categories' );
365 }
366
367 /**
368 * @covers ContentHandler::supportsDirectEditing
369 */
370 public function testSupportsDirectEditing() {
371 $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_JSON );
372 $this->assertFalse( $handler->supportsDirectEditing(), 'direct editing is not supported' );
373 }
374
375 public static function dummyHookHandler( $foo, &$text, $bar ) {
376 if ( $text === null || $text === false ) {
377 return false;
378 }
379
380 $text = strtoupper( $text );
381
382 return true;
383 }
384
385 public function provideGetModelForID() {
386 return [
387 [ CONTENT_MODEL_WIKITEXT, WikitextContentHandler::class ],
388 [ CONTENT_MODEL_JAVASCRIPT, JavaScriptContentHandler::class ],
389 [ CONTENT_MODEL_JSON, JsonContentHandler::class ],
390 [ CONTENT_MODEL_CSS, CssContentHandler::class ],
391 [ CONTENT_MODEL_TEXT, TextContentHandler::class ],
392 [ 'testing', DummyContentHandlerForTesting::class ],
393 [ 'testing-callbacks', DummyContentHandlerForTesting::class ],
394 ];
395 }
396
397 /**
398 * @covers ContentHandler::getForModelID
399 * @dataProvider provideGetModelForID
400 */
401 public function testGetModelForID( $modelId, $handlerClass ) {
402 $handler = ContentHandler::getForModelID( $modelId );
403
404 $this->assertInstanceOf( $handlerClass, $handler );
405 }
406
407 /**
408 * @covers ContentHandler::getFieldsForSearchIndex
409 */
410 public function testGetFieldsForSearchIndex() {
411 $searchEngine = $this->newSearchEngine();
412
413 $handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
414
415 $fields = $handler->getFieldsForSearchIndex( $searchEngine );
416
417 $this->assertArrayHasKey( 'category', $fields );
418 $this->assertArrayHasKey( 'external_link', $fields );
419 $this->assertArrayHasKey( 'outgoing_link', $fields );
420 $this->assertArrayHasKey( 'template', $fields );
421 $this->assertArrayHasKey( 'content_model', $fields );
422 }
423
424 private function newSearchEngine() {
425 $searchEngine = $this->getMockBuilder( SearchEngine::class )
426 ->getMock();
427
428 $searchEngine->expects( $this->any() )
429 ->method( 'makeSearchFieldMapping' )
430 ->will( $this->returnCallback( function ( $name, $type ) {
431 return new DummySearchIndexFieldDefinition( $name, $type );
432 } ) );
433
434 return $searchEngine;
435 }
436
437 /**
438 * @covers ContentHandler::getDataForSearchIndex
439 */
440 public function testDataIndexFields() {
441 $mockEngine = $this->createMock( SearchEngine::class );
442 $title = Title::newFromText( 'Not_Main_Page', NS_MAIN );
443 $page = new WikiPage( $title );
444
445 $this->setTemporaryHook( 'SearchDataForIndex',
446 function (
447 &$fields,
448 ContentHandler $handler,
449 WikiPage $page,
450 ParserOutput $output,
451 SearchEngine $engine
452 ) {
453 $fields['testDataField'] = 'test content';
454 } );
455
456 $output = $page->getContent()->getParserOutput( $title );
457 $data = $page->getContentHandler()->getDataForSearchIndex( $page, $output, $mockEngine );
458 $this->assertArrayHasKey( 'text', $data );
459 $this->assertArrayHasKey( 'text_bytes', $data );
460 $this->assertArrayHasKey( 'language', $data );
461 $this->assertArrayHasKey( 'testDataField', $data );
462 $this->assertEquals( 'test content', $data['testDataField'] );
463 $this->assertEquals( 'wikitext', $data['content_model'] );
464 }
465
466 /**
467 * @covers ContentHandler::getParserOutputForIndexing
468 */
469 public function testParserOutputForIndexing() {
470 $title = Title::newFromText( 'Smithee', NS_MAIN );
471 $page = new WikiPage( $title );
472
473 $out = $page->getContentHandler()->getParserOutputForIndexing( $page );
474 $this->assertInstanceOf( ParserOutput::class, $out );
475 $this->assertContains( 'one who smiths', $out->getRawText() );
476 }
477
478 /**
479 * @covers ContentHandler::getContentModels
480 */
481 public function testGetContentModelsHook() {
482 $this->setTemporaryHook( 'GetContentModels', function ( &$models ) {
483 $models[] = 'Ferrari';
484 } );
485 $this->assertContains( 'Ferrari', ContentHandler::getContentModels() );
486 }
487
488 /**
489 * @covers ContentHandler::getSlotDiffRenderer
490 */
491 public function testGetSlotDiffRenderer_default() {
492 $this->mergeMwGlobalArrayValue( 'wgHooks', [
493 'GetSlotDiffRenderer' => [],
494 ] );
495
496 // test default renderer
497 $contentHandler = new WikitextContentHandler( CONTENT_MODEL_WIKITEXT );
498 $slotDiffRenderer = $contentHandler->getSlotDiffRenderer( RequestContext::getMain() );
499 $this->assertInstanceOf( TextSlotDiffRenderer::class, $slotDiffRenderer );
500 }
501
502 /**
503 * @covers ContentHandler::getSlotDiffRenderer
504 */
505 public function testGetSlotDiffRenderer_bc() {
506 $this->mergeMwGlobalArrayValue( 'wgHooks', [
507 'GetSlotDiffRenderer' => [],
508 ] );
509
510 // test B/C renderer
511 $customDifferenceEngine = $this->getMockBuilder( DifferenceEngine::class )
512 ->disableOriginalConstructor()
513 ->getMock();
514 // hack to track object identity across cloning
515 $customDifferenceEngine->objectId = 12345;
516 $customContentHandler = $this->getMockBuilder( ContentHandler::class )
517 ->setConstructorArgs( [ 'foo', [] ] )
518 ->setMethods( [ 'createDifferenceEngine' ] )
519 ->getMockForAbstractClass();
520 $customContentHandler->expects( $this->any() )
521 ->method( 'createDifferenceEngine' )
522 ->willReturn( $customDifferenceEngine );
523 /** @var ContentHandler $customContentHandler */
524 $slotDiffRenderer = $customContentHandler->getSlotDiffRenderer( RequestContext::getMain() );
525 $this->assertInstanceOf( DifferenceEngineSlotDiffRenderer::class, $slotDiffRenderer );
526 $this->assertSame(
527 $customDifferenceEngine->objectId,
528 TestingAccessWrapper::newFromObject( $slotDiffRenderer )->differenceEngine->objectId
529 );
530 }
531
532 /**
533 * @covers ContentHandler::getSlotDiffRenderer
534 */
535 public function testGetSlotDiffRenderer_nobc() {
536 $this->mergeMwGlobalArrayValue( 'wgHooks', [
537 'GetSlotDiffRenderer' => [],
538 ] );
539
540 // test that B/C renderer does not get used when getSlotDiffRendererInternal is overridden
541 $customDifferenceEngine = $this->getMockBuilder( DifferenceEngine::class )
542 ->disableOriginalConstructor()
543 ->getMock();
544 $customSlotDiffRenderer = $this->getMockBuilder( SlotDiffRenderer::class )
545 ->disableOriginalConstructor()
546 ->getMockForAbstractClass();
547 $customContentHandler2 = $this->getMockBuilder( ContentHandler::class )
548 ->setConstructorArgs( [ 'bar', [] ] )
549 ->setMethods( [ 'createDifferenceEngine', 'getSlotDiffRendererInternal' ] )
550 ->getMockForAbstractClass();
551 $customContentHandler2->expects( $this->any() )
552 ->method( 'createDifferenceEngine' )
553 ->willReturn( $customDifferenceEngine );
554 $customContentHandler2->expects( $this->any() )
555 ->method( 'getSlotDiffRendererInternal' )
556 ->willReturn( $customSlotDiffRenderer );
557 /** @var ContentHandler $customContentHandler2 */
558 $slotDiffRenderer = $customContentHandler2->getSlotDiffRenderer( RequestContext::getMain() );
559 $this->assertSame( $customSlotDiffRenderer, $slotDiffRenderer );
560 }
561
562 /**
563 * @covers ContentHandler::getSlotDiffRenderer
564 */
565 public function testGetSlotDiffRenderer_hook() {
566 $this->mergeMwGlobalArrayValue( 'wgHooks', [
567 'GetSlotDiffRenderer' => [],
568 ] );
569
570 // test that the hook handler takes precedence
571 $customDifferenceEngine = $this->getMockBuilder( DifferenceEngine::class )
572 ->disableOriginalConstructor()
573 ->getMock();
574 $customContentHandler = $this->getMockBuilder( ContentHandler::class )
575 ->setConstructorArgs( [ 'foo', [] ] )
576 ->setMethods( [ 'createDifferenceEngine' ] )
577 ->getMockForAbstractClass();
578 $customContentHandler->expects( $this->any() )
579 ->method( 'createDifferenceEngine' )
580 ->willReturn( $customDifferenceEngine );
581 /** @var ContentHandler $customContentHandler */
582
583 $customSlotDiffRenderer = $this->getMockBuilder( SlotDiffRenderer::class )
584 ->disableOriginalConstructor()
585 ->getMockForAbstractClass();
586 $customContentHandler2 = $this->getMockBuilder( ContentHandler::class )
587 ->setConstructorArgs( [ 'bar', [] ] )
588 ->setMethods( [ 'createDifferenceEngine', 'getSlotDiffRendererInternal' ] )
589 ->getMockForAbstractClass();
590 $customContentHandler2->expects( $this->any() )
591 ->method( 'createDifferenceEngine' )
592 ->willReturn( $customDifferenceEngine );
593 $customContentHandler2->expects( $this->any() )
594 ->method( 'getSlotDiffRendererInternal' )
595 ->willReturn( $customSlotDiffRenderer );
596 /** @var ContentHandler $customContentHandler2 */
597
598 $customSlotDiffRenderer2 = $this->getMockBuilder( SlotDiffRenderer::class )
599 ->disableOriginalConstructor()
600 ->getMockForAbstractClass();
601 $this->setTemporaryHook( 'GetSlotDiffRenderer',
602 function ( $handler, &$slotDiffRenderer ) use ( $customSlotDiffRenderer2 ) {
603 $slotDiffRenderer = $customSlotDiffRenderer2;
604 } );
605
606 $slotDiffRenderer = $customContentHandler->getSlotDiffRenderer( RequestContext::getMain() );
607 $this->assertSame( $customSlotDiffRenderer2, $slotDiffRenderer );
608 $slotDiffRenderer = $customContentHandler2->getSlotDiffRenderer( RequestContext::getMain() );
609 $this->assertSame( $customSlotDiffRenderer2, $slotDiffRenderer );
610 }
611
612 }