3 use MediaWiki\Interwiki\InterwikiLookup
;
4 use MediaWiki\MediaWikiServices
;
7 * @group ContentHandler
10 * @note We don't make assumptions about the main namespace.
11 * But we do expect the Help namespace to contain Wikitext.
13 class TitleMethodsTest
extends MediaWikiLangTestCase
{
15 protected function setUp() {
18 $this->mergeMwGlobalArrayValue(
22 12303 => 'TEST-JS_TALK',
26 $this->mergeMwGlobalArrayValue(
27 'wgNamespaceContentModels',
29 12302 => CONTENT_MODEL_JAVASCRIPT
,
34 public static function provideInNamespace() {
36 [ 'Main Page', NS_MAIN
, true ],
37 [ 'Main Page', NS_TALK
, false ],
38 [ 'Main Page', NS_USER
, false ],
39 [ 'User:Foo', NS_USER
, true ],
40 [ 'User:Foo', NS_USER_TALK
, false ],
41 [ 'User:Foo', NS_TEMPLATE
, false ],
42 [ 'User_talk:Foo', NS_USER_TALK
, true ],
43 [ 'User_talk:Foo', NS_USER
, false ],
48 * @dataProvider provideInNamespace
49 * @covers Title::inNamespace
51 public function testInNamespace( $title, $ns, $expectedBool ) {
52 $title = Title
::newFromText( $title );
53 $this->assertEquals( $expectedBool, $title->inNamespace( $ns ) );
57 * @covers Title::inNamespaces
59 public function testInNamespaces() {
60 $mainpage = Title
::newFromText( 'Main Page' );
61 $this->assertTrue( $mainpage->inNamespaces( NS_MAIN
, NS_USER
) );
62 $this->assertTrue( $mainpage->inNamespaces( [ NS_MAIN
, NS_USER
] ) );
63 $this->assertTrue( $mainpage->inNamespaces( [ NS_USER
, NS_MAIN
] ) );
64 $this->assertFalse( $mainpage->inNamespaces( [ NS_PROJECT
, NS_TEMPLATE
] ) );
67 public static function provideHasSubjectNamespace() {
69 [ 'Main Page', NS_MAIN
, true ],
70 [ 'Main Page', NS_TALK
, true ],
71 [ 'Main Page', NS_USER
, false ],
72 [ 'User:Foo', NS_USER
, true ],
73 [ 'User:Foo', NS_USER_TALK
, true ],
74 [ 'User:Foo', NS_TEMPLATE
, false ],
75 [ 'User_talk:Foo', NS_USER_TALK
, true ],
76 [ 'User_talk:Foo', NS_USER
, true ],
81 * @dataProvider provideHasSubjectNamespace
82 * @covers Title::hasSubjectNamespace
84 public function testHasSubjectNamespace( $title, $ns, $expectedBool ) {
85 $title = Title
::newFromText( $title );
86 $this->assertEquals( $expectedBool, $title->hasSubjectNamespace( $ns ) );
89 public function dataGetContentModel() {
91 [ 'Help:Foo', CONTENT_MODEL_WIKITEXT
],
92 [ 'Help:Foo.js', CONTENT_MODEL_WIKITEXT
],
93 [ 'Help:Foo/bar.js', CONTENT_MODEL_WIKITEXT
],
94 [ 'User:Foo', CONTENT_MODEL_WIKITEXT
],
95 [ 'User:Foo.js', CONTENT_MODEL_WIKITEXT
],
96 [ 'User:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT
],
97 [ 'User:Foo/bar.css', CONTENT_MODEL_CSS
],
98 [ 'User talk:Foo/bar.css', CONTENT_MODEL_WIKITEXT
],
99 [ 'User:Foo/bar.js.xxx', CONTENT_MODEL_WIKITEXT
],
100 [ 'User:Foo/bar.xxx', CONTENT_MODEL_WIKITEXT
],
101 [ 'MediaWiki:Foo.js', CONTENT_MODEL_JAVASCRIPT
],
102 [ 'MediaWiki:Foo.css', CONTENT_MODEL_CSS
],
103 [ 'MediaWiki:Foo/bar.css', CONTENT_MODEL_CSS
],
104 [ 'MediaWiki:Foo.JS', CONTENT_MODEL_WIKITEXT
],
105 [ 'MediaWiki:Foo.CSS', CONTENT_MODEL_WIKITEXT
],
106 [ 'MediaWiki:Foo.css.xxx', CONTENT_MODEL_WIKITEXT
],
107 [ 'TEST-JS:Foo', CONTENT_MODEL_JAVASCRIPT
],
108 [ 'TEST-JS:Foo.js', CONTENT_MODEL_JAVASCRIPT
],
109 [ 'TEST-JS:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT
],
110 [ 'TEST-JS_TALK:Foo.js', CONTENT_MODEL_WIKITEXT
],
115 * @dataProvider dataGetContentModel
116 * @covers Title::getContentModel
118 public function testGetContentModel( $title, $expectedModelId ) {
119 $title = Title
::newFromText( $title );
120 $this->assertEquals( $expectedModelId, $title->getContentModel() );
124 * @dataProvider dataGetContentModel
125 * @covers Title::hasContentModel
127 public function testHasContentModel( $title, $expectedModelId ) {
128 $title = Title
::newFromText( $title );
129 $this->assertTrue( $title->hasContentModel( $expectedModelId ) );
132 public static function provideIsSiteConfigPage() {
134 [ 'Help:Foo', false ],
135 [ 'Help:Foo.js', false ],
136 [ 'Help:Foo/bar.js', false ],
137 [ 'User:Foo', false ],
138 [ 'User:Foo.js', false ],
139 [ 'User:Foo/bar.js', false ],
140 [ 'User:Foo/bar.json', false ],
141 [ 'User:Foo/bar.css', false ],
142 [ 'User:Foo/bar.JS', false ],
143 [ 'User:Foo/bar.JSON', false ],
144 [ 'User:Foo/bar.CSS', false ],
145 [ 'User talk:Foo/bar.css', false ],
146 [ 'User:Foo/bar.js.xxx', false ],
147 [ 'User:Foo/bar.xxx', false ],
148 [ 'MediaWiki:Foo.js', true ],
149 [ 'MediaWiki:Foo.json', true ],
150 [ 'MediaWiki:Foo.css', true ],
151 [ 'MediaWiki:Foo.JS', false ],
152 [ 'MediaWiki:Foo.JSON', false ],
153 [ 'MediaWiki:Foo.CSS', false ],
154 [ 'MediaWiki:Foo/bar.css', true ],
155 [ 'MediaWiki:Foo.css.xxx', false ],
156 [ 'TEST-JS:Foo', false ],
157 [ 'TEST-JS:Foo.js', false ],
162 * @dataProvider provideIsSiteConfigPage
163 * @covers Title::isSiteConfigPage
165 public function testSiteConfigPage( $title, $expectedBool ) {
166 $title = Title
::newFromText( $title );
167 $this->assertEquals( $expectedBool, $title->isSiteConfigPage() );
170 public static function provideIsUserConfigPage() {
172 [ 'Help:Foo', false ],
173 [ 'Help:Foo.js', false ],
174 [ 'Help:Foo/bar.js', false ],
175 [ 'User:Foo', false ],
176 [ 'User:Foo.js', false ],
177 [ 'User:Foo/bar.js', true ],
178 [ 'User:Foo/bar.JS', false ],
179 [ 'User:Foo/bar.json', true ],
180 [ 'User:Foo/bar.JSON', false ],
181 [ 'User:Foo/bar.css', true ],
182 [ 'User:Foo/bar.CSS', false ],
183 [ 'User talk:Foo/bar.css', false ],
184 [ 'User:Foo/bar.js.xxx', false ],
185 [ 'User:Foo/bar.xxx', false ],
186 [ 'MediaWiki:Foo.js', false ],
187 [ 'MediaWiki:Foo.json', false ],
188 [ 'MediaWiki:Foo.css', false ],
189 [ 'MediaWiki:Foo.JS', false ],
190 [ 'MediaWiki:Foo.JSON', false ],
191 [ 'MediaWiki:Foo.CSS', false ],
192 [ 'MediaWiki:Foo.css.xxx', false ],
193 [ 'TEST-JS:Foo', false ],
194 [ 'TEST-JS:Foo.js', false ],
199 * @dataProvider provideIsUserConfigPage
200 * @covers Title::isUserConfigPage
202 public function testIsUserConfigPage( $title, $expectedBool ) {
203 $title = Title
::newFromText( $title );
204 $this->assertEquals( $expectedBool, $title->isUserConfigPage() );
207 public static function provideIsUserCssConfigPage() {
209 [ 'Help:Foo', false ],
210 [ 'Help:Foo.css', false ],
211 [ 'User:Foo', false ],
212 [ 'User:Foo.js', false ],
213 [ 'User:Foo.json', false ],
214 [ 'User:Foo.css', false ],
215 [ 'User:Foo/bar.js', false ],
216 [ 'User:Foo/bar.json', false ],
217 [ 'User:Foo/bar.css', true ],
222 * @dataProvider provideIsUserCssConfigPage
223 * @covers Title::isUserCssConfigPage
225 public function testIsUserCssConfigPage( $title, $expectedBool ) {
226 $title = Title
::newFromText( $title );
227 $this->assertEquals( $expectedBool, $title->isUserCssConfigPage() );
230 public static function provideIsUserJsConfigPage() {
232 [ 'Help:Foo', false ],
233 [ 'Help:Foo.css', false ],
234 [ 'User:Foo', false ],
235 [ 'User:Foo.js', false ],
236 [ 'User:Foo.json', false ],
237 [ 'User:Foo.css', false ],
238 [ 'User:Foo/bar.js', true ],
239 [ 'User:Foo/bar.json', false ],
240 [ 'User:Foo/bar.css', false ],
245 * @dataProvider provideIsUserJsConfigPage
246 * @covers Title::isUserJsConfigPage
248 public function testIsUserJsConfigPage( $title, $expectedBool ) {
249 $title = Title
::newFromText( $title );
250 $this->assertEquals( $expectedBool, $title->isUserJsConfigPage() );
253 public static function provideIsWikitextPage() {
255 [ 'Help:Foo', true ],
256 [ 'Help:Foo.js', true ],
257 [ 'Help:Foo/bar.js', true ],
258 [ 'User:Foo', true ],
259 [ 'User:Foo.js', true ],
260 [ 'User:Foo/bar.js', false ],
261 [ 'User:Foo/bar.json', false ],
262 [ 'User:Foo/bar.css', false ],
263 [ 'User talk:Foo/bar.css', true ],
264 [ 'User:Foo/bar.js.xxx', true ],
265 [ 'User:Foo/bar.xxx', true ],
266 [ 'MediaWiki:Foo.js', false ],
267 [ 'User:Foo/bar.JS', true ],
268 [ 'User:Foo/bar.JSON', true ],
269 [ 'User:Foo/bar.CSS', true ],
270 [ 'MediaWiki:Foo.json', false ],
271 [ 'MediaWiki:Foo.css', false ],
272 [ 'MediaWiki:Foo.JS', true ],
273 [ 'MediaWiki:Foo.JSON', true ],
274 [ 'MediaWiki:Foo.CSS', true ],
275 [ 'MediaWiki:Foo.css.xxx', true ],
276 [ 'TEST-JS:Foo', false ],
277 [ 'TEST-JS:Foo.js', false ],
282 * @dataProvider provideIsWikitextPage
283 * @covers Title::isWikitextPage
285 public function testIsWikitextPage( $title, $expectedBool ) {
286 $title = Title
::newFromText( $title );
287 $this->assertEquals( $expectedBool, $title->isWikitextPage() );
290 public static function provideGetOtherPage() {
292 [ 'Main Page', 'Talk:Main Page' ],
293 [ 'Talk:Main Page', 'Main Page' ],
294 [ 'Help:Main Page', 'Help talk:Main Page' ],
295 [ 'Help talk:Main Page', 'Help:Main Page' ],
296 [ 'Special:FooBar', null ],
297 [ 'Media:File.jpg', null ],
302 * @dataProvider provideGetOtherpage
303 * @covers Title::getOtherPage
305 * @param string $text
306 * @param string|null $expected
308 public function testGetOtherPage( $text, $expected ) {
309 if ( $expected === null ) {
310 $this->setExpectedException( MWException
::class );
313 $title = Title
::newFromText( $text );
314 $this->assertEquals( $expected, $title->getOtherPage()->getPrefixedText() );
318 * @covers Title::clearCaches
320 public function testClearCaches() {
321 $linkCache = MediaWikiServices
::getInstance()->getLinkCache();
323 $title1 = Title
::newFromText( 'Foo' );
324 $linkCache->addGoodLinkObj( 23, $title1 );
326 Title
::clearCaches();
328 $title2 = Title
::newFromText( 'Foo' );
329 $this->assertNotSame( $title1, $title2, 'title cache should be empty' );
330 $this->assertEquals( 0, $linkCache->getGoodLinkID( 'Foo' ), 'link cache should be empty' );
333 public function provideGetLinkURL() {
340 yield
'Fragment' => [
341 '/wiki/Goats#Goatificatiön',
347 yield
'Unknown interwiki with fragment' => [
348 'https://xx.wiki.test/wiki/xyzzy:Goats#Goatificatiön',
355 yield
'Interwiki with fragment' => [
356 'https://acme.test/Goats#Goatificati.C3.B6n',
363 yield
'Interwiki with query' => [
364 'https://acme.test/Goats?a=1&b=blank+blank#Goatificati.C3.B6n',
375 yield
'Local interwiki with fragment' => [
376 'https://yy.wiki.test/wiki/Goats#Goatificatiön',
385 * @dataProvider provideGetLinkURL
387 * @covers Title::getLinkURL
388 * @covers Title::getFullURL
389 * @covers Title::getLocalURL
390 * @covers Title::getFragmentForURL
392 public function testGetLinkURL(
402 $this->setMwGlobals( [
403 'wgServer' => 'https://xx.wiki.test',
404 'wgArticlePath' => '/wiki/$1',
405 'wgExternalInterwikiFragmentMode' => 'legacy',
406 'wgFragmentMode' => [ 'html5', 'legacy' ]
409 $interwikiLookup = $this->getMock( InterwikiLookup
::class );
411 $interwikiLookup->method( 'fetch' )
412 ->willReturnCallback( function ( $interwiki ) {
413 switch ( $interwiki ) {
417 return new Interwiki(
419 'https://acme.test/$1'
422 return new Interwiki(
424 'https://yy.wiki.test/wiki/$1',
434 $this->setService( 'InterwikiLookup', $interwikiLookup );
436 $title = Title
::makeTitle( $ns, $title, $fragment, $interwiki );
437 $this->assertSame( $expected, $title->getLinkURL( $query, $query2, $proto ) );
441 * Integration test to catch regressions like T74870. Taken and modified
442 * from SemanticMediaWiki
444 * @covers Title::moveTo
446 public function testTitleMoveCompleteIntegrationTest() {
447 $this->hideDeprecated( 'Title::moveTo' );
449 $oldTitle = Title
::newFromText( 'Help:Some title' );
450 WikiPage
::factory( $oldTitle )->doEditContent( new WikitextContent( 'foo' ), 'bar' );
451 $newTitle = Title
::newFromText( 'Help:Some other title' );
453 WikiPage
::factory( $newTitle )->getRevision()
456 $this->assertTrue( $oldTitle->moveTo( $newTitle, false, 'test1', true ) );
457 $this->assertNotNull(
458 WikiPage
::factory( $oldTitle )->getRevision()
460 $this->assertNotNull(
461 WikiPage
::factory( $newTitle )->getRevision()
465 function tearDown() {
466 Title
::clearCaches();