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 MediaWikiTestCase
{
15 * @dataProvider provideExtractSectionTitle
17 function testExtractSectionTitle( $section, $title ) {
18 $extracted = EditPage
::extractSectionTitle( $section );
19 $this->assertEquals( $title, $extracted );
22 public static function provideExtractSectionTitle() {
25 "== Test ==\n\nJust a test section.",
29 "An initial section, no header.",
33 "An initial section with a fake heder (bug 32617)\n\n== Test == ??\nwtf",
37 "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
41 "== Section== \t\r\n followed by whitespace (bug 35051)",
47 protected function forceRevisionDate( WikiPage
$page, $timestamp ) {
48 $dbw = wfGetDB( DB_MASTER
);
50 $dbw->update( 'revision',
51 array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
52 array( 'rev_id' => $page->getLatest() ) );
58 * User input text is passed to rtrim() by edit page. This is a simple
59 * wrapper around assertEquals() which calls rrtrim() to normalize the
60 * expected and actual texts.
62 function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
63 return $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
67 * Performs an edit and checks the result.
69 * @param String|Title $title The title of the page to edit
70 * @param String|null $baseText Some text to create the page with before attempting the edit.
71 * @param User|String|null $user The user to perform the edit as.
72 * @param array $edit An array of request parameters used to define the edit to perform.
73 * Some well known fields are:
74 * * wpTextbox1: the text to submit
75 * * wpSummary: the edit summary
76 * * wpEditToken: the edit token (will be inserted if not provided)
77 * * wpEdittime: timestamp of the edit's base revision (will be inserted if not provided)
78 * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
79 * * wpSectionTitle: the section to edit
80 * * wpMinorEdit: mark as minor edit
81 * * wpWatchthis: whether to watch the page
82 * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
83 * Set to null to skip the check. Defaults to EditPage::AS_OK.
84 * @param String|null $expectedText The text expected to be on the page after the edit.
85 * Set to null to skip the check.
86 * @param String|null $message An optional message to show along with any error message.
88 * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
90 protected function assertEdit( $title, $baseText, $user = null, array $edit,
91 $expectedCode = EditPage
::AS_OK
, $expectedText = null, $message = null
93 if ( is_string( $title ) ) {
94 $ns = $this->getDefaultWikitextNS();
95 $title = Title
::newFromText( $title, $ns );
98 if ( is_string( $user ) ) {
99 $user = User
::newFromName( $user );
101 if ( $user->getId() === 0 ) {
102 $user->addToDatabase();
106 $page = WikiPage
::factory( $title );
108 if ( $baseText !== null ) {
109 $content = ContentHandler
::makeContent( $baseText, $title );
110 $page->doEditContent( $content, "base text for test" );
111 $this->forceRevisionDate( $page, '20120101000000' );
115 $currentText = ContentHandler
::getContentText( $page->getContent() );
117 # EditPage rtrim() the user input, so we alter our expected text
119 $this->assertEditedTextEquals( $baseText, $currentText );
122 if ( $user == null ) {
123 $user = $GLOBALS['wgUser'];
125 $this->setMwGlobals( 'wgUser', $user );
128 if ( !isset( $edit['wpEditToken'] ) ) {
129 $edit['wpEditToken'] = $user->getEditToken();
132 if ( !isset( $edit['wpEdittime'] ) ) {
133 $edit['wpEdittime'] = $page->exists() ?
$page->getTimestamp() : '';
136 if ( !isset( $edit['wpStarttime'] ) ) {
137 $edit['wpStarttime'] = wfTimestampNow();
140 $req = new FauxRequest( $edit, true ); // session ??
142 $ep = new EditPage( new Article( $title ) );
143 $ep->setContextTitle( $title );
144 $ep->importFormData( $req );
146 $bot = isset( $edit['bot'] ) ?
(bool)$edit['bot'] : false;
148 // this is where the edit happens!
149 // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
150 // and throws exceptions like PermissionsError
151 $status = $ep->internalAttemptSave( $result, $bot );
153 if ( $expectedCode !== null ) {
155 $this->assertEquals( $expectedCode, $status->value
,
156 "Expected result code mismatch. $message" );
159 $page = WikiPage
::factory( $title );
161 if ( $expectedText !== null ) {
162 // check resulting page text
163 $content = $page->getContent();
164 $text = ContentHandler
::getContentText( $content );
166 # EditPage rtrim() the user input, so we alter our expected text
168 $this->assertEditedTextEquals( $expectedText, $text,
169 "Expected article text mismatch. $message" );
175 public function testCreatePage() {
176 $text = "Hello World!";
178 'wpTextbox1' => $text,
179 'wpSummary' => 'just testing',
182 $this->assertEdit( 'EditPageTest_testCreatePafe', null, null, $edit,
183 EditPage
::AS_SUCCESS_NEW_ARTICLE
, $text,
184 "expected successfull creation with given text" );
187 public function testUpdatePage() {
190 'wpTextbox1' => $text,
191 'wpSummary' => 'first update',
194 $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
195 EditPage
::AS_SUCCESS_UPDATE
, $text,
196 "expected successfull update with given text" );
198 $this->forceRevisionDate( $page, '20120101000000' );
202 'wpTextbox1' => $text,
203 'wpSummary' => 'second update',
206 $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
207 EditPage
::AS_SUCCESS_UPDATE
, $text,
208 "expected successfull update with given text" );
211 public static function provideSectionEdit() {
221 $sectionOne = '== one ==
225 $newSection = '== new section ==
230 $textWithNewSectionOne = preg_replace(
231 '/== one ==.*== two ==/ms',
232 "$sectionOne\n== two ==", $text
235 $textWithNewSectionAdded = "$text\n$newSection";
250 'replace first section',
251 $textWithNewSectionOne,
259 $textWithNewSectionAdded,
265 * @dataProvider provideSectionEdit
267 public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
269 'wpTextbox1' => $text,
270 'wpSummary' => $summary,
271 'wpSection' => $section,
274 $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
275 EditPage
::AS_SUCCESS_UPDATE
, $expected,
276 "expected successfull update of section" );
279 public static function provideAutoMerge() {
282 $tests[] = array( #0: plain conflict
283 "Elmo", # base edit user
284 "one\n\ntwo\n\nthree\n",
287 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
291 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
293 EditPage
::AS_CONFLICT_DETECTED
, # expected code
294 "ONE\n\ntwo\n\nthree\n", # expected text
295 'expected edit conflict', # message
298 $tests[] = array( #1: successful merge
299 "Elmo", # base edit user
300 "one\n\ntwo\n\nthree\n",
303 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
307 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
309 EditPage
::AS_SUCCESS_UPDATE
, # expected code
310 "ONE\n\ntwo\n\nTHREE\n", # expected text
311 'expected automatic merge', # message
315 $text .= "== first section ==\n\n";
316 $text .= "one\n\ntwo\n\nthree\n\n";
317 $text .= "== second section ==\n\n";
318 $text .= "four\n\nfive\n\nsix\n\n";
320 // extract the first section.
321 $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
323 // generate expected text after merge
324 $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
326 $tests[] = array( #2: merge in section
327 "Elmo", # base edit user
331 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
336 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
339 EditPage
::AS_SUCCESS_UPDATE
, # expected code
340 $expected, # expected text
341 'expected automatic section merge', # message
344 // see whether it makes a difference who did the base edit
345 $testsWithAdam = array_map( function ( $test ) {
346 $test[0] = 'Adam'; // change base edit user
350 $testsWithBerta = array_map( function ( $test ) {
351 $test[0] = 'Berta'; // change base edit user
355 return array_merge( $tests, $testsWithAdam, $testsWithBerta );
359 * @dataProvider provideAutoMerge
361 public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
362 $expectedCode, $expectedText, $message = null
364 $this->checkHasDiff3();
367 $ns = $this->getDefaultWikitextNS();
368 $title = Title
::newFromText( 'EditPageTest_testAutoMerge', $ns );
369 $page = WikiPage
::factory( $title );
371 if ( $page->exists() ) {
372 $page->doDeleteArticle( "clean slate for testing" );
376 'wpTextbox1' => $text,
379 $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
380 $baseUser, $baseEdit, null, null, __METHOD__
);
382 $this->forceRevisionDate( $page, '20120101000000' );
384 $edittime = $page->getTimestamp();
386 // start timestamps for conflict detection
387 if ( !isset( $adamsEdit['wpStarttime'] ) ) {
388 $adamsEdit['wpStarttime'] = 1;
391 if ( !isset( $bertasEdit['wpStarttime'] ) ) {
392 $bertasEdit['wpStarttime'] = 2;
395 $starttime = wfTimestampNow();
396 $adamsTime = wfTimestamp( TS_MW
, (int)wfTimestamp( TS_UNIX
, $starttime ) +
(int)$adamsEdit['wpStarttime'] );
397 $bertasTime = wfTimestamp( TS_MW
, (int)wfTimestamp( TS_UNIX
, $starttime ) +
(int)$bertasEdit['wpStarttime'] );
399 $adamsEdit['wpStarttime'] = $adamsTime;
400 $bertasEdit['wpStarttime'] = $bertasTime;
402 $adamsEdit['wpSummary'] = 'Adam\'s edit';
403 $bertasEdit['wpSummary'] = 'Bertas\'s edit';
405 $adamsEdit['wpEdittime'] = $edittime;
406 $bertasEdit['wpEdittime'] = $edittime;
409 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
410 EditPage
::AS_SUCCESS_UPDATE
, null, "expected successfull update" );
413 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
414 $expectedCode, $expectedText, $message );