Add @covers tags to specials tests
[lhc/web/wiklou.git] / tests / phpunit / includes / EditPageTest.php
1 <?php
2
3 /**
4 * @group Editing
5 *
6 * @group Database
7 * ^--- tell jenkins this test needs the database
8 *
9 * @group medium
10 * ^--- tell phpunit that these test cases may take longer than 2 seconds.
11 */
12 class EditPageTest extends MediaWikiLangTestCase {
13
14 protected function setUp() {
15 global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
16
17 parent::setUp();
18
19 $this->setMwGlobals( [
20 'wgExtraNamespaces' => $wgExtraNamespaces,
21 'wgNamespaceContentModels' => $wgNamespaceContentModels,
22 'wgContentHandlers' => $wgContentHandlers,
23 'wgContLang' => $wgContLang,
24 ] );
25
26 $wgExtraNamespaces[12312] = 'Dummy';
27 $wgExtraNamespaces[12313] = 'Dummy_talk';
28
29 $wgNamespaceContentModels[12312] = "testing";
30 $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
31
32 MWNamespace::clearCaches();
33 $wgContLang->resetNamespaces(); # reset namespace cache
34 }
35
36 protected function tearDown() {
37 global $wgContLang;
38
39 MWNamespace::clearCaches();
40 $wgContLang->resetNamespaces(); # reset namespace cache
41 parent::tearDown();
42 }
43
44 /**
45 * @dataProvider provideExtractSectionTitle
46 * @covers EditPage::extractSectionTitle
47 */
48 public function testExtractSectionTitle( $section, $title ) {
49 $extracted = EditPage::extractSectionTitle( $section );
50 $this->assertEquals( $title, $extracted );
51 }
52
53 public static function provideExtractSectionTitle() {
54 return [
55 [
56 "== Test ==\n\nJust a test section.",
57 "Test"
58 ],
59 [
60 "An initial section, no header.",
61 false
62 ],
63 [
64 "An initial section with a fake heder (T34617)\n\n== Test == ??\nwtf",
65 false
66 ],
67 [
68 "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
69 "Section"
70 ],
71 [
72 "== Section== \t\r\n followed by whitespace (T37051)",
73 'Section',
74 ],
75 ];
76 }
77
78 protected function forceRevisionDate( WikiPage $page, $timestamp ) {
79 $dbw = wfGetDB( DB_MASTER );
80
81 $dbw->update( 'revision',
82 [ 'rev_timestamp' => $dbw->timestamp( $timestamp ) ],
83 [ 'rev_id' => $page->getLatest() ] );
84
85 $page->clear();
86 }
87
88 /**
89 * User input text is passed to rtrim() by edit page. This is a simple
90 * wrapper around assertEquals() which calls rrtrim() to normalize the
91 * expected and actual texts.
92 * @param string $expected
93 * @param string $actual
94 * @param string $msg
95 */
96 protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
97 $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
98 }
99
100 /**
101 * Performs an edit and checks the result.
102 *
103 * @param string|Title $title The title of the page to edit
104 * @param string|null $baseText Some text to create the page with before attempting the edit.
105 * @param User|string|null $user The user to perform the edit as.
106 * @param array $edit An array of request parameters used to define the edit to perform.
107 * Some well known fields are:
108 * * wpTextbox1: the text to submit
109 * * wpSummary: the edit summary
110 * * wpEditToken: the edit token (will be inserted if not provided)
111 * * wpEdittime: timestamp of the edit's base revision (will be inserted
112 * if not provided)
113 * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
114 * * wpSectionTitle: the section to edit
115 * * wpMinorEdit: mark as minor edit
116 * * wpWatchthis: whether to watch the page
117 * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
118 * Set to null to skip the check.
119 * @param string|null $expectedText The text expected to be on the page after the edit.
120 * Set to null to skip the check.
121 * @param string|null $message An optional message to show along with any error message.
122 *
123 * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
124 */
125 protected function assertEdit( $title, $baseText, $user = null, array $edit,
126 $expectedCode = null, $expectedText = null, $message = null
127 ) {
128 if ( is_string( $title ) ) {
129 $ns = $this->getDefaultWikitextNS();
130 $title = Title::newFromText( $title, $ns );
131 }
132 $this->assertNotNull( $title );
133
134 if ( is_string( $user ) ) {
135 $user = User::newFromName( $user );
136
137 if ( $user->getId() === 0 ) {
138 $user->addToDatabase();
139 }
140 }
141
142 $page = WikiPage::factory( $title );
143
144 if ( $baseText !== null ) {
145 $content = ContentHandler::makeContent( $baseText, $title );
146 $page->doEditContent( $content, "base text for test" );
147 $this->forceRevisionDate( $page, '20120101000000' );
148
149 // sanity check
150 $page->clear();
151 $currentText = ContentHandler::getContentText( $page->getContent() );
152
153 # EditPage rtrim() the user input, so we alter our expected text
154 # to reflect that.
155 $this->assertEditedTextEquals( $baseText, $currentText );
156 }
157
158 if ( $user == null ) {
159 $user = $GLOBALS['wgUser'];
160 } else {
161 $this->setMwGlobals( 'wgUser', $user );
162 }
163
164 if ( !isset( $edit['wpEditToken'] ) ) {
165 $edit['wpEditToken'] = $user->getEditToken();
166 }
167
168 if ( !isset( $edit['wpEdittime'] ) ) {
169 $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : '';
170 }
171
172 if ( !isset( $edit['wpStarttime'] ) ) {
173 $edit['wpStarttime'] = wfTimestampNow();
174 }
175
176 if ( !isset( $edit['wpUnicodeCheck'] ) ) {
177 $edit['wpUnicodeCheck'] = EditPage::UNICODE_CHECK;
178 }
179
180 $req = new FauxRequest( $edit, true ); // session ??
181
182 $article = new Article( $title );
183 $article->getContext()->setTitle( $title );
184 $ep = new EditPage( $article );
185 $ep->setContextTitle( $title );
186 $ep->importFormData( $req );
187
188 $bot = isset( $edit['bot'] ) ? (bool)$edit['bot'] : false;
189
190 // this is where the edit happens!
191 // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
192 // and throws exceptions like PermissionsError
193 $status = $ep->internalAttemptSave( $result, $bot );
194
195 if ( $expectedCode !== null ) {
196 // check edit code
197 $this->assertEquals( $expectedCode, $status->value,
198 "Expected result code mismatch. $message" );
199 }
200
201 $page = WikiPage::factory( $title );
202
203 if ( $expectedText !== null ) {
204 // check resulting page text
205 $content = $page->getContent();
206 $text = ContentHandler::getContentText( $content );
207
208 # EditPage rtrim() the user input, so we alter our expected text
209 # to reflect that.
210 $this->assertEditedTextEquals( $expectedText, $text,
211 "Expected article text mismatch. $message" );
212 }
213
214 return $page;
215 }
216
217 public static function provideCreatePages() {
218 return [
219 [ 'expected article being created',
220 'EditPageTest_testCreatePage',
221 null,
222 'Hello World!',
223 EditPage::AS_SUCCESS_NEW_ARTICLE,
224 'Hello World!'
225 ],
226 [ 'expected article not being created if empty',
227 'EditPageTest_testCreatePage',
228 null,
229 '',
230 EditPage::AS_BLANK_ARTICLE,
231 null
232 ],
233 [ 'expected MediaWiki: page being created',
234 'MediaWiki:January',
235 'UTSysop',
236 'Not January',
237 EditPage::AS_SUCCESS_NEW_ARTICLE,
238 'Not January'
239 ],
240 [ 'expected not-registered MediaWiki: page not being created if empty',
241 'MediaWiki:EditPageTest_testCreatePage',
242 'UTSysop',
243 '',
244 EditPage::AS_BLANK_ARTICLE,
245 null
246 ],
247 [ 'expected registered MediaWiki: page being created even if empty',
248 'MediaWiki:January',
249 'UTSysop',
250 '',
251 EditPage::AS_SUCCESS_NEW_ARTICLE,
252 ''
253 ],
254 [ 'expected registered MediaWiki: page whose default content is empty'
255 . ' not being created if empty',
256 'MediaWiki:Ipb-default-expiry',
257 'UTSysop',
258 '',
259 EditPage::AS_BLANK_ARTICLE,
260 ''
261 ],
262 [ 'expected MediaWiki: page not being created if text equals default message',
263 'MediaWiki:January',
264 'UTSysop',
265 'January',
266 EditPage::AS_BLANK_ARTICLE,
267 null
268 ],
269 [ 'expected empty article being created',
270 'EditPageTest_testCreatePage',
271 null,
272 '',
273 EditPage::AS_SUCCESS_NEW_ARTICLE,
274 '',
275 true
276 ],
277 ];
278 }
279
280 /**
281 * @dataProvider provideCreatePages
282 * @covers EditPage
283 */
284 public function testCreatePage(
285 $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
286 ) {
287 $checkId = null;
288
289 $this->setMwGlobals( 'wgHooks', [
290 'PageContentInsertComplete' => [ function (
291 WikiPage &$page, User &$user, Content $content,
292 $summary, $minor, $u1, $u2, &$flags, Revision $revision
293 ) {
294 // types/refs checked
295 } ],
296 'PageContentSaveComplete' => [ function (
297 WikiPage &$page, User &$user, Content $content,
298 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
299 Status &$status, $baseRevId
300 ) use ( &$checkId ) {
301 $checkId = $status->value['revision']->getId();
302 // types/refs checked
303 } ],
304 ] );
305
306 $edit = [ 'wpTextbox1' => $editText ];
307 if ( $ignoreBlank ) {
308 $edit['wpIgnoreBlankArticle'] = 1;
309 }
310
311 $page = $this->assertEdit( $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
312
313 if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
314 $latest = $page->getLatest();
315 $page->doDeleteArticleReal( $pageTitle );
316
317 $this->assertGreaterThan( 0, $latest, "Page revision ID updated in object" );
318 $this->assertEquals( $latest, $checkId, "Revision in Status for hook" );
319 }
320 }
321
322 /**
323 * @dataProvider provideCreatePages
324 * @covers EditPage
325 */
326 public function testCreatePageTrx(
327 $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
328 ) {
329 $checkIds = [];
330 $this->setMwGlobals( 'wgHooks', [
331 'PageContentInsertComplete' => [ function (
332 WikiPage &$page, User &$user, Content $content,
333 $summary, $minor, $u1, $u2, &$flags, Revision $revision
334 ) {
335 // types/refs checked
336 } ],
337 'PageContentSaveComplete' => [ function (
338 WikiPage &$page, User &$user, Content $content,
339 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
340 Status &$status, $baseRevId
341 ) use ( &$checkIds ) {
342 $checkIds[] = $status->value['revision']->getId();
343 // types/refs checked
344 } ],
345 ] );
346
347 wfGetDB( DB_MASTER )->begin( __METHOD__ );
348
349 $edit = [ 'wpTextbox1' => $editText ];
350 if ( $ignoreBlank ) {
351 $edit['wpIgnoreBlankArticle'] = 1;
352 }
353
354 $page = $this->assertEdit(
355 $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
356
357 $pageTitle2 = (string)$pageTitle . '/x';
358 $page2 = $this->assertEdit(
359 $pageTitle2, null, $user, $edit, $expectedCode, $expectedText, $desc );
360
361 wfGetDB( DB_MASTER )->commit( __METHOD__ );
362
363 $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
364
365 if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
366 $latest = $page->getLatest();
367 $page->doDeleteArticleReal( $pageTitle );
368
369 $this->assertGreaterThan( 0, $latest, "Page #1 revision ID updated in object" );
370 $this->assertEquals( $latest, $checkIds[0], "Revision #1 in Status for hook" );
371
372 $latest2 = $page2->getLatest();
373 $page2->doDeleteArticleReal( $pageTitle2 );
374
375 $this->assertGreaterThan( 0, $latest2, "Page #2 revision ID updated in object" );
376 $this->assertEquals( $latest2, $checkIds[1], "Revision #2 in Status for hook" );
377 }
378 }
379
380 public function testUpdatePage() {
381 $checkIds = [];
382
383 $this->setMwGlobals( 'wgHooks', [
384 'PageContentInsertComplete' => [ function (
385 WikiPage &$page, User &$user, Content $content,
386 $summary, $minor, $u1, $u2, &$flags, Revision $revision
387 ) {
388 // types/refs checked
389 } ],
390 'PageContentSaveComplete' => [ function (
391 WikiPage &$page, User &$user, Content $content,
392 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
393 Status &$status, $baseRevId
394 ) use ( &$checkIds ) {
395 $checkIds[] = $status->value['revision']->getId();
396 // types/refs checked
397 } ],
398 ] );
399
400 $text = "one";
401 $edit = [
402 'wpTextbox1' => $text,
403 'wpSummary' => 'first update',
404 ];
405
406 $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
407 EditPage::AS_SUCCESS_UPDATE, $text,
408 "expected successfull update with given text" );
409 $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
410
411 $this->forceRevisionDate( $page, '20120101000000' );
412
413 $text = "two";
414 $edit = [
415 'wpTextbox1' => $text,
416 'wpSummary' => 'second update',
417 ];
418
419 $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
420 EditPage::AS_SUCCESS_UPDATE, $text,
421 "expected successfull update with given text" );
422 $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
423 $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
424 }
425
426 public function testUpdatePageTrx() {
427 $text = "one";
428 $edit = [
429 'wpTextbox1' => $text,
430 'wpSummary' => 'first update',
431 ];
432
433 $page = $this->assertEdit( 'EditPageTest_testTrxUpdatePage', "zero", null, $edit,
434 EditPage::AS_SUCCESS_UPDATE, $text,
435 "expected successfull update with given text" );
436
437 $this->forceRevisionDate( $page, '20120101000000' );
438
439 $checkIds = [];
440 $this->setMwGlobals( 'wgHooks', [
441 'PageContentSaveComplete' => [ function (
442 WikiPage &$page, User &$user, Content $content,
443 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
444 Status &$status, $baseRevId
445 ) use ( &$checkIds ) {
446 $checkIds[] = $status->value['revision']->getId();
447 // types/refs checked
448 } ],
449 ] );
450
451 wfGetDB( DB_MASTER )->begin( __METHOD__ );
452
453 $text = "two";
454 $edit = [
455 'wpTextbox1' => $text,
456 'wpSummary' => 'second update',
457 ];
458
459 $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
460 EditPage::AS_SUCCESS_UPDATE, $text,
461 "expected successfull update with given text" );
462
463 $text = "three";
464 $edit = [
465 'wpTextbox1' => $text,
466 'wpSummary' => 'third update',
467 ];
468
469 $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
470 EditPage::AS_SUCCESS_UPDATE, $text,
471 "expected successfull update with given text" );
472
473 wfGetDB( DB_MASTER )->commit( __METHOD__ );
474
475 $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
476 $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
477 $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
478 }
479
480 public static function provideSectionEdit() {
481 $text = 'Intro
482
483 == one ==
484 first section.
485
486 == two ==
487 second section.
488 ';
489
490 $sectionOne = '== one ==
491 hello
492 ';
493
494 $newSection = '== new section ==
495
496 hello
497 ';
498
499 $textWithNewSectionOne = preg_replace(
500 '/== one ==.*== two ==/ms',
501 "$sectionOne\n== two ==", $text
502 );
503
504 $textWithNewSectionAdded = "$text\n$newSection";
505
506 return [
507 [ # 0
508 $text,
509 '',
510 'hello',
511 'replace all',
512 'hello'
513 ],
514
515 [ # 1
516 $text,
517 '1',
518 $sectionOne,
519 'replace first section',
520 $textWithNewSectionOne,
521 ],
522
523 [ # 2
524 $text,
525 'new',
526 'hello',
527 'new section',
528 $textWithNewSectionAdded,
529 ],
530 ];
531 }
532
533 /**
534 * @dataProvider provideSectionEdit
535 * @covers EditPage
536 */
537 public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
538 $edit = [
539 'wpTextbox1' => $text,
540 'wpSummary' => $summary,
541 'wpSection' => $section,
542 ];
543
544 $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
545 EditPage::AS_SUCCESS_UPDATE, $expected,
546 "expected successfull update of section" );
547 }
548
549 public static function provideAutoMerge() {
550 $tests = [];
551
552 $tests[] = [ # 0: plain conflict
553 "Elmo", # base edit user
554 "one\n\ntwo\n\nthree\n",
555 [ # adam's edit
556 'wpStarttime' => 1,
557 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
558 ],
559 [ # berta's edit
560 'wpStarttime' => 2,
561 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
562 ],
563 EditPage::AS_CONFLICT_DETECTED, # expected code
564 "ONE\n\ntwo\n\nthree\n", # expected text
565 'expected edit conflict', # message
566 ];
567
568 $tests[] = [ # 1: successful merge
569 "Elmo", # base edit user
570 "one\n\ntwo\n\nthree\n",
571 [ # adam's edit
572 'wpStarttime' => 1,
573 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
574 ],
575 [ # berta's edit
576 'wpStarttime' => 2,
577 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
578 ],
579 EditPage::AS_SUCCESS_UPDATE, # expected code
580 "ONE\n\ntwo\n\nTHREE\n", # expected text
581 'expected automatic merge', # message
582 ];
583
584 $text = "Intro\n\n";
585 $text .= "== first section ==\n\n";
586 $text .= "one\n\ntwo\n\nthree\n\n";
587 $text .= "== second section ==\n\n";
588 $text .= "four\n\nfive\n\nsix\n\n";
589
590 // extract the first section.
591 $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
592
593 // generate expected text after merge
594 $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
595
596 $tests[] = [ # 2: merge in section
597 "Elmo", # base edit user
598 $text,
599 [ # adam's edit
600 'wpStarttime' => 1,
601 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
602 'wpSection' => '1'
603 ],
604 [ # berta's edit
605 'wpStarttime' => 2,
606 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
607 'wpSection' => '1'
608 ],
609 EditPage::AS_SUCCESS_UPDATE, # expected code
610 $expected, # expected text
611 'expected automatic section merge', # message
612 ];
613
614 // see whether it makes a difference who did the base edit
615 $testsWithAdam = array_map( function ( $test ) {
616 $test[0] = 'Adam'; // change base edit user
617 return $test;
618 }, $tests );
619
620 $testsWithBerta = array_map( function ( $test ) {
621 $test[0] = 'Berta'; // change base edit user
622 return $test;
623 }, $tests );
624
625 return array_merge( $tests, $testsWithAdam, $testsWithBerta );
626 }
627
628 /**
629 * @dataProvider provideAutoMerge
630 * @covers EditPage
631 */
632 public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
633 $expectedCode, $expectedText, $message = null
634 ) {
635 $this->markTestSkippedIfNoDiff3();
636
637 // create page
638 $ns = $this->getDefaultWikitextNS();
639 $title = Title::newFromText( 'EditPageTest_testAutoMerge', $ns );
640 $page = WikiPage::factory( $title );
641
642 if ( $page->exists() ) {
643 $page->doDeleteArticle( "clean slate for testing" );
644 }
645
646 $baseEdit = [
647 'wpTextbox1' => $text,
648 ];
649
650 $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
651 $baseUser, $baseEdit, null, null, __METHOD__ );
652
653 $this->forceRevisionDate( $page, '20120101000000' );
654
655 $edittime = $page->getTimestamp();
656
657 // start timestamps for conflict detection
658 if ( !isset( $adamsEdit['wpStarttime'] ) ) {
659 $adamsEdit['wpStarttime'] = 1;
660 }
661
662 if ( !isset( $bertasEdit['wpStarttime'] ) ) {
663 $bertasEdit['wpStarttime'] = 2;
664 }
665
666 $starttime = wfTimestampNow();
667 $adamsTime = wfTimestamp(
668 TS_MW,
669 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$adamsEdit['wpStarttime']
670 );
671 $bertasTime = wfTimestamp(
672 TS_MW,
673 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$bertasEdit['wpStarttime']
674 );
675
676 $adamsEdit['wpStarttime'] = $adamsTime;
677 $bertasEdit['wpStarttime'] = $bertasTime;
678
679 $adamsEdit['wpSummary'] = 'Adam\'s edit';
680 $bertasEdit['wpSummary'] = 'Bertas\'s edit';
681
682 $adamsEdit['wpEdittime'] = $edittime;
683 $bertasEdit['wpEdittime'] = $edittime;
684
685 // first edit
686 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
687 EditPage::AS_SUCCESS_UPDATE, null, "expected successfull update" );
688
689 // second edit
690 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
691 $expectedCode, $expectedText, $message );
692 }
693
694 /**
695 * @depends testAutoMerge
696 */
697 public function testCheckDirectEditingDisallowed_forNonTextContent() {
698 $title = Title::newFromText( 'Dummy:NonTextPageForEditPage' );
699 $page = WikiPage::factory( $title );
700
701 $article = new Article( $title );
702 $article->getContext()->setTitle( $title );
703 $ep = new EditPage( $article );
704 $ep->setContextTitle( $title );
705
706 $user = $GLOBALS['wgUser'];
707
708 $edit = [
709 'wpTextbox1' => serialize( 'non-text content' ),
710 'wpEditToken' => $user->getEditToken(),
711 'wpEdittime' => '',
712 'wpStarttime' => wfTimestampNow(),
713 'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
714 ];
715
716 $req = new FauxRequest( $edit, true );
717 $ep->importFormData( $req );
718
719 $this->setExpectedException(
720 'MWException',
721 'This content model is not supported: testing'
722 );
723
724 $ep->internalAttemptSave( $result, false );
725 }
726
727 }