2 use MediaWiki\MediaWikiServices
;
5 * @group ContentHandler
8 class ContentHandlerTest
extends MediaWikiTestCase
{
10 protected function setUp() {
14 $this->setMwGlobals( [
15 'wgExtraNamespaces' => [
17 12313 => 'Dummy_talk',
19 // The below tests assume that namespaces not mentioned here (Help, User, MediaWiki, ..)
20 // default to CONTENT_MODEL_WIKITEXT.
21 'wgNamespaceContentModels' => [
24 'wgContentHandlers' => [
25 CONTENT_MODEL_WIKITEXT
=> 'WikitextContentHandler',
26 CONTENT_MODEL_JAVASCRIPT
=> 'JavaScriptContentHandler',
27 CONTENT_MODEL_JSON
=> 'JsonContentHandler',
28 CONTENT_MODEL_CSS
=> 'CssContentHandler',
29 CONTENT_MODEL_TEXT
=> 'TextContentHandler',
30 'testing' => 'DummyContentHandlerForTesting',
31 'testing-callbacks' => function( $modelId ) {
32 return new DummyContentHandlerForTesting( $modelId );
37 // Reset namespace cache
38 MWNamespace
::getCanonicalNamespaces( true );
39 $wgContLang->resetNamespaces();
41 MediaWikiServices
::getInstance()->resetServiceForTesting( 'LinkCache' );
44 protected function tearDown() {
47 // Reset namespace cache
48 MWNamespace
::getCanonicalNamespaces( true );
49 $wgContLang->resetNamespaces();
51 MediaWikiServices
::getInstance()->resetServiceForTesting( 'LinkCache' );
56 public function addDBDataOnce() {
57 $this->insertPage( 'Not_Main_Page', 'This is not a main page' );
58 $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]' );
61 public static function dataGetDefaultModelFor() {
63 [ 'Help:Foo', CONTENT_MODEL_WIKITEXT
],
64 [ 'Help:Foo.js', CONTENT_MODEL_WIKITEXT
],
65 [ 'Help:Foo.css', CONTENT_MODEL_WIKITEXT
],
66 [ 'Help:Foo.json', CONTENT_MODEL_WIKITEXT
],
67 [ 'Help:Foo/bar.js', CONTENT_MODEL_WIKITEXT
],
68 [ 'User:Foo', CONTENT_MODEL_WIKITEXT
],
69 [ 'User:Foo.js', CONTENT_MODEL_WIKITEXT
],
70 [ 'User:Foo.css', CONTENT_MODEL_WIKITEXT
],
71 [ 'User:Foo.json', CONTENT_MODEL_WIKITEXT
],
72 [ 'User:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT
],
73 [ 'User:Foo/bar.css', CONTENT_MODEL_CSS
],
74 [ 'User:Foo/bar.json', CONTENT_MODEL_JSON
],
75 [ 'User:Foo/bar.json.nope', CONTENT_MODEL_WIKITEXT
],
76 [ 'User talk:Foo/bar.css', CONTENT_MODEL_WIKITEXT
],
77 [ 'User:Foo/bar.js.xxx', CONTENT_MODEL_WIKITEXT
],
78 [ 'User:Foo/bar.xxx', CONTENT_MODEL_WIKITEXT
],
79 [ 'MediaWiki:Foo.js', CONTENT_MODEL_JAVASCRIPT
],
80 [ 'MediaWiki:Foo.JS', CONTENT_MODEL_WIKITEXT
],
81 [ 'MediaWiki:Foo.css', CONTENT_MODEL_CSS
],
82 [ 'MediaWiki:Foo.css.xxx', CONTENT_MODEL_WIKITEXT
],
83 [ 'MediaWiki:Foo.CSS', CONTENT_MODEL_WIKITEXT
],
84 [ 'MediaWiki:Foo.json', CONTENT_MODEL_JSON
],
85 [ 'MediaWiki:Foo.JSON', CONTENT_MODEL_WIKITEXT
],
90 * @dataProvider dataGetDefaultModelFor
91 * @covers ContentHandler::getDefaultModelFor
93 public function testGetDefaultModelFor( $title, $expectedModelId ) {
94 $title = Title
::newFromText( $title );
95 $this->assertEquals( $expectedModelId, ContentHandler
::getDefaultModelFor( $title ) );
99 * @dataProvider dataGetDefaultModelFor
100 * @covers ContentHandler::getForTitle
102 public function testGetForTitle( $title, $expectedContentModel ) {
103 $title = Title
::newFromText( $title );
104 LinkCache
::singleton()->addBadLinkObj( $title );
105 $handler = ContentHandler
::getForTitle( $title );
106 $this->assertEquals( $expectedContentModel, $handler->getModelID() );
109 public static function dataGetLocalizedName() {
114 // XXX: depends on content language
115 [ CONTENT_MODEL_JAVASCRIPT
, '/javascript/i' ],
120 * @dataProvider dataGetLocalizedName
121 * @covers ContentHandler::getLocalizedName
123 public function testGetLocalizedName( $id, $expected ) {
124 $name = ContentHandler
::getLocalizedName( $id );
127 $this->assertNotNull( $name, "no name found for content model $id" );
128 $this->assertTrue( preg_match( $expected, $name ) > 0,
129 "content model name for #$id did not match pattern $expected"
132 $this->assertEquals( $id, $name, "localization of unknown model $id should have "
133 . "fallen back to use the model id directly."
138 public static function dataGetPageLanguage() {
139 global $wgLanguageCode;
142 [ "Main", $wgLanguageCode ],
143 [ "Dummy:Foo", $wgLanguageCode ],
144 [ "MediaWiki:common.js", 'en' ],
145 [ "User:Foo/common.js", 'en' ],
146 [ "MediaWiki:common.css", 'en' ],
147 [ "User:Foo/common.css", 'en' ],
148 [ "User:Foo", $wgLanguageCode ],
150 [ CONTENT_MODEL_JAVASCRIPT
, 'javascript' ],
155 * @dataProvider dataGetPageLanguage
156 * @covers ContentHandler::getPageLanguage
158 public function testGetPageLanguage( $title, $expected ) {
159 if ( is_string( $title ) ) {
160 $title = Title
::newFromText( $title );
161 LinkCache
::singleton()->addBadLinkObj( $title );
164 $expected = wfGetLangObj( $expected );
166 $handler = ContentHandler
::getForTitle( $title );
167 $lang = $handler->getPageLanguage( $title );
169 $this->assertEquals( $expected->getCode(), $lang->getCode() );
172 public static function dataGetContentText_Null() {
181 * @dataProvider dataGetContentText_Null
182 * @covers ContentHandler::getContentText
184 public function testGetContentText_Null( $contentHandlerTextFallback ) {
185 $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
189 $text = ContentHandler
::getContentText( $content );
190 $this->assertEquals( '', $text );
193 public static function dataGetContentText_TextContent() {
202 * @dataProvider dataGetContentText_TextContent
203 * @covers ContentHandler::getContentText
205 public function testGetContentText_TextContent( $contentHandlerTextFallback ) {
206 $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
208 $content = new WikitextContent( "hello world" );
210 $text = ContentHandler
::getContentText( $content );
211 $this->assertEquals( $content->getNativeData(), $text );
215 * ContentHandler::getContentText should have thrown an exception for non-text Content object
216 * @expectedException MWException
217 * @covers ContentHandler::getContentText
219 public function testGetContentText_NonTextContent_fail() {
220 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'fail' );
222 $content = new DummyContentForTesting( "hello world" );
224 ContentHandler
::getContentText( $content );
228 * @covers ContentHandler::getContentText
230 public function testGetContentText_NonTextContent_serialize() {
231 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'serialize' );
233 $content = new DummyContentForTesting( "hello world" );
235 $text = ContentHandler
::getContentText( $content );
236 $this->assertEquals( $content->serialize(), $text );
240 * @covers ContentHandler::getContentText
242 public function testGetContentText_NonTextContent_ignore() {
243 $this->setMwGlobals( 'wgContentHandlerTextFallback', 'ignore' );
245 $content = new DummyContentForTesting( "hello world" );
247 $text = ContentHandler
::getContentText( $content );
248 $this->assertNull( $text );
252 public static function makeContent( $text, Title $title, $modelId = null, $format = null ) {}
255 public static function dataMakeContent() {
257 [ 'hallo', 'Help:Test', null, null, CONTENT_MODEL_WIKITEXT
, 'hallo', false ],
258 [ 'hallo', 'MediaWiki:Test.js', null, null, CONTENT_MODEL_JAVASCRIPT
, 'hallo', false ],
259 [ serialize( 'hallo' ), 'Dummy:Test', null, null, "testing", 'hallo', false ],
265 CONTENT_FORMAT_WIKITEXT
,
266 CONTENT_MODEL_WIKITEXT
,
274 CONTENT_FORMAT_JAVASCRIPT
,
275 CONTENT_MODEL_JAVASCRIPT
,
279 [ serialize( 'hallo' ), 'Dummy:Test', null, "testing", "testing", 'hallo', false ],
281 [ 'hallo', 'Help:Test', CONTENT_MODEL_CSS
, null, CONTENT_MODEL_CSS
, 'hallo', false ],
292 serialize( 'hallo' ),
297 serialize( 'hallo' ),
301 [ 'hallo', 'Help:Test', CONTENT_MODEL_WIKITEXT
, "testing", null, null, true ],
302 [ 'hallo', 'MediaWiki:Test.js', CONTENT_MODEL_CSS
, "testing", null, null, true ],
303 [ 'hallo', 'Dummy:Test', CONTENT_MODEL_JAVASCRIPT
, "testing", null, null, true ],
308 * @dataProvider dataMakeContent
309 * @covers ContentHandler::makeContent
311 public function testMakeContent( $data, $title, $modelId, $format,
312 $expectedModelId, $expectedNativeData, $shouldFail
314 $title = Title
::newFromText( $title );
315 LinkCache
::singleton()->addBadLinkObj( $title );
317 $content = ContentHandler
::makeContent( $data, $title, $modelId, $format );
320 $this->fail( "ContentHandler::makeContent should have failed!" );
323 $this->assertEquals( $expectedModelId, $content->getModel(), 'bad model id' );
324 $this->assertEquals( $expectedNativeData, $content->getNativeData(), 'bads native data' );
325 } catch ( MWException
$ex ) {
326 if ( !$shouldFail ) {
327 $this->fail( "ContentHandler::makeContent failed unexpectedly: " . $ex->getMessage() );
329 // dummy, so we don't get the "test did not perform any assertions" message.
330 $this->assertTrue( true );
336 * Test if we become a "Created blank page" summary from getAutoSummary if no Content added to
339 public function testGetAutosummary() {
340 $this->setMwGlobals( 'wgContLang', Language
::factory( 'en' ) );
342 $content = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT
);
343 $title = Title
::newFromText( 'Help:Test' );
344 // Create a new content object with no content
345 $newContent = ContentHandler
::makeContent( '', $title, null, null, CONTENT_MODEL_WIKITEXT
);
346 // first check, if we become a blank page created summary with the right bitmask
347 $autoSummary = $content->getAutosummary( null, $newContent, 97 );
348 $this->assertEquals( $autoSummary, 'Created blank page' );
349 // now check, what we become with another bitmask
350 $autoSummary = $content->getAutosummary( null, $newContent, 92 );
351 $this->assertEquals( $autoSummary, '' );
355 public function testSupportsSections() {
356 $this->markTestIncomplete( "not yet implemented" );
360 public function testSupportsCategories() {
361 $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT
);
362 $this->assertTrue( $handler->supportsCategories(), 'content model supports categories' );
365 public function testSupportsDirectEditing() {
366 $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_JSON
);
367 $this->assertFalse( $handler->supportsDirectEditing(), 'direct editing is not supported' );
371 * @covers ContentHandler::runLegacyHooks
373 public function testRunLegacyHooks() {
374 Hooks
::register( 'testRunLegacyHooks', __CLASS__
. '::dummyHookHandler' );
376 $content = new WikitextContent( 'test text' );
377 $ok = ContentHandler
::runLegacyHooks(
378 'testRunLegacyHooks',
379 [ 'foo', &$content, 'bar' ],
383 $this->assertTrue( $ok, "runLegacyHooks should have returned true" );
384 $this->assertEquals( "TEST TEXT", $content->getNativeData() );
387 public static function dummyHookHandler( $foo, &$text, $bar ) {
388 if ( $text === null ||
$text === false ) {
392 $text = strtoupper( $text );
397 public function provideGetModelForID() {
399 [ CONTENT_MODEL_WIKITEXT
, 'WikitextContentHandler' ],
400 [ CONTENT_MODEL_JAVASCRIPT
, 'JavaScriptContentHandler' ],
401 [ CONTENT_MODEL_JSON
, 'JsonContentHandler' ],
402 [ CONTENT_MODEL_CSS
, 'CssContentHandler' ],
403 [ CONTENT_MODEL_TEXT
, 'TextContentHandler' ],
404 [ 'testing', 'DummyContentHandlerForTesting' ],
405 [ 'testing-callbacks', 'DummyContentHandlerForTesting' ],
410 * @dataProvider provideGetModelForID
412 public function testGetModelForID( $modelId, $handlerClass ) {
413 $handler = ContentHandler
::getForModelID( $modelId );
415 $this->assertInstanceOf( $handlerClass, $handler );
418 public function testGetFieldsForSearchIndex() {
419 $searchEngine = $this->newSearchEngine();
421 $handler = ContentHandler
::getForModelID( CONTENT_MODEL_WIKITEXT
);
423 $fields = $handler->getFieldsForSearchIndex( $searchEngine );
425 $this->assertArrayHasKey( 'category', $fields );
426 $this->assertArrayHasKey( 'external_link', $fields );
427 $this->assertArrayHasKey( 'outgoing_link', $fields );
428 $this->assertArrayHasKey( 'template', $fields );
431 private function newSearchEngine() {
432 $searchEngine = $this->getMockBuilder( 'SearchEngine' )
435 $searchEngine->expects( $this->any() )
436 ->method( 'makeSearchFieldMapping' )
437 ->will( $this->returnCallback( function( $name, $type ) {
438 return new DummySearchIndexFieldDefinition( $name, $type );
441 return $searchEngine;
445 * @covers ContentHandler::getDataForSearchIndex
447 public function testDataIndexFields() {
448 $mockEngine = $this->getMock( 'SearchEngine' );
449 $title = Title
::newFromText( 'Not_Main_Page', NS_MAIN
);
450 $page = new WikiPage( $title );
452 $this->setTemporaryHook( 'SearchDataForIndex',
453 function ( &$fields, ContentHandler
$handler, WikiPage
$page, ParserOutput
$output,
454 SearchEngine
$engine ) {
455 $fields['testDataField'] = 'test content';
458 $output = $page->getContent()->getParserOutput( $title );
459 $data = $page->getContentHandler()->getDataForSearchIndex( $page, $output, $mockEngine );
460 $this->assertArrayHasKey( 'text', $data );
461 $this->assertArrayHasKey( 'text_bytes', $data );
462 $this->assertArrayHasKey( 'language', $data );
463 $this->assertArrayHasKey( 'testDataField', $data );
464 $this->assertEquals( 'test content', $data['testDataField'] );
468 * @covers ContentHandler::getParserOutputForIndexing
470 public function testParserOutputForIndexing() {
471 $title = Title
::newFromText( 'Smithee', NS_MAIN
);
472 $page = new WikiPage( $title );
474 $out = $page->getContentHandler()->getParserOutputForIndexing( $page );
475 $this->assertInstanceOf( ParserOutput
::class, $out );
476 $this->assertContains( 'one who smiths', $out->getRawText() );