4 * Tests for MediaWiki api.php?action=edit.
6 * @author Daniel Kinzler
14 class ApiEditPageTest
extends ApiTestCase
{
16 protected function setUp() {
17 global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
21 $this->setMwGlobals( array(
22 'wgExtraNamespaces' => $wgExtraNamespaces,
23 'wgNamespaceContentModels' => $wgNamespaceContentModels,
24 'wgContentHandlers' => $wgContentHandlers,
25 'wgContLang' => $wgContLang,
28 $wgExtraNamespaces[12312] = 'Dummy';
29 $wgExtraNamespaces[12313] = 'Dummy_talk';
31 $wgNamespaceContentModels[12312] = "testing";
32 $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
34 MWNamespace
::getCanonicalNamespaces( true ); # reset namespace cache
35 $wgContLang->resetNamespaces(); # reset namespace cache
40 protected function tearDown() {
41 MWNamespace
::getCanonicalNamespaces( true ); # reset namespace cache
45 public function testEdit() {
46 $name = 'Help:ApiEditPageTest_testEdit'; // assume Help namespace to default to wikitext
48 // -- test new page --------------------------------------------
49 $apiResult = $this->doApiRequestWithToken( array(
52 'text' => 'some text',
54 $apiResult = $apiResult[0];
56 // Validate API result data
57 $this->assertArrayHasKey( 'edit', $apiResult );
58 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
59 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
61 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
62 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
64 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
66 // -- test existing page, no change ----------------------------
67 $data = $this->doApiRequestWithToken( array(
70 'text' => 'some text',
73 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
75 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
76 $this->assertArrayHasKey( 'nochange', $data[0]['edit'] );
78 // -- test existing page, with change --------------------------
79 $data = $this->doApiRequestWithToken( array(
82 'text' => 'different text'
85 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
87 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
88 $this->assertArrayNotHasKey( 'nochange', $data[0]['edit'] );
90 $this->assertArrayHasKey( 'oldrevid', $data[0]['edit'] );
91 $this->assertArrayHasKey( 'newrevid', $data[0]['edit'] );
92 $this->assertNotEquals(
93 $data[0]['edit']['newrevid'],
94 $data[0]['edit']['oldrevid'],
95 "revision id should change after edit"
99 public function testNonTextEdit() {
100 $name = 'Dummy:ApiEditPageTest_testNonTextEdit';
101 $data = serialize( 'some bla bla text' );
103 // -- test new page --------------------------------------------
104 $apiResult = $this->doApiRequestWithToken( array(
107 'text' => $data, ) );
108 $apiResult = $apiResult[0];
110 // Validate API result data
111 $this->assertArrayHasKey( 'edit', $apiResult );
112 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
113 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
115 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
116 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
118 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
120 // validate resulting revision
121 $page = WikiPage
::factory( Title
::newFromText( $name ) );
122 $this->assertEquals( "testing", $page->getContentModel() );
123 $this->assertEquals( $data, $page->getContent()->serialize() );
129 public static function provideEditAppend() {
132 'foo', 'append', 'bar', "foobar"
135 'foo', 'prepend', 'bar', "barfoo"
137 array( #2: append to empty page
138 '', 'append', 'foo', "foo"
140 array( #3: prepend to empty page
141 '', 'prepend', 'foo', "foo"
143 array( #4: append to non-existing page
144 null, 'append', 'foo', "foo"
146 array( #5: prepend to non-existing page
147 null, 'prepend', 'foo', "foo"
153 * @dataProvider provideEditAppend
155 public function testEditAppend( $text, $op, $append, $expected ) {
159 // assume NS_HELP defaults to wikitext
160 $name = "Help:ApiEditPageTest_testEditAppend_$count";
162 // -- create page (or not) -----------------------------------------
163 if ( $text !== null ) {
164 list( $re ) = $this->doApiRequestWithToken( array(
167 'text' => $text, ) );
169 $this->assertEquals( 'Success', $re['edit']['result'] ); // sanity
172 // -- try append/prepend --------------------------------------------
173 list( $re ) = $this->doApiRequestWithToken( array(
176 $op . 'text' => $append, ) );
178 $this->assertEquals( 'Success', $re['edit']['result'] );
180 // -- validate -----------------------------------------------------
181 $page = new WikiPage( Title
::newFromText( $name ) );
182 $content = $page->getContent();
183 $this->assertNotNull( $content, 'Page should have been created' );
185 $text = $content->getNativeData();
187 $this->assertEquals( $expected, $text );
191 * Test editing of sections
193 public function testEditSection() {
194 $name = 'Help:ApiEditPageTest_testEditSection';
195 $page = WikiPage
::factory( Title
::newFromText( $name ) );
196 $text = "==section 1==\ncontent 1\n==section 2==\ncontent2";
197 // Preload the page with some text
198 $page->doEditContent( ContentHandler
::makeContent( $text, $page->getTitle() ), 'summary' );
200 list( $re ) = $this->doApiRequestWithToken( array(
204 'text' => "==section 1==\nnew content 1",
206 $this->assertEquals( 'Success', $re['edit']['result'] );
207 $newtext = WikiPage
::factory( Title
::newFromText( $name ) )
208 ->getContent( Revision
::RAW
)
210 $this->assertEquals( "==section 1==\nnew content 1\n\n==section 2==\ncontent2", $newtext );
212 // Test that we raise a 'nosuchsection' error
214 $this->doApiRequestWithToken( array(
220 $this->fail( "Should have raised a UsageException" );
221 } catch ( UsageException
$e ) {
222 $this->assertEquals( 'nosuchsection', $e->getCodeString() );
227 * Test action=edit§ion=new
228 * Run it twice so we test adding a new section on a
229 * page that doesn't exist (bug 52830) and one that
232 public function testEditNewSection() {
233 $name = 'Help:ApiEditPageTest_testEditNewSection';
235 // Test on a page that does not already exist
236 $this->assertFalse( Title
::newFromText( $name )->exists() );
237 list( $re ) = $this->doApiRequestWithToken( array(
242 'summary' => 'header',
245 $this->assertEquals( 'Success', $re['edit']['result'] );
246 // Check the page text is correct
247 $text = WikiPage
::factory( Title
::newFromText( $name ) )
248 ->getContent( Revision
::RAW
)
250 $this->assertEquals( "== header ==\n\ntest", $text );
252 // Now on one that does
253 $this->assertTrue( Title
::newFromText( $name )->exists() );
254 list( $re2 ) = $this->doApiRequestWithToken( array(
259 'summary' => 'header',
262 $this->assertEquals( 'Success', $re2['edit']['result'] );
263 $text = WikiPage
::factory( Title
::newFromText( $name ) )
264 ->getContent( Revision
::RAW
)
266 $this->assertEquals( "== header ==\n\ntest\n\n== header ==\n\ntest", $text );
270 * Ensure we can edit through a redirect, if adding a section
272 public function testEdit_redirect() {
276 // assume NS_HELP defaults to wikitext
277 $name = "Help:ApiEditPageTest_testEdit_redirect_$count";
278 $title = Title
::newFromText( $name );
279 $page = WikiPage
::factory( $title );
281 $rname = "Help:ApiEditPageTest_testEdit_redirect_r$count";
282 $rtitle = Title
::newFromText( $rname );
283 $rpage = WikiPage
::factory( $rtitle );
285 // base edit for content
286 $page->doEditContent( new WikitextContent( "Foo" ),
287 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
288 $this->forceRevisionDate( $page, '20120101000000' );
289 $baseTime = $page->getRevision()->getTimestamp();
291 // base edit for redirect
292 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
293 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
294 $this->forceRevisionDate( $rpage, '20120101000000' );
296 // conflicting edit to redirect
297 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ),
298 "testing 2", EDIT_UPDATE
, $page->getLatest(), self
::$users['uploader']->user
);
299 $this->forceRevisionDate( $rpage, '20120101020202' );
301 // try to save edit, following the redirect
302 list( $re, , ) = $this->doApiRequestWithToken( array(
305 'text' => 'nix bar!',
306 'basetimestamp' => $baseTime,
309 ), null, self
::$users['sysop']->user
);
311 $this->assertEquals( 'Success', $re['edit']['result'],
312 "no problems expected when following redirect" );
316 * Ensure we cannot edit through a redirect, if attempting to overwrite content
318 public function testEdit_redirectText() {
322 // assume NS_HELP defaults to wikitext
323 $name = "Help:ApiEditPageTest_testEdit_redirectText_$count";
324 $title = Title
::newFromText( $name );
325 $page = WikiPage
::factory( $title );
327 $rname = "Help:ApiEditPageTest_testEdit_redirectText_r$count";
328 $rtitle = Title
::newFromText( $rname );
329 $rpage = WikiPage
::factory( $rtitle );
331 // base edit for content
332 $page->doEditContent( new WikitextContent( "Foo" ),
333 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
334 $this->forceRevisionDate( $page, '20120101000000' );
335 $baseTime = $page->getRevision()->getTimestamp();
337 // base edit for redirect
338 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
339 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
340 $this->forceRevisionDate( $rpage, '20120101000000' );
342 // conflicting edit to redirect
343 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ),
344 "testing 2", EDIT_UPDATE
, $page->getLatest(), self
::$users['uploader']->user
);
345 $this->forceRevisionDate( $rpage, '20120101020202' );
347 // try to save edit, following the redirect but without creating a section
349 $this->doApiRequestWithToken( array(
352 'text' => 'nix bar!',
353 'basetimestamp' => $baseTime,
355 ), null, self
::$users['sysop']->user
);
357 $this->fail( 'redirect-appendonly error expected' );
358 } catch ( UsageException
$ex ) {
359 $this->assertEquals( 'redirect-appendonly', $ex->getCodeString() );
363 public function testEditConflict() {
367 // assume NS_HELP defaults to wikitext
368 $name = "Help:ApiEditPageTest_testEditConflict_$count";
369 $title = Title
::newFromText( $name );
371 $page = WikiPage
::factory( $title );
374 $page->doEditContent( new WikitextContent( "Foo" ),
375 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
376 $this->forceRevisionDate( $page, '20120101000000' );
377 $baseTime = $page->getRevision()->getTimestamp();
380 $page->doEditContent( new WikitextContent( "Foo bar" ),
381 "testing 2", EDIT_UPDATE
, $page->getLatest(), self
::$users['uploader']->user
);
382 $this->forceRevisionDate( $page, '20120101020202' );
384 // try to save edit, expect conflict
386 $this->doApiRequestWithToken( array(
389 'text' => 'nix bar!',
390 'basetimestamp' => $baseTime,
391 ), null, self
::$users['sysop']->user
);
393 $this->fail( 'edit conflict expected' );
394 } catch ( UsageException
$ex ) {
395 $this->assertEquals( 'editconflict', $ex->getCodeString() );
400 * Ensure that editing using section=new will prevent simple conflicts
402 public function testEditConflict_newSection() {
406 // assume NS_HELP defaults to wikitext
407 $name = "Help:ApiEditPageTest_testEditConflict_newSection_$count";
408 $title = Title
::newFromText( $name );
410 $page = WikiPage
::factory( $title );
413 $page->doEditContent( new WikitextContent( "Foo" ),
414 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
415 $this->forceRevisionDate( $page, '20120101000000' );
416 $baseTime = $page->getRevision()->getTimestamp();
419 $page->doEditContent( new WikitextContent( "Foo bar" ),
420 "testing 2", EDIT_UPDATE
, $page->getLatest(), self
::$users['uploader']->user
);
421 $this->forceRevisionDate( $page, '20120101020202' );
423 // try to save edit, expect no conflict
424 list( $re, , ) = $this->doApiRequestWithToken( array(
427 'text' => 'nix bar!',
428 'basetimestamp' => $baseTime,
430 ), null, self
::$users['sysop']->user
);
432 $this->assertEquals( 'Success', $re['edit']['result'],
433 "no edit conflict expected here" );
436 public function testEditConflict_bug41990() {
441 * bug 41990: if the target page has a newer revision than the redirect, then editing the
442 * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erroneously
443 * caused an edit conflict to be detected.
446 // assume NS_HELP defaults to wikitext
447 $name = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_$count";
448 $title = Title
::newFromText( $name );
449 $page = WikiPage
::factory( $title );
451 $rname = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_r$count";
452 $rtitle = Title
::newFromText( $rname );
453 $rpage = WikiPage
::factory( $rtitle );
455 // base edit for content
456 $page->doEditContent( new WikitextContent( "Foo" ),
457 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
458 $this->forceRevisionDate( $page, '20120101000000' );
460 // base edit for redirect
461 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
462 "testing 1", EDIT_NEW
, false, self
::$users['sysop']->user
);
463 $this->forceRevisionDate( $rpage, '20120101000000' );
465 // new edit to content
466 $page->doEditContent( new WikitextContent( "Foo bar" ),
467 "testing 2", EDIT_UPDATE
, $page->getLatest(), self
::$users['uploader']->user
);
468 $this->forceRevisionDate( $rpage, '20120101020202' );
470 // try to save edit; should work, following the redirect.
471 list( $re, , ) = $this->doApiRequestWithToken( array(
474 'text' => 'nix bar!',
477 ), null, self
::$users['sysop']->user
);
479 $this->assertEquals( 'Success', $re['edit']['result'],
480 "no edit conflict expected here" );
484 * @param WikiPage $page
485 * @param string|int $timestamp
487 protected function forceRevisionDate( WikiPage
$page, $timestamp ) {
488 $dbw = wfGetDB( DB_MASTER
);
490 $dbw->update( 'revision',
491 array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
492 array( 'rev_id' => $page->getLatest() ) );