Merge "Use job queue for deletion of pages with many revisions"
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / PageUpdaterTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Storage;
4
5 use CommentStoreComment;
6 use Content;
7 use MediaWiki\MediaWikiServices;
8 use MediaWiki\Storage\RevisionRecord;
9 use MediaWiki\Storage\SlotRecord;
10 use MediaWikiTestCase;
11 use ParserOptions;
12 use RecentChange;
13 use Revision;
14 use TextContent;
15 use Title;
16 use User;
17 use WikiPage;
18
19 /**
20 * @covers \MediaWiki\Storage\PageUpdater
21 * @group Database
22 */
23 class PageUpdaterTest extends MediaWikiTestCase {
24
25 private function getDummyTitle( $method ) {
26 return Title::newFromText( $method, $this->getDefaultWikitextNS() );
27 }
28
29 /**
30 * @param int $revId
31 *
32 * @return null|RecentChange
33 */
34 private function getRecentChangeFor( $revId ) {
35 $qi = RecentChange::getQueryInfo();
36 $row = $this->db->selectRow(
37 $qi['tables'],
38 $qi['fields'],
39 [ 'rc_this_oldid' => $revId ],
40 __METHOD__,
41 [],
42 $qi['joins']
43 );
44
45 return $row ? RecentChange::newFromRow( $row ) : null;
46 }
47
48 // TODO: test setAjaxEditStash();
49
50 /**
51 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
52 * @covers \WikiPage::newPageUpdater()
53 */
54 public function testCreatePage() {
55 $user = $this->getTestUser()->getUser();
56
57 $title = $this->getDummyTitle( __METHOD__ );
58 $page = WikiPage::factory( $title );
59 $updater = $page->newPageUpdater( $user );
60
61 $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
62
63 $this->assertFalse( $updater->wasCommitted(), 'wasCommitted' );
64 $this->assertFalse( $updater->getOriginalRevisionId(), 'getOriginalRevisionId' );
65 $this->assertSame( 0, $updater->getUndidRevisionId(), 'getUndidRevisionId' );
66
67 $updater->addTag( 'foo' );
68 $updater->addTags( [ 'bar', 'qux' ] );
69
70 $tags = $updater->getExplicitTags();
71 sort( $tags );
72 $this->assertSame( [ 'bar', 'foo', 'qux' ], $tags, 'getExplicitTags' );
73
74 // TODO: MCR: test additional slots
75 $content = new TextContent( 'Lorem Ipsum' );
76 $updater->setContent( SlotRecord::MAIN, $content );
77
78 $parent = $updater->grabParentRevision();
79
80 $this->assertNull( $parent, 'getParentRevision' );
81 $this->assertFalse( $updater->wasCommitted(), 'wasCommitted' );
82
83 // TODO: test that hasEditConflict() grabs the parent revision
84 $this->assertFalse( $updater->hasEditConflict( 0 ), 'hasEditConflict' );
85 $this->assertTrue( $updater->hasEditConflict( 1 ), 'hasEditConflict' );
86
87 // TODO: test failure with EDIT_UPDATE
88 // TODO: test EDIT_MINOR, EDIT_BOT, etc
89 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
90 $rev = $updater->saveRevision( $summary );
91
92 $this->assertNotNull( $rev );
93 $this->assertSame( 0, $rev->getParentId() );
94 $this->assertSame( $summary->text, $rev->getComment( RevisionRecord::RAW )->text );
95 $this->assertSame( $user->getName(), $rev->getUser( RevisionRecord::RAW )->getName() );
96
97 $this->assertTrue( $updater->wasCommitted(), 'wasCommitted()' );
98 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
99 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
100 $this->assertTrue( $updater->isNew(), 'isNew()' );
101 $this->assertFalse( $updater->isUnchanged(), 'isUnchanged()' );
102 $this->assertNotNull( $updater->getNewRevision(), 'getNewRevision()' );
103 $this->assertInstanceOf( Revision::class, $updater->getStatus()->value['revision'] );
104
105 $rev = $updater->getNewRevision();
106 $revContent = $rev->getContent( SlotRecord::MAIN );
107 $this->assertSame( 'Lorem Ipsum', $revContent->serialize(), 'revision content' );
108
109 // were the WikiPage and Title objects updated?
110 $this->assertTrue( $page->exists(), 'WikiPage::exists()' );
111 $this->assertTrue( $title->exists(), 'Title::exists()' );
112 $this->assertSame( $rev->getId(), $page->getLatest(), 'WikiPage::getRevision()' );
113 $this->assertNotNull( $page->getRevision(), 'WikiPage::getRevision()' );
114
115 // re-load
116 $page2 = WikiPage::factory( $title );
117 $this->assertTrue( $page2->exists(), 'WikiPage::exists()' );
118 $this->assertSame( $rev->getId(), $page2->getLatest(), 'WikiPage::getRevision()' );
119 $this->assertNotNull( $page2->getRevision(), 'WikiPage::getRevision()' );
120
121 // Check RC entry
122 $rc = $this->getRecentChangeFor( $rev->getId() );
123 $this->assertNotNull( $rc, 'RecentChange' );
124
125 // check site stats - this asserts that derived data updates where run.
126 $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
127 $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
128 $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
129
130 // re-edit with same content - should be a "null-edit"
131 $updater = $page->newPageUpdater( $user );
132 $updater->setContent( SlotRecord::MAIN, $content );
133
134 $summary = CommentStoreComment::newUnsavedComment( 'to to re-edit' );
135 $rev = $updater->saveRevision( $summary );
136 $status = $updater->getStatus();
137
138 $this->assertNull( $rev, 'getNewRevision()' );
139 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
140 $this->assertTrue( $updater->isUnchanged(), 'isUnchanged' );
141 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
142 $this->assertTrue( $status->isOK(), 'getStatus()->isOK()' );
143 $this->assertTrue( $status->hasMessage( 'edit-no-change' ), 'edit-no-change' );
144 }
145
146 /**
147 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
148 * @covers \WikiPage::newPageUpdater()
149 */
150 public function testUpdatePage() {
151 $user = $this->getTestUser()->getUser();
152
153 $title = $this->getDummyTitle( __METHOD__ );
154 $this->insertPage( $title );
155
156 $page = WikiPage::factory( $title );
157 $parentId = $page->getLatest();
158
159 $updater = $page->newPageUpdater( $user );
160
161 $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
162
163 $updater->setOriginalRevisionId( 7 );
164 $this->assertSame( 7, $updater->getOriginalRevisionId(), 'getOriginalRevisionId' );
165
166 $this->assertFalse( $updater->hasEditConflict( $parentId ), 'hasEditConflict' );
167 $this->assertTrue( $updater->hasEditConflict( $parentId - 1 ), 'hasEditConflict' );
168 $this->assertTrue( $updater->hasEditConflict( 0 ), 'hasEditConflict' );
169
170 // TODO: MCR: test additional slots
171 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
172
173 // TODO: test all flags for saveRevision()!
174 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
175 $rev = $updater->saveRevision( $summary );
176
177 $this->assertNotNull( $rev );
178 $this->assertSame( $parentId, $rev->getParentId() );
179 $this->assertSame( $summary->text, $rev->getComment( RevisionRecord::RAW )->text );
180 $this->assertSame( $user->getName(), $rev->getUser( RevisionRecord::RAW )->getName() );
181
182 $this->assertTrue( $updater->wasCommitted(), 'wasCommitted()' );
183 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
184 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
185 $this->assertFalse( $updater->isNew(), 'isNew()' );
186 $this->assertNotNull( $updater->getNewRevision(), 'getNewRevision()' );
187 $this->assertInstanceOf( Revision::class, $updater->getStatus()->value['revision'] );
188 $this->assertFalse( $updater->isUnchanged(), 'isUnchanged()' );
189
190 // TODO: Test null revision (with different user): new revision!
191
192 $rev = $updater->getNewRevision();
193 $revContent = $rev->getContent( SlotRecord::MAIN );
194 $this->assertSame( 'Lorem Ipsum', $revContent->serialize(), 'revision content' );
195
196 // were the WikiPage and Title objects updated?
197 $this->assertTrue( $page->exists(), 'WikiPage::exists()' );
198 $this->assertTrue( $title->exists(), 'Title::exists()' );
199 $this->assertSame( $rev->getId(), $page->getLatest(), 'WikiPage::getRevision()' );
200 $this->assertNotNull( $page->getRevision(), 'WikiPage::getRevision()' );
201
202 // re-load
203 $page2 = WikiPage::factory( $title );
204 $this->assertTrue( $page2->exists(), 'WikiPage::exists()' );
205 $this->assertSame( $rev->getId(), $page2->getLatest(), 'WikiPage::getRevision()' );
206 $this->assertNotNull( $page2->getRevision(), 'WikiPage::getRevision()' );
207
208 // Check RC entry
209 $rc = $this->getRecentChangeFor( $rev->getId() );
210 $this->assertNotNull( $rc, 'RecentChange' );
211
212 // re-edit
213 $updater = $page->newPageUpdater( $user );
214 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
215
216 $summary = CommentStoreComment::newUnsavedComment( 're-edit' );
217 $updater->saveRevision( $summary );
218 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
219 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
220
221 // check site stats - this asserts that derived data updates where run.
222 $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
223 $this->assertNotNull( $stats, 'site_stats' );
224 $this->assertSame( $oldStats->ss_total_pages + 0, (int)$stats->ss_total_pages );
225 $this->assertSame( $oldStats->ss_total_edits + 2, (int)$stats->ss_total_edits );
226 }
227
228 /**
229 * Creates a revision in the database.
230 *
231 * @param WikiPage $page
232 * @param $summary
233 * @param null|string|Content $content
234 *
235 * @return RevisionRecord|null
236 */
237 private function createRevision( WikiPage $page, $summary, $content = null ) {
238 $user = $this->getTestUser()->getUser();
239 $comment = CommentStoreComment::newUnsavedComment( $summary );
240
241 if ( !$content instanceof Content ) {
242 $content = new TextContent( $content ?? $summary );
243 }
244
245 $updater = $page->newPageUpdater( $user );
246 $updater->setContent( SlotRecord::MAIN, $content );
247 $rev = $updater->saveRevision( $comment );
248 return $rev;
249 }
250
251 /**
252 * @covers \MediaWiki\Storage\PageUpdater::grabParentRevision()
253 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
254 */
255 public function testCompareAndSwapFailure() {
256 $user = $this->getTestUser()->getUser();
257
258 $title = $this->getDummyTitle( __METHOD__ );
259
260 // start editing non-existing page
261 $page = WikiPage::factory( $title );
262 $updater = $page->newPageUpdater( $user );
263 $updater->grabParentRevision();
264
265 // create page concurrently
266 $concurrentPage = WikiPage::factory( $title );
267 $this->createRevision( $concurrentPage, __METHOD__ . '-one' );
268
269 // try creating the page - should trigger CAS failure.
270 $summary = CommentStoreComment::newUnsavedComment( 'create?!' );
271 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
272 $updater->saveRevision( $summary );
273 $status = $updater->getStatus();
274
275 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
276 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
277 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
278 $this->assertTrue( $status->hasMessage( 'edit-already-exists' ), 'edit-conflict' );
279
280 // start editing existing page
281 $page = WikiPage::factory( $title );
282 $updater = $page->newPageUpdater( $user );
283 $updater->grabParentRevision();
284
285 // update page concurrently
286 $concurrentPage = WikiPage::factory( $title );
287 $this->createRevision( $concurrentPage, __METHOD__ . '-two' );
288
289 // try creating the page - should trigger CAS failure.
290 $summary = CommentStoreComment::newUnsavedComment( 'edit?!' );
291 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
292 $updater->saveRevision( $summary );
293 $status = $updater->getStatus();
294
295 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
296 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
297 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
298 $this->assertTrue( $status->hasMessage( 'edit-conflict' ), 'edit-conflict' );
299 }
300
301 /**
302 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
303 */
304 public function testFailureOnEditFlags() {
305 $user = $this->getTestUser()->getUser();
306
307 $title = $this->getDummyTitle( __METHOD__ );
308
309 // start editing non-existing page
310 $page = WikiPage::factory( $title );
311 $updater = $page->newPageUpdater( $user );
312
313 // update with EDIT_UPDATE flag should fail
314 $summary = CommentStoreComment::newUnsavedComment( 'udpate?!' );
315 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
316 $updater->saveRevision( $summary, EDIT_UPDATE );
317 $status = $updater->getStatus();
318
319 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
320 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
321 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
322 $this->assertTrue( $status->hasMessage( 'edit-gone-missing' ), 'edit-gone-missing' );
323
324 // create the page
325 $this->createRevision( $page, __METHOD__ );
326
327 // update with EDIT_NEW flag should fail
328 $summary = CommentStoreComment::newUnsavedComment( 'create?!' );
329 $updater = $page->newPageUpdater( $user );
330 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
331 $updater->saveRevision( $summary, EDIT_NEW );
332 $status = $updater->getStatus();
333
334 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
335 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
336 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
337 $this->assertTrue( $status->hasMessage( 'edit-already-exists' ), 'edit-already-exists' );
338 }
339
340 public function provideSetRcPatrolStatus( $patrolled ) {
341 yield [ RecentChange::PRC_UNPATROLLED ];
342 yield [ RecentChange::PRC_AUTOPATROLLED ];
343 }
344
345 /**
346 * @dataProvider provideSetRcPatrolStatus
347 * @covers \MediaWiki\Storage\PageUpdater::setRcPatrolStatus()
348 */
349 public function testSetRcPatrolStatus( $patrolled ) {
350 $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
351
352 $user = $this->getTestUser()->getUser();
353
354 $title = $this->getDummyTitle( __METHOD__ );
355
356 $page = WikiPage::factory( $title );
357 $updater = $page->newPageUpdater( $user );
358
359 $summary = CommentStoreComment::newUnsavedComment( 'Lorem ipsum ' . $patrolled );
360 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum ' . $patrolled ) );
361 $updater->setRcPatrolStatus( $patrolled );
362 $rev = $updater->saveRevision( $summary );
363
364 $rc = $revisionStore->getRecentChange( $rev );
365 $this->assertEquals( $patrolled, $rc->getAttribute( 'rc_patrolled' ) );
366 }
367
368 /**
369 * @covers \MediaWiki\Storage\PageUpdater::inheritSlot()
370 * @covers \MediaWiki\Storage\PageUpdater::setContent()
371 */
372 public function testInheritSlot() {
373 $user = $this->getTestUser()->getUser();
374 $title = $this->getDummyTitle( __METHOD__ );
375 $page = WikiPage::factory( $title );
376
377 $updater = $page->newPageUpdater( $user );
378 $summary = CommentStoreComment::newUnsavedComment( 'one' );
379 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
380 $rev1 = $updater->saveRevision( $summary, EDIT_NEW );
381
382 $updater = $page->newPageUpdater( $user );
383 $summary = CommentStoreComment::newUnsavedComment( 'two' );
384 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Foo Bar' ) );
385 $rev2 = $updater->saveRevision( $summary, EDIT_UPDATE );
386
387 $updater = $page->newPageUpdater( $user );
388 $summary = CommentStoreComment::newUnsavedComment( 'three' );
389 $updater->inheritSlot( $rev1->getSlot( SlotRecord::MAIN ) );
390 $rev3 = $updater->saveRevision( $summary, EDIT_UPDATE );
391
392 $this->assertNotSame( $rev1->getId(), $rev3->getId() );
393 $this->assertNotSame( $rev2->getId(), $rev3->getId() );
394
395 $main1 = $rev1->getSlot( SlotRecord::MAIN );
396 $main3 = $rev3->getSlot( SlotRecord::MAIN );
397
398 $this->assertNotSame( $main1->getRevision(), $main3->getRevision() );
399 $this->assertSame( $main1->getAddress(), $main3->getAddress() );
400 $this->assertTrue( $main1->getContent()->equals( $main3->getContent() ) );
401 }
402
403 // TODO: MCR: test adding multiple slots, inheriting parent slots, and removing slots.
404
405 public function testSetUseAutomaticEditSummaries() {
406 $this->setContentLang( 'qqx' );
407 $user = $this->getTestUser()->getUser();
408
409 $title = $this->getDummyTitle( __METHOD__ );
410 $page = WikiPage::factory( $title );
411
412 $updater = $page->newPageUpdater( $user );
413 $updater->setUseAutomaticEditSummaries( true );
414 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
415
416 // empty comment triggers auto-summary
417 $summary = CommentStoreComment::newUnsavedComment( '' );
418 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
419
420 $rev = $updater->getNewRevision();
421 $comment = $rev->getComment( RevisionRecord::RAW );
422 $this->assertSame( '(autosumm-new: Lorem Ipsum)', $comment->text, 'comment text' );
423
424 // check that this also works when blanking the page
425 $updater = $page->newPageUpdater( $user );
426 $updater->setUseAutomaticEditSummaries( true );
427 $updater->setContent( SlotRecord::MAIN, new TextContent( '' ) );
428
429 $summary = CommentStoreComment::newUnsavedComment( '' );
430 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
431
432 $rev = $updater->getNewRevision();
433 $comment = $rev->getComment( RevisionRecord::RAW );
434 $this->assertSame( '(autosumm-blank)', $comment->text, 'comment text' );
435
436 // check that we can also disable edit-summaries
437 $title2 = $this->getDummyTitle( __METHOD__ . '/2' );
438 $page2 = WikiPage::factory( $title2 );
439
440 $updater = $page2->newPageUpdater( $user );
441 $updater->setUseAutomaticEditSummaries( false );
442 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
443
444 $summary = CommentStoreComment::newUnsavedComment( '' );
445 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
446
447 $rev = $updater->getNewRevision();
448 $comment = $rev->getComment( RevisionRecord::RAW );
449 $this->assertSame( '', $comment->text, 'comment text should still be lank' );
450
451 // check that we don't do auto.summaries without the EDIT_AUTOSUMMARY flag
452 $updater = $page2->newPageUpdater( $user );
453 $updater->setUseAutomaticEditSummaries( true );
454 $updater->setContent( SlotRecord::MAIN, new TextContent( '' ) );
455
456 $summary = CommentStoreComment::newUnsavedComment( '' );
457 $updater->saveRevision( $summary, 0 );
458
459 $rev = $updater->getNewRevision();
460 $comment = $rev->getComment( RevisionRecord::RAW );
461 $this->assertSame( '', $comment->text, 'comment text' );
462 }
463
464 public function provideSetUsePageCreationLog() {
465 yield [ true, [ [ 'create', 'create' ] ] ];
466 yield [ false, [] ];
467 }
468
469 /**
470 * @dataProvider provideSetUsePageCreationLog
471 * @param bool $use
472 */
473 public function testSetUsePageCreationLog( $use, $expected ) {
474 $user = $this->getTestUser()->getUser();
475 $title = $this->getDummyTitle( __METHOD__ . ( $use ? '_logged' : '_unlogged' ) );
476 $page = WikiPage::factory( $title );
477
478 $updater = $page->newPageUpdater( $user );
479 $updater->setUsePageCreationLog( $use );
480 $summary = CommentStoreComment::newUnsavedComment( 'cmt' );
481 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
482 $updater->saveRevision( $summary, EDIT_NEW );
483
484 $rev = $updater->getNewRevision();
485 $this->assertSelect(
486 'logging',
487 [ 'log_type', 'log_action' ],
488 [ 'log_page' => $rev->getPageId() ],
489 $expected
490 );
491 }
492
493 public function provideMagicWords() {
494 yield 'PAGEID' => [
495 'Test {{PAGEID}} Test',
496 function ( RevisionRecord $rev ) {
497 return $rev->getPageId();
498 }
499 ];
500
501 yield 'REVISIONID' => [
502 'Test {{REVISIONID}} Test',
503 function ( RevisionRecord $rev ) {
504 return $rev->getId();
505 }
506 ];
507
508 yield 'REVISIONUSER' => [
509 'Test {{REVISIONUSER}} Test',
510 function ( RevisionRecord $rev ) {
511 return $rev->getUser()->getName();
512 }
513 ];
514
515 yield 'REVISIONTIMESTAMP' => [
516 'Test {{REVISIONTIMESTAMP}} Test',
517 function ( RevisionRecord $rev ) {
518 return $rev->getTimestamp();
519 }
520 ];
521
522 yield 'subst:REVISIONUSER' => [
523 'Test {{subst:REVISIONUSER}} Test',
524 function ( RevisionRecord $rev ) {
525 return $rev->getUser()->getName();
526 },
527 'subst'
528 ];
529
530 yield 'subst:PAGENAME' => [
531 'Test {{subst:PAGENAME}} Test',
532 function ( RevisionRecord $rev ) {
533 return 'PageUpdaterTest::testMagicWords';
534 },
535 'subst'
536 ];
537 }
538
539 /**
540 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
541 *
542 * Integration test for PageUpdater, DerivedPageDataUpdater, RevisionRenderer
543 * and RenderedRevision, that ensures that magic words depending on revision meta-data
544 * are handled correctly. Note that each magic word needs to be tested separately,
545 * to assert correct behavior for each "vary" flag in the ParserOutput.
546 *
547 * @dataProvider provideMagicWords
548 */
549 public function testMagicWords( $wikitext, $callback, $subst = false ) {
550 $user = User::newFromName( 'A user for ' . __METHOD__ );
551 $user->addToDatabase();
552
553 $title = $this->getDummyTitle( __METHOD__ . '-' . $this->getName() );
554 $this->insertPage( $title );
555
556 $page = WikiPage::factory( $title );
557 $updater = $page->newPageUpdater( $user );
558
559 $updater->setContent( SlotRecord::MAIN, new \WikitextContent( $wikitext ) );
560
561 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
562 $rev = $updater->saveRevision( $summary, EDIT_UPDATE );
563
564 if ( !$rev ) {
565 $this->fail( $updater->getStatus()->getWikiText() );
566 }
567
568 $expected = strval( $callback( $rev ) );
569
570 $output = $page->getParserOutput( ParserOptions::newCanonical( 'canonical' ) );
571 $html = $output->getText();
572 $text = $rev->getContent( SlotRecord::MAIN )->serialize();
573
574 if ( $subst ) {
575 $this->assertContains( $expected, $text, 'In Wikitext' );
576 }
577
578 $this->assertContains( $expected, $html, 'In HTML' );
579 }
580
581 }