Merge "Remove Revision::getRevisionText from migrateArchiveText"
[lhc/web/wiklou.git] / tests / phpunit / includes / TitleMethodsTest.php
1 <?php
2
3 use MediaWiki\Interwiki\InterwikiLookup;
4 use MediaWiki\MediaWikiServices;
5
6 /**
7 * @group ContentHandler
8 * @group Database
9 *
10 * @note We don't make assumptions about the main namespace.
11 * But we do expect the Help namespace to contain Wikitext.
12 */
13 class TitleMethodsTest extends MediaWikiLangTestCase {
14
15 protected function setUp() {
16 parent::setUp();
17
18 $this->mergeMwGlobalArrayValue(
19 'wgExtraNamespaces',
20 [
21 12302 => 'TEST-JS',
22 12303 => 'TEST-JS_TALK',
23 ]
24 );
25
26 $this->mergeMwGlobalArrayValue(
27 'wgNamespaceContentModels',
28 [
29 12302 => CONTENT_MODEL_JAVASCRIPT,
30 ]
31 );
32 }
33
34 public static function provideInNamespace() {
35 return [
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 ],
44 ];
45 }
46
47 /**
48 * @dataProvider provideInNamespace
49 * @covers Title::inNamespace
50 */
51 public function testInNamespace( $title, $ns, $expectedBool ) {
52 $title = Title::newFromText( $title );
53 $this->assertEquals( $expectedBool, $title->inNamespace( $ns ) );
54 }
55
56 /**
57 * @covers Title::inNamespaces
58 */
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 ] ) );
65 }
66
67 public static function provideHasSubjectNamespace() {
68 return [
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 ],
77 ];
78 }
79
80 /**
81 * @dataProvider provideHasSubjectNamespace
82 * @covers Title::hasSubjectNamespace
83 */
84 public function testHasSubjectNamespace( $title, $ns, $expectedBool ) {
85 $title = Title::newFromText( $title );
86 $this->assertEquals( $expectedBool, $title->hasSubjectNamespace( $ns ) );
87 }
88
89 public function dataGetContentModel() {
90 return [
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 ],
111 ];
112 }
113
114 /**
115 * @dataProvider dataGetContentModel
116 * @covers Title::getContentModel
117 */
118 public function testGetContentModel( $title, $expectedModelId ) {
119 $title = Title::newFromText( $title );
120 $this->assertEquals( $expectedModelId, $title->getContentModel() );
121 }
122
123 /**
124 * @dataProvider dataGetContentModel
125 * @covers Title::hasContentModel
126 */
127 public function testHasContentModel( $title, $expectedModelId ) {
128 $title = Title::newFromText( $title );
129 $this->assertTrue( $title->hasContentModel( $expectedModelId ) );
130 }
131
132 public static function provideIsSiteConfigPage() {
133 return [
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 ],
158 ];
159 }
160
161 /**
162 * @dataProvider provideIsSiteConfigPage
163 * @covers Title::isSiteConfigPage
164 */
165 public function testSiteConfigPage( $title, $expectedBool ) {
166 $title = Title::newFromText( $title );
167 $this->assertEquals( $expectedBool, $title->isSiteConfigPage() );
168 }
169
170 public static function provideIsUserConfigPage() {
171 return [
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 ],
195 ];
196 }
197
198 /**
199 * @dataProvider provideIsUserConfigPage
200 * @covers Title::isUserConfigPage
201 */
202 public function testIsUserConfigPage( $title, $expectedBool ) {
203 $title = Title::newFromText( $title );
204 $this->assertEquals( $expectedBool, $title->isUserConfigPage() );
205 }
206
207 public static function provideIsUserCssConfigPage() {
208 return [
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 ],
218 ];
219 }
220
221 /**
222 * @dataProvider provideIsUserCssConfigPage
223 * @covers Title::isUserCssConfigPage
224 */
225 public function testIsUserCssConfigPage( $title, $expectedBool ) {
226 $title = Title::newFromText( $title );
227 $this->assertEquals( $expectedBool, $title->isUserCssConfigPage() );
228 }
229
230 public static function provideIsUserJsConfigPage() {
231 return [
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 ],
241 ];
242 }
243
244 /**
245 * @dataProvider provideIsUserJsConfigPage
246 * @covers Title::isUserJsConfigPage
247 */
248 public function testIsUserJsConfigPage( $title, $expectedBool ) {
249 $title = Title::newFromText( $title );
250 $this->assertEquals( $expectedBool, $title->isUserJsConfigPage() );
251 }
252
253 public static function provideIsWikitextPage() {
254 return [
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 ],
278 ];
279 }
280
281 /**
282 * @dataProvider provideIsWikitextPage
283 * @covers Title::isWikitextPage
284 */
285 public function testIsWikitextPage( $title, $expectedBool ) {
286 $title = Title::newFromText( $title );
287 $this->assertEquals( $expectedBool, $title->isWikitextPage() );
288 }
289
290 public static function provideGetOtherPage() {
291 return [
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 ],
298 ];
299 }
300
301 /**
302 * @dataProvider provideGetOtherpage
303 * @covers Title::getOtherPage
304 *
305 * @param string $text
306 * @param string|null $expected
307 */
308 public function testGetOtherPage( $text, $expected ) {
309 if ( $expected === null ) {
310 $this->setExpectedException( MWException::class );
311 }
312
313 $title = Title::newFromText( $text );
314 $this->assertEquals( $expected, $title->getOtherPage()->getPrefixedText() );
315 }
316
317 /**
318 * @covers Title::clearCaches
319 */
320 public function testClearCaches() {
321 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
322
323 $title1 = Title::newFromText( 'Foo' );
324 $linkCache->addGoodLinkObj( 23, $title1 );
325
326 Title::clearCaches();
327
328 $title2 = Title::newFromText( 'Foo' );
329 $this->assertNotSame( $title1, $title2, 'title cache should be empty' );
330 $this->assertSame( 0, $linkCache->getGoodLinkID( 'Foo' ), 'link cache should be empty' );
331 }
332
333 public function provideGetLinkURL() {
334 yield 'Simple' => [
335 '/wiki/Goats',
336 NS_MAIN,
337 'Goats'
338 ];
339
340 yield 'Fragment' => [
341 '/wiki/Goats#Goatificatiön',
342 NS_MAIN,
343 'Goats',
344 'Goatificatiön'
345 ];
346
347 yield 'Unknown interwiki with fragment' => [
348 'https://xx.wiki.test/wiki/xyzzy:Goats#Goatificatiön',
349 NS_MAIN,
350 'Goats',
351 'Goatificatiön',
352 'xyzzy'
353 ];
354
355 yield 'Interwiki with fragment' => [
356 'https://acme.test/Goats#Goatificati.C3.B6n',
357 NS_MAIN,
358 'Goats',
359 'Goatificatiön',
360 'acme'
361 ];
362
363 yield 'Interwiki with query' => [
364 'https://acme.test/Goats?a=1&b=blank+blank#Goatificati.C3.B6n',
365 NS_MAIN,
366 'Goats',
367 'Goatificatiön',
368 'acme',
369 [
370 'a' => 1,
371 'b' => 'blank blank'
372 ]
373 ];
374
375 yield 'Local interwiki with fragment' => [
376 'https://yy.wiki.test/wiki/Goats#Goatificatiön',
377 NS_MAIN,
378 'Goats',
379 'Goatificatiön',
380 'yy'
381 ];
382 }
383
384 /**
385 * @dataProvider provideGetLinkURL
386 *
387 * @covers Title::getLinkURL
388 * @covers Title::getFullURL
389 * @covers Title::getLocalURL
390 * @covers Title::getFragmentForURL
391 */
392 public function testGetLinkURL(
393 $expected,
394 $ns,
395 $title,
396 $fragment = '',
397 $interwiki = '',
398 $query = '',
399 $query2 = false,
400 $proto = false
401 ) {
402 $this->setMwGlobals( [
403 'wgServer' => 'https://xx.wiki.test',
404 'wgArticlePath' => '/wiki/$1',
405 'wgExternalInterwikiFragmentMode' => 'legacy',
406 'wgFragmentMode' => [ 'html5', 'legacy' ]
407 ] );
408
409 $interwikiLookup = $this->getMock( InterwikiLookup::class );
410
411 $interwikiLookup->method( 'fetch' )
412 ->willReturnCallback( function ( $interwiki ) {
413 switch ( $interwiki ) {
414 case '':
415 return null;
416 case 'acme':
417 return new Interwiki(
418 'acme',
419 'https://acme.test/$1'
420 );
421 case 'yy':
422 return new Interwiki(
423 'yy',
424 'https://yy.wiki.test/wiki/$1',
425 '/w/api.php',
426 'yywiki',
427 true
428 );
429 default:
430 return false;
431 }
432 } );
433
434 $this->setService( 'InterwikiLookup', $interwikiLookup );
435
436 $title = Title::makeTitle( $ns, $title, $fragment, $interwiki );
437 $this->assertSame( $expected, $title->getLinkURL( $query, $query2, $proto ) );
438 }
439
440 /**
441 * Integration test to catch regressions like T74870. Taken and modified
442 * from SemanticMediaWiki
443 *
444 * @covers Title::moveTo
445 */
446 public function testTitleMoveCompleteIntegrationTest() {
447 $this->hideDeprecated( 'Title::moveTo' );
448
449 $oldTitle = Title::newFromText( 'Help:Some title' );
450 WikiPage::factory( $oldTitle )->doEditContent( new WikitextContent( 'foo' ), 'bar' );
451 $newTitle = Title::newFromText( 'Help:Some other title' );
452 $this->assertNull(
453 WikiPage::factory( $newTitle )->getRevision()
454 );
455
456 $this->assertTrue( $oldTitle->moveTo( $newTitle, false, 'test1', true ) );
457 $this->assertNotNull(
458 WikiPage::factory( $oldTitle )->getRevision()
459 );
460 $this->assertNotNull(
461 WikiPage::factory( $newTitle )->getRevision()
462 );
463 }
464
465 function tearDown() {
466 Title::clearCaches();
467 parent::tearDown();
468 }
469 }