Merge "Add .mw-editsection-like class, behavior same as .mw-editsection"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiEditPageTest.php
1 <?php
2
3 /**
4 * Tests for MediaWiki api.php?action=edit.
5 *
6 * @author Daniel Kinzler
7 *
8 * @group API
9 * @group Database
10 * @group medium
11 *
12 * @covers ApiEditPage
13 */
14 class ApiEditPageTest extends ApiTestCase {
15
16 public function setUp() {
17 global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
18
19 parent::setUp();
20
21 $wgExtraNamespaces[12312] = 'Dummy';
22 $wgExtraNamespaces[12313] = 'Dummy_talk';
23
24 $wgNamespaceContentModels[12312] = "testing";
25 $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
26
27 MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
28 $wgContLang->resetNamespaces(); # reset namespace cache
29
30 $this->doLogin();
31 }
32
33 public function tearDown() {
34 global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
35
36 unset( $wgExtraNamespaces[12312] );
37 unset( $wgExtraNamespaces[12313] );
38
39 unset( $wgNamespaceContentModels[12312] );
40 unset( $wgContentHandlers["testing"] );
41
42 MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
43 $wgContLang->resetNamespaces(); # reset namespace cache
44
45 parent::tearDown();
46 }
47
48 public function testEdit() {
49 $name = 'Help:ApiEditPageTest_testEdit'; // assume Help namespace to default to wikitext
50
51 // -- test new page --------------------------------------------
52 $apiResult = $this->doApiRequestWithToken( array(
53 'action' => 'edit',
54 'title' => $name,
55 'text' => 'some text',
56 ) );
57 $apiResult = $apiResult[0];
58
59 // Validate API result data
60 $this->assertArrayHasKey( 'edit', $apiResult );
61 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
62 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
63
64 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
65 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
66
67 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
68
69 // -- test existing page, no change ----------------------------
70 $data = $this->doApiRequestWithToken( array(
71 'action' => 'edit',
72 'title' => $name,
73 'text' => 'some text',
74 ) );
75
76 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
77
78 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
79 $this->assertArrayHasKey( 'nochange', $data[0]['edit'] );
80
81 // -- test existing page, with change --------------------------
82 $data = $this->doApiRequestWithToken( array(
83 'action' => 'edit',
84 'title' => $name,
85 'text' => 'different text'
86 ) );
87
88 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
89
90 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
91 $this->assertArrayNotHasKey( 'nochange', $data[0]['edit'] );
92
93 $this->assertArrayHasKey( 'oldrevid', $data[0]['edit'] );
94 $this->assertArrayHasKey( 'newrevid', $data[0]['edit'] );
95 $this->assertNotEquals(
96 $data[0]['edit']['newrevid'],
97 $data[0]['edit']['oldrevid'],
98 "revision id should change after edit"
99 );
100 }
101
102 public function testNonTextEdit() {
103 $name = 'Dummy:ApiEditPageTest_testNonTextEdit';
104 $data = serialize( 'some bla bla text' );
105
106 // -- test new page --------------------------------------------
107 $apiResult = $this->doApiRequestWithToken( array(
108 'action' => 'edit',
109 'title' => $name,
110 'text' => $data, ) );
111 $apiResult = $apiResult[0];
112
113 // Validate API result data
114 $this->assertArrayHasKey( 'edit', $apiResult );
115 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
116 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
117
118 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
119 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
120
121 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
122
123 // validate resulting revision
124 $page = WikiPage::factory( Title::newFromText( $name ) );
125 $this->assertEquals( "testing", $page->getContentModel() );
126 $this->assertEquals( $data, $page->getContent()->serialize() );
127 }
128
129 public static function provideEditAppend() {
130 return array(
131 array( #0: append
132 'foo', 'append', 'bar', "foobar"
133 ),
134 array( #1: prepend
135 'foo', 'prepend', 'bar', "barfoo"
136 ),
137 array( #2: append to empty page
138 '', 'append', 'foo', "foo"
139 ),
140 array( #3: prepend to empty page
141 '', 'prepend', 'foo', "foo"
142 ),
143 array( #4: append to non-existing page
144 null, 'append', 'foo', "foo"
145 ),
146 array( #5: prepend to non-existing page
147 null, 'prepend', 'foo', "foo"
148 ),
149 );
150 }
151
152 /**
153 * @dataProvider provideEditAppend
154 */
155 public function testEditAppend( $text, $op, $append, $expected ) {
156 static $count = 0;
157 $count++;
158
159 // assume NS_HELP defaults to wikitext
160 $name = "Help:ApiEditPageTest_testEditAppend_$count";
161
162 // -- create page (or not) -----------------------------------------
163 if ( $text !== null ) {
164 if ( $text === '' ) {
165 // can't create an empty page, so create it with some content
166 $this->doApiRequestWithToken( array(
167 'action' => 'edit',
168 'title' => $name,
169 'text' => '(dummy)', ) );
170 }
171
172 list( $re ) = $this->doApiRequestWithToken( array(
173 'action' => 'edit',
174 'title' => $name,
175 'text' => $text, ) );
176
177 $this->assertEquals( 'Success', $re['edit']['result'] ); // sanity
178 }
179
180 // -- try append/prepend --------------------------------------------
181 list( $re ) = $this->doApiRequestWithToken( array(
182 'action' => 'edit',
183 'title' => $name,
184 $op . 'text' => $append, ) );
185
186 $this->assertEquals( 'Success', $re['edit']['result'] );
187
188 // -- validate -----------------------------------------------------
189 $page = new WikiPage( Title::newFromText( $name ) );
190 $content = $page->getContent();
191 $this->assertNotNull( $content, 'Page should have been created' );
192
193 $text = $content->getNativeData();
194
195 $this->assertEquals( $expected, $text );
196 }
197
198 /**
199 * Test editing of sections
200 */
201 public function testEditSection() {
202 $name = 'Help:ApiEditPageTest_testEditSection';
203 $page = WikiPage::factory( Title::newFromText( $name ) );
204 $text = "==section 1==\ncontent 1\n==section 2==\ncontent2";
205 // Preload the page with some text
206 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), 'summary' );
207
208 list( $re ) = $this->doApiRequestWithToken( array(
209 'action' => 'edit',
210 'title' => $name,
211 'section' => '1',
212 'text' => "==section 1==\nnew content 1",
213 ) );
214 $this->assertEquals( 'Success', $re['edit']['result'] );
215 $newtext = WikiPage::factory( Title::newFromText( $name ) )->getContent( Revision::RAW )->getNativeData();
216 $this->assertEquals( $newtext, "==section 1==\nnew content 1\n\n==section 2==\ncontent2" );
217
218 // Test that we raise a 'nosuchsection' error
219 try {
220 $this->doApiRequestWithToken( array(
221 'action' => 'edit',
222 'title' => $name,
223 'section' => '9999',
224 'text' => 'text',
225 ) );
226 $this->fail( "Should have raised a UsageException" );
227 } catch ( UsageException $e ) {
228 $this->assertEquals( $e->getCodeString(), 'nosuchsection' );
229 }
230 }
231
232 /**
233 * Test action=edit&section=new
234 * Run it twice so we test adding a new section on a
235 * page that doesn't exist (bug 52830) and one that
236 * does exist
237 */
238 public function testEditNewSection() {
239 $name = 'Help:ApiEditPageTest_testEditNewSection';
240
241 // Test on a page that does not already exist
242 $this->assertFalse( Title::newFromText( $name )->exists() );
243 list( $re ) = $this->doApiRequestWithToken( array(
244 'action' => 'edit',
245 'title' => $name,
246 'section' => 'new',
247 'text' => 'test',
248 'summary' => 'header',
249 ));
250
251 $this->assertEquals( 'Success', $re['edit']['result'] );
252 // Check the page text is correct
253 $text = WikiPage::factory( Title::newFromText( $name ) )->getContent( Revision::RAW )->getNativeData();
254 $this->assertEquals( $text, "== header ==\n\ntest" );
255
256 // Now on one that does
257 $this->assertTrue( Title::newFromText( $name )->exists() );
258 list( $re2 ) = $this->doApiRequestWithToken( array(
259 'action' => 'edit',
260 'title' => $name,
261 'section' => 'new',
262 'text' => 'test',
263 'summary' => 'header',
264 ));
265
266 $this->assertEquals( 'Success', $re2['edit']['result'] );
267 $text = WikiPage::factory( Title::newFromText( $name ) )->getContent( Revision::RAW )->getNativeData();
268 $this->assertEquals( $text, "== header ==\n\ntest\n\n== header ==\n\ntest" );
269 }
270
271 public function testEditConflict() {
272 static $count = 0;
273 $count++;
274
275 // assume NS_HELP defaults to wikitext
276 $name = "Help:ApiEditPageTest_testEditConflict_$count";
277 $title = Title::newFromText( $name );
278
279 $page = WikiPage::factory( $title );
280
281 // base edit
282 $page->doEditContent( new WikitextContent( "Foo" ),
283 "testing 1", EDIT_NEW, false, self::$users['sysop']->user );
284 $this->forceRevisionDate( $page, '20120101000000' );
285 $baseTime = $page->getRevision()->getTimestamp();
286
287 // conflicting edit
288 $page->doEditContent( new WikitextContent( "Foo bar" ),
289 "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user );
290 $this->forceRevisionDate( $page, '20120101020202' );
291
292 // try to save edit, expect conflict
293 try {
294 $this->doApiRequestWithToken( array(
295 'action' => 'edit',
296 'title' => $name,
297 'text' => 'nix bar!',
298 'basetimestamp' => $baseTime,
299 ), null, self::$users['sysop']->user );
300
301 $this->fail( 'edit conflict expected' );
302 } catch ( UsageException $ex ) {
303 $this->assertEquals( 'editconflict', $ex->getCodeString() );
304 }
305 }
306
307 public function testEditConflict_redirect() {
308 static $count = 0;
309 $count++;
310
311 // assume NS_HELP defaults to wikitext
312 $name = "Help:ApiEditPageTest_testEditConflict_redirect_$count";
313 $title = Title::newFromText( $name );
314 $page = WikiPage::factory( $title );
315
316 $rname = "Help:ApiEditPageTest_testEditConflict_redirect_r$count";
317 $rtitle = Title::newFromText( $rname );
318 $rpage = WikiPage::factory( $rtitle );
319
320 // base edit for content
321 $page->doEditContent( new WikitextContent( "Foo" ),
322 "testing 1", EDIT_NEW, false, self::$users['sysop']->user );
323 $this->forceRevisionDate( $page, '20120101000000' );
324 $baseTime = $page->getRevision()->getTimestamp();
325
326 // base edit for redirect
327 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
328 "testing 1", EDIT_NEW, false, self::$users['sysop']->user );
329 $this->forceRevisionDate( $rpage, '20120101000000' );
330
331 // conflicting edit to redirect
332 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ),
333 "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user );
334 $this->forceRevisionDate( $rpage, '20120101020202' );
335
336 // try to save edit; should work, because we follow the redirect
337 list( $re, , ) = $this->doApiRequestWithToken( array(
338 'action' => 'edit',
339 'title' => $rname,
340 'text' => 'nix bar!',
341 'basetimestamp' => $baseTime,
342 'redirect' => true,
343 ), null, self::$users['sysop']->user );
344
345 $this->assertEquals( 'Success', $re['edit']['result'],
346 "no edit conflict expected when following redirect" );
347
348 // try again, without following the redirect. Should fail.
349 try {
350 $this->doApiRequestWithToken( array(
351 'action' => 'edit',
352 'title' => $rname,
353 'text' => 'nix bar!',
354 'basetimestamp' => $baseTime,
355 ), null, self::$users['sysop']->user );
356
357 $this->fail( 'edit conflict expected' );
358 } catch ( UsageException $ex ) {
359 $this->assertEquals( 'editconflict', $ex->getCodeString() );
360 }
361 }
362
363 public function testEditConflict_bug41990() {
364 static $count = 0;
365 $count++;
366
367 /*
368 * bug 41990: if the target page has a newer revision than the redirect, then editing the
369 * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erroneously
370 * caused an edit conflict to be detected.
371 */
372
373 // assume NS_HELP defaults to wikitext
374 $name = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_$count";
375 $title = Title::newFromText( $name );
376 $page = WikiPage::factory( $title );
377
378 $rname = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_r$count";
379 $rtitle = Title::newFromText( $rname );
380 $rpage = WikiPage::factory( $rtitle );
381
382 // base edit for content
383 $page->doEditContent( new WikitextContent( "Foo" ),
384 "testing 1", EDIT_NEW, false, self::$users['sysop']->user );
385 $this->forceRevisionDate( $page, '20120101000000' );
386
387 // base edit for redirect
388 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
389 "testing 1", EDIT_NEW, false, self::$users['sysop']->user );
390 $this->forceRevisionDate( $rpage, '20120101000000' );
391 $baseTime = $rpage->getRevision()->getTimestamp();
392
393 // new edit to content
394 $page->doEditContent( new WikitextContent( "Foo bar" ),
395 "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user );
396 $this->forceRevisionDate( $rpage, '20120101020202' );
397
398 // try to save edit; should work, following the redirect.
399 list( $re, , ) = $this->doApiRequestWithToken( array(
400 'action' => 'edit',
401 'title' => $rname,
402 'text' => 'nix bar!',
403 'redirect' => true,
404 ), null, self::$users['sysop']->user );
405
406 $this->assertEquals( 'Success', $re['edit']['result'],
407 "no edit conflict expected here" );
408 }
409
410 protected function forceRevisionDate( WikiPage $page, $timestamp ) {
411 $dbw = wfGetDB( DB_MASTER );
412
413 $dbw->update( 'revision',
414 array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
415 array( 'rev_id' => $page->getLatest() ) );
416
417 $page->clear();
418 }
419 }