7 * ^--- tell jenkins this test needs the database
10 * ^--- tell phpunit that these test cases may take longer than 2 seconds.
12 class EditPageTest
extends MediaWikiLangTestCase
{
15 * @dataProvider provideExtractSectionTitle
16 * @covers EditPage::extractSectionTitle
18 public function testExtractSectionTitle( $section, $title ) {
19 $extracted = EditPage
::extractSectionTitle( $section );
20 $this->assertEquals( $title, $extracted );
23 public static function provideExtractSectionTitle() {
26 "== Test ==\n\nJust a test section.",
30 "An initial section, no header.",
34 "An initial section with a fake heder (bug 32617)\n\n== Test == ??\nwtf",
38 "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
42 "== Section== \t\r\n followed by whitespace (bug 35051)",
48 protected function forceRevisionDate( WikiPage
$page, $timestamp ) {
49 $dbw = wfGetDB( DB_MASTER
);
51 $dbw->update( 'revision',
52 array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
53 array( 'rev_id' => $page->getLatest() ) );
59 * User input text is passed to rtrim() by edit page. This is a simple
60 * wrapper around assertEquals() which calls rrtrim() to normalize the
61 * expected and actual texts.
62 * @param string $expected
63 * @param string $actual
66 protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
67 return $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
71 * Performs an edit and checks the result.
73 * @param string|Title $title The title of the page to edit
74 * @param string|null $baseText Some text to create the page with before attempting the edit.
75 * @param User|string|null $user The user to perform the edit as.
76 * @param array $edit An array of request parameters used to define the edit to perform.
77 * Some well known fields are:
78 * * wpTextbox1: the text to submit
79 * * wpSummary: the edit summary
80 * * wpEditToken: the edit token (will be inserted if not provided)
81 * * wpEdittime: timestamp of the edit's base revision (will be inserted
83 * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
84 * * wpSectionTitle: the section to edit
85 * * wpMinorEdit: mark as minor edit
86 * * wpWatchthis: whether to watch the page
87 * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
88 * Set to null to skip the check.
89 * @param string|null $expectedText The text expected to be on the page after the edit.
90 * Set to null to skip the check.
91 * @param string|null $message An optional message to show along with any error message.
93 * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
95 protected function assertEdit( $title, $baseText, $user = null, array $edit,
96 $expectedCode = null, $expectedText = null, $message = null
98 if ( is_string( $title ) ) {
99 $ns = $this->getDefaultWikitextNS();
100 $title = Title
::newFromText( $title, $ns );
102 $this->assertNotNull( $title );
104 if ( is_string( $user ) ) {
105 $user = User
::newFromName( $user );
107 if ( $user->getId() === 0 ) {
108 $user->addToDatabase();
112 $page = WikiPage
::factory( $title );
114 if ( $baseText !== null ) {
115 $content = ContentHandler
::makeContent( $baseText, $title );
116 $page->doEditContent( $content, "base text for test" );
117 $this->forceRevisionDate( $page, '20120101000000' );
121 $currentText = ContentHandler
::getContentText( $page->getContent() );
123 # EditPage rtrim() the user input, so we alter our expected text
125 $this->assertEditedTextEquals( $baseText, $currentText );
128 if ( $user == null ) {
129 $user = $GLOBALS['wgUser'];
131 $this->setMwGlobals( 'wgUser', $user );
134 if ( !isset( $edit['wpEditToken'] ) ) {
135 $edit['wpEditToken'] = $user->getEditToken();
138 if ( !isset( $edit['wpEdittime'] ) ) {
139 $edit['wpEdittime'] = $page->exists() ?
$page->getTimestamp() : '';
142 if ( !isset( $edit['wpStarttime'] ) ) {
143 $edit['wpStarttime'] = wfTimestampNow();
146 $req = new FauxRequest( $edit, true ); // session ??
148 $article = new Article( $title );
149 $article->getContext()->setTitle( $title );
150 $ep = new EditPage( $article );
151 $ep->setContextTitle( $title );
152 $ep->importFormData( $req );
154 $bot = isset( $edit['bot'] ) ?
(bool)$edit['bot'] : false;
156 // this is where the edit happens!
157 // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
158 // and throws exceptions like PermissionsError
159 $status = $ep->internalAttemptSave( $result, $bot );
161 if ( $expectedCode !== null ) {
163 $this->assertEquals( $expectedCode, $status->value
,
164 "Expected result code mismatch. $message" );
167 $page = WikiPage
::factory( $title );
169 if ( $expectedText !== null ) {
170 // check resulting page text
171 $content = $page->getContent();
172 $text = ContentHandler
::getContentText( $content );
174 # EditPage rtrim() the user input, so we alter our expected text
176 $this->assertEditedTextEquals( $expectedText, $text,
177 "Expected article text mismatch. $message" );
183 public static function provideCreatePages() {
185 array( 'expected article being created',
186 'EditPageTest_testCreatePage',
189 EditPage
::AS_SUCCESS_NEW_ARTICLE
,
192 array( 'expected article not being created if empty',
193 'EditPageTest_testCreatePage',
196 EditPage
::AS_BLANK_ARTICLE
,
199 array( 'expected MediaWiki: page being created',
203 EditPage
::AS_SUCCESS_NEW_ARTICLE
,
206 array( 'expected not-registered MediaWiki: page not being created if empty',
207 'MediaWiki:EditPageTest_testCreatePage',
210 EditPage
::AS_BLANK_ARTICLE
,
213 array( 'expected registered MediaWiki: page being created even if empty',
217 EditPage
::AS_SUCCESS_NEW_ARTICLE
,
220 array( 'expected registered MediaWiki: page whose default content is empty'
221 . ' not being created if empty',
222 'MediaWiki:Ipb-default-expiry',
225 EditPage
::AS_BLANK_ARTICLE
,
228 array( 'expected MediaWiki: page not being created if text equals default message',
232 EditPage
::AS_BLANK_ARTICLE
,
235 array( 'expected empty article being created',
236 'EditPageTest_testCreatePage',
239 EditPage
::AS_SUCCESS_NEW_ARTICLE
,
247 * @dataProvider provideCreatePages
250 public function testCreatePage(
251 $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
253 $edit = array( 'wpTextbox1' => $editText );
254 if ( $ignoreBlank ) {
255 $edit['wpIgnoreBlankArticle'] = 1;
258 $page = $this->assertEdit( $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
260 if ( $expectedCode != EditPage
::AS_BLANK_ARTICLE
) {
261 $page->doDeleteArticleReal( $pageTitle );
265 public function testUpdatePage() {
268 'wpTextbox1' => $text,
269 'wpSummary' => 'first update',
272 $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
273 EditPage
::AS_SUCCESS_UPDATE
, $text,
274 "expected successfull update with given text" );
276 $this->forceRevisionDate( $page, '20120101000000' );
280 'wpTextbox1' => $text,
281 'wpSummary' => 'second update',
284 $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
285 EditPage
::AS_SUCCESS_UPDATE
, $text,
286 "expected successfull update with given text" );
289 public static function provideSectionEdit() {
299 $sectionOne = '== one ==
303 $newSection = '== new section ==
308 $textWithNewSectionOne = preg_replace(
309 '/== one ==.*== two ==/ms',
310 "$sectionOne\n== two ==", $text
313 $textWithNewSectionAdded = "$text\n$newSection";
328 'replace first section',
329 $textWithNewSectionOne,
337 $textWithNewSectionAdded,
343 * @dataProvider provideSectionEdit
346 public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
348 'wpTextbox1' => $text,
349 'wpSummary' => $summary,
350 'wpSection' => $section,
353 $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
354 EditPage
::AS_SUCCESS_UPDATE
, $expected,
355 "expected successfull update of section" );
358 public static function provideAutoMerge() {
361 $tests[] = array( #0: plain conflict
362 "Elmo", # base edit user
363 "one\n\ntwo\n\nthree\n",
366 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
370 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
372 EditPage
::AS_CONFLICT_DETECTED
, # expected code
373 "ONE\n\ntwo\n\nthree\n", # expected text
374 'expected edit conflict', # message
377 $tests[] = array( #1: successful merge
378 "Elmo", # base edit user
379 "one\n\ntwo\n\nthree\n",
382 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
386 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
388 EditPage
::AS_SUCCESS_UPDATE
, # expected code
389 "ONE\n\ntwo\n\nTHREE\n", # expected text
390 'expected automatic merge', # message
394 $text .= "== first section ==\n\n";
395 $text .= "one\n\ntwo\n\nthree\n\n";
396 $text .= "== second section ==\n\n";
397 $text .= "four\n\nfive\n\nsix\n\n";
399 // extract the first section.
400 $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
402 // generate expected text after merge
403 $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
405 $tests[] = array( #2: merge in section
406 "Elmo", # base edit user
410 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
415 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
418 EditPage
::AS_SUCCESS_UPDATE
, # expected code
419 $expected, # expected text
420 'expected automatic section merge', # message
423 // see whether it makes a difference who did the base edit
424 $testsWithAdam = array_map( function ( $test ) {
425 $test[0] = 'Adam'; // change base edit user
429 $testsWithBerta = array_map( function ( $test ) {
430 $test[0] = 'Berta'; // change base edit user
434 return array_merge( $tests, $testsWithAdam, $testsWithBerta );
438 * @dataProvider provideAutoMerge
441 public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
442 $expectedCode, $expectedText, $message = null
444 $this->checkHasDiff3();
447 $ns = $this->getDefaultWikitextNS();
448 $title = Title
::newFromText( 'EditPageTest_testAutoMerge', $ns );
449 $page = WikiPage
::factory( $title );
451 if ( $page->exists() ) {
452 $page->doDeleteArticle( "clean slate for testing" );
456 'wpTextbox1' => $text,
459 $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
460 $baseUser, $baseEdit, null, null, __METHOD__
);
462 $this->forceRevisionDate( $page, '20120101000000' );
464 $edittime = $page->getTimestamp();
466 // start timestamps for conflict detection
467 if ( !isset( $adamsEdit['wpStarttime'] ) ) {
468 $adamsEdit['wpStarttime'] = 1;
471 if ( !isset( $bertasEdit['wpStarttime'] ) ) {
472 $bertasEdit['wpStarttime'] = 2;
475 $starttime = wfTimestampNow();
476 $adamsTime = wfTimestamp(
478 (int)wfTimestamp( TS_UNIX
, $starttime ) +
(int)$adamsEdit['wpStarttime']
480 $bertasTime = wfTimestamp(
482 (int)wfTimestamp( TS_UNIX
, $starttime ) +
(int)$bertasEdit['wpStarttime']
485 $adamsEdit['wpStarttime'] = $adamsTime;
486 $bertasEdit['wpStarttime'] = $bertasTime;
488 $adamsEdit['wpSummary'] = 'Adam\'s edit';
489 $bertasEdit['wpSummary'] = 'Bertas\'s edit';
491 $adamsEdit['wpEdittime'] = $edittime;
492 $bertasEdit['wpEdittime'] = $edittime;
495 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
496 EditPage
::AS_SUCCESS_UPDATE
, null, "expected successfull update" );
499 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
500 $expectedCode, $expectedText, $message );