Merge "Add part to update ctd_user_defined in populateChangeTagDef"
[lhc/web/wiklou.git] / tests / phpunit / includes / content / WikitextContentTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 /**
6 * @group ContentHandler
7 *
8 * @group Database
9 * ^--- needed, because we do need the database to test link updates
10 */
11 class WikitextContentTest extends TextContentTest {
12 public static $sections = "Intro
13
14 == stuff ==
15 hello world
16
17 == test ==
18 just a test
19
20 == foo ==
21 more stuff
22 ";
23
24 public function newContent( $text ) {
25 return new WikitextContent( $text );
26 }
27
28 public static function dataGetParserOutput() {
29 return [
30 [
31 "WikitextContentTest_testGetParserOutput",
32 CONTENT_MODEL_WIKITEXT,
33 "hello ''world''\n",
34 "<div class=\"mw-parser-output\"><p>hello <i>world</i>\n</p>\n\n\n</div>"
35 ],
36 // TODO: more...?
37 ];
38 }
39
40 public static function dataGetSecondaryDataUpdates() {
41 return [
42 [ "WikitextContentTest_testGetSecondaryDataUpdates_1",
43 CONTENT_MODEL_WIKITEXT, "hello ''world''\n",
44 [
45 LinksUpdate::class => [
46 'mRecursive' => true,
47 'mLinks' => []
48 ]
49 ]
50 ],
51 [ "WikitextContentTest_testGetSecondaryDataUpdates_2",
52 CONTENT_MODEL_WIKITEXT, "hello [[world test 21344]]\n",
53 [
54 LinksUpdate::class => [
55 'mRecursive' => true,
56 'mLinks' => [
57 [ 'World_test_21344' => 0 ]
58 ]
59 ]
60 ]
61 ],
62 // TODO: more...?
63 ];
64 }
65
66 /**
67 * @dataProvider dataGetSecondaryDataUpdates
68 * @group Database
69 * @covers WikitextContent::getSecondaryDataUpdates
70 */
71 public function testGetSecondaryDataUpdates( $title, $model, $text, $expectedStuff ) {
72 $ns = $this->getDefaultWikitextNS();
73 $title = Title::newFromText( $title, $ns );
74
75 $content = ContentHandler::makeContent( $text, $title, $model );
76
77 $page = WikiPage::factory( $title );
78 $page->doEditContent( $content, '' );
79
80 $updates = $content->getSecondaryDataUpdates( $title );
81
82 // make updates accessible by class name
83 foreach ( $updates as $update ) {
84 $class = get_class( $update );
85 $updates[$class] = $update;
86 }
87
88 foreach ( $expectedStuff as $class => $fieldValues ) {
89 $this->assertArrayHasKey( $class, $updates, "missing an update of type $class" );
90
91 $update = $updates[$class];
92
93 foreach ( $fieldValues as $field => $value ) {
94 $v = $update->$field; # if the field doesn't exist, just crash and burn
95 $this->assertEquals(
96 $value,
97 $v,
98 "unexpected value for field $field in instance of $class"
99 );
100 }
101 }
102
103 $page->doDeleteArticle( '' );
104 }
105
106 public static function dataGetSection() {
107 return [
108 [ self::$sections,
109 "0",
110 "Intro"
111 ],
112 [ self::$sections,
113 "2",
114 "== test ==
115 just a test"
116 ],
117 [ self::$sections,
118 "8",
119 false
120 ],
121 ];
122 }
123
124 /**
125 * @dataProvider dataGetSection
126 * @covers WikitextContent::getSection
127 */
128 public function testGetSection( $text, $sectionId, $expectedText ) {
129 $content = $this->newContent( $text );
130
131 $sectionContent = $content->getSection( $sectionId );
132 if ( is_object( $sectionContent ) ) {
133 $sectionText = $sectionContent->getNativeData();
134 } else {
135 $sectionText = $sectionContent;
136 }
137
138 $this->assertEquals( $expectedText, $sectionText );
139 }
140
141 public static function dataReplaceSection() {
142 return [
143 [ self::$sections,
144 "0",
145 "No more",
146 null,
147 trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
148 ],
149 [ self::$sections,
150 "",
151 "No more",
152 null,
153 "No more"
154 ],
155 [ self::$sections,
156 "2",
157 "== TEST ==\nmore fun",
158 null,
159 trim( preg_replace(
160 '/^== test ==.*== foo ==/sm', "== TEST ==\nmore fun\n\n== foo ==",
161 self::$sections
162 ) )
163 ],
164 [ self::$sections,
165 "8",
166 "No more",
167 null,
168 self::$sections
169 ],
170 [ self::$sections,
171 "new",
172 "No more",
173 "New",
174 trim( self::$sections ) . "\n\n\n== New ==\n\nNo more"
175 ],
176 ];
177 }
178
179 /**
180 * @dataProvider dataReplaceSection
181 * @covers WikitextContent::replaceSection
182 */
183 public function testReplaceSection( $text, $section, $with, $sectionTitle, $expected ) {
184 $content = $this->newContent( $text );
185 $c = $content->replaceSection( $section, $this->newContent( $with ), $sectionTitle );
186
187 $this->assertEquals( $expected, is_null( $c ) ? null : $c->getNativeData() );
188 }
189
190 /**
191 * @covers WikitextContent::addSectionHeader
192 */
193 public function testAddSectionHeader() {
194 $content = $this->newContent( 'hello world' );
195 $content = $content->addSectionHeader( 'test' );
196
197 $this->assertEquals( "== test ==\n\nhello world", $content->getNativeData() );
198 }
199
200 public static function dataPreSaveTransform() {
201 return [
202 [ 'hello this is ~~~',
203 "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
204 ],
205 [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
206 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
207 ],
208 [ // rtrim
209 " Foo \n ",
210 " Foo",
211 ],
212 ];
213 }
214
215 public static function dataPreloadTransform() {
216 return [
217 [
218 'hello this is ~~~',
219 "hello this is ~~~",
220 ],
221 [
222 'hello \'\'this\'\' is <noinclude>foo</noinclude><includeonly>bar</includeonly>',
223 'hello \'\'this\'\' is bar',
224 ],
225 ];
226 }
227
228 public static function dataGetRedirectTarget() {
229 return [
230 [ '#REDIRECT [[Test]]',
231 'Test',
232 ],
233 [ '#REDIRECT Test',
234 null,
235 ],
236 [ '* #REDIRECT [[Test]]',
237 null,
238 ],
239 ];
240 }
241
242 public static function dataGetTextForSummary() {
243 return [
244 [ "hello\nworld.",
245 16,
246 'hello world.',
247 ],
248 [ 'hello world.',
249 8,
250 'hello...',
251 ],
252 [ '[[hello world]].',
253 8,
254 'hel...',
255 ],
256 ];
257 }
258
259 public static function dataIsCountable() {
260 return [
261 [ '',
262 null,
263 'any',
264 true
265 ],
266 [ 'Foo',
267 null,
268 'any',
269 true
270 ],
271 [ 'Foo',
272 null,
273 'link',
274 false
275 ],
276 [ 'Foo [[bar]]',
277 null,
278 'link',
279 true
280 ],
281 [ 'Foo',
282 true,
283 'link',
284 true
285 ],
286 [ 'Foo [[bar]]',
287 false,
288 'link',
289 false
290 ],
291 [ '#REDIRECT [[bar]]',
292 true,
293 'any',
294 false
295 ],
296 [ '#REDIRECT [[bar]]',
297 true,
298 'link',
299 false
300 ],
301 ];
302 }
303
304 /**
305 * @covers WikitextContent::matchMagicWord
306 */
307 public function testMatchMagicWord() {
308 $mw = MediaWikiServices::getInstance()->getMagicWordFactory()->get( "staticredirect" );
309
310 $content = $this->newContent( "#REDIRECT [[FOO]]\n__STATICREDIRECT__" );
311 $this->assertTrue( $content->matchMagicWord( $mw ), "should have matched magic word" );
312
313 $content = $this->newContent( "#REDIRECT [[FOO]]" );
314 $this->assertFalse(
315 $content->matchMagicWord( $mw ),
316 "should not have matched magic word"
317 );
318 }
319
320 /**
321 * @covers WikitextContent::updateRedirect
322 */
323 public function testUpdateRedirect() {
324 $target = Title::newFromText( "testUpdateRedirect_target" );
325
326 // test with non-redirect page
327 $content = $this->newContent( "hello world." );
328 $newContent = $content->updateRedirect( $target );
329
330 $this->assertTrue( $content->equals( $newContent ), "content should be unchanged" );
331
332 // test with actual redirect
333 $content = $this->newContent( "#REDIRECT [[Someplace]]" );
334 $newContent = $content->updateRedirect( $target );
335
336 $this->assertFalse( $content->equals( $newContent ), "content should have changed" );
337 $this->assertTrue( $newContent->isRedirect(), "new content should be a redirect" );
338
339 $this->assertEquals(
340 $target->getFullText(),
341 $newContent->getRedirectTarget()->getFullText()
342 );
343 }
344
345 /**
346 * @covers WikitextContent::getModel
347 */
348 public function testGetModel() {
349 $content = $this->newContent( "hello world." );
350
351 $this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getModel() );
352 }
353
354 /**
355 * @covers WikitextContent::getContentHandler
356 */
357 public function testGetContentHandler() {
358 $content = $this->newContent( "hello world." );
359
360 $this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getContentHandler()->getModelID() );
361 }
362
363 public function testRedirectParserOption() {
364 $title = Title::newFromText( 'testRedirectParserOption' );
365
366 // Set up hook and its reporting variables
367 $wikitext = null;
368 $redirectTarget = null;
369 $this->mergeMwGlobalArrayValue( 'wgHooks', [
370 'InternalParseBeforeLinks' => [
371 function ( &$parser, &$text, &$stripState ) use ( &$wikitext, &$redirectTarget ) {
372 $wikitext = $text;
373 $redirectTarget = $parser->getOptions()->getRedirectTarget();
374 }
375 ]
376 ] );
377
378 // Test with non-redirect page
379 $wikitext = false;
380 $redirectTarget = false;
381 $content = $this->newContent( 'hello world.' );
382 $options = ParserOptions::newCanonical( 'canonical' );
383 $options->setRedirectTarget( $title );
384 $content->getParserOutput( $title, null, $options );
385 $this->assertEquals( 'hello world.', $wikitext,
386 'Wikitext passed to hook was not as expected'
387 );
388 $this->assertEquals( null, $redirectTarget, 'Redirect seen in hook was not null' );
389 $this->assertEquals( $title, $options->getRedirectTarget(),
390 'ParserOptions\' redirectTarget was changed'
391 );
392
393 // Test with a redirect page
394 $wikitext = false;
395 $redirectTarget = false;
396 $content = $this->newContent(
397 "#REDIRECT [[TestRedirectParserOption/redir]]\nhello redirect."
398 );
399 $options = ParserOptions::newCanonical( 'canonical' );
400 $content->getParserOutput( $title, null, $options );
401 $this->assertEquals(
402 'hello redirect.',
403 $wikitext,
404 'Wikitext passed to hook was not as expected'
405 );
406 $this->assertNotEquals(
407 null,
408 $redirectTarget,
409 'Redirect seen in hook was null' );
410 $this->assertEquals(
411 'TestRedirectParserOption/redir',
412 $redirectTarget->getFullText(),
413 'Redirect seen in hook was not the expected title'
414 );
415 $this->assertEquals(
416 null,
417 $options->getRedirectTarget(),
418 'ParserOptions\' redirectTarget was changed'
419 );
420 }
421
422 public static function dataEquals() {
423 return [
424 [ new WikitextContent( "hallo" ), null, false ],
425 [ new WikitextContent( "hallo" ), new WikitextContent( "hallo" ), true ],
426 [ new WikitextContent( "hallo" ), new JavaScriptContent( "hallo" ), false ],
427 [ new WikitextContent( "hallo" ), new TextContent( "hallo" ), false ],
428 [ new WikitextContent( "hallo" ), new WikitextContent( "HALLO" ), false ],
429 ];
430 }
431
432 public static function dataGetDeletionUpdates() {
433 return [
434 [
435 CONTENT_MODEL_WIKITEXT, "hello ''world''\n",
436 [ LinksDeletionUpdate::class => [] ]
437 ],
438 [
439 CONTENT_MODEL_WIKITEXT, "hello [[world test 21344]]\n",
440 [ LinksDeletionUpdate::class => [] ]
441 ],
442 // @todo more...?
443 ];
444 }
445
446 /**
447 * @covers WikitextContent::preSaveTransform
448 * @covers WikitextContent::fillParserOutput
449 */
450 public function testHadSignature() {
451 $titleObj = Title::newFromText( __CLASS__ );
452
453 $content = new WikitextContent( '~~~~' );
454 $pstContent = $content->preSaveTransform(
455 $titleObj, $this->getTestUser()->getUser(), new ParserOptions()
456 );
457
458 $this->assertTrue( $pstContent->getParserOutput( $titleObj )->getFlag( 'user-signature' ) );
459 }
460 }