summaryrefslogtreecommitdiff
path: root/tests/phpunit/includes/EditPageTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'tests/phpunit/includes/EditPageTest.php')
-rw-r--r--tests/phpunit/includes/EditPageTest.php499
1 files changed, 499 insertions, 0 deletions
diff --git a/tests/phpunit/includes/EditPageTest.php b/tests/phpunit/includes/EditPageTest.php
new file mode 100644
index 00000000..702fce4c
--- /dev/null
+++ b/tests/phpunit/includes/EditPageTest.php
@@ -0,0 +1,499 @@
+<?php
+
+/**
+ * @group Editing
+ *
+ * @group Database
+ * ^--- tell jenkins this test needs the database
+ *
+ * @group medium
+ * ^--- tell phpunit that these test cases may take longer than 2 seconds.
+ */
+class EditPageTest extends MediaWikiLangTestCase {
+
+ /**
+ * @dataProvider provideExtractSectionTitle
+ * @covers EditPage::extractSectionTitle
+ */
+ public function testExtractSectionTitle( $section, $title ) {
+ $extracted = EditPage::extractSectionTitle( $section );
+ $this->assertEquals( $title, $extracted );
+ }
+
+ public static function provideExtractSectionTitle() {
+ return array(
+ array(
+ "== Test ==\n\nJust a test section.",
+ "Test"
+ ),
+ array(
+ "An initial section, no header.",
+ false
+ ),
+ array(
+ "An initial section with a fake heder (bug 32617)\n\n== Test == ??\nwtf",
+ false
+ ),
+ array(
+ "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
+ "Section"
+ ),
+ array(
+ "== Section== \t\r\n followed by whitespace (bug 35051)",
+ 'Section',
+ ),
+ );
+ }
+
+ protected function forceRevisionDate( WikiPage $page, $timestamp ) {
+ $dbw = wfGetDB( DB_MASTER );
+
+ $dbw->update( 'revision',
+ array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
+ array( 'rev_id' => $page->getLatest() ) );
+
+ $page->clear();
+ }
+
+ /**
+ * User input text is passed to rtrim() by edit page. This is a simple
+ * wrapper around assertEquals() which calls rrtrim() to normalize the
+ * expected and actual texts.
+ * @param string $expected
+ * @param string $actual
+ * @param string $msg
+ */
+ protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
+ return $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
+ }
+
+ /**
+ * Performs an edit and checks the result.
+ *
+ * @param string|Title $title The title of the page to edit
+ * @param string|null $baseText Some text to create the page with before attempting the edit.
+ * @param User|string|null $user The user to perform the edit as.
+ * @param array $edit An array of request parameters used to define the edit to perform.
+ * Some well known fields are:
+ * * wpTextbox1: the text to submit
+ * * wpSummary: the edit summary
+ * * wpEditToken: the edit token (will be inserted if not provided)
+ * * wpEdittime: timestamp of the edit's base revision (will be inserted
+ * if not provided)
+ * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
+ * * wpSectionTitle: the section to edit
+ * * wpMinorEdit: mark as minor edit
+ * * wpWatchthis: whether to watch the page
+ * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
+ * Set to null to skip the check.
+ * @param string|null $expectedText The text expected to be on the page after the edit.
+ * Set to null to skip the check.
+ * @param string|null $message An optional message to show along with any error message.
+ *
+ * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
+ */
+ protected function assertEdit( $title, $baseText, $user = null, array $edit,
+ $expectedCode = null, $expectedText = null, $message = null
+ ) {
+ if ( is_string( $title ) ) {
+ $ns = $this->getDefaultWikitextNS();
+ $title = Title::newFromText( $title, $ns );
+ }
+ $this->assertNotNull( $title );
+
+ if ( is_string( $user ) ) {
+ $user = User::newFromName( $user );
+
+ if ( $user->getId() === 0 ) {
+ $user->addToDatabase();
+ }
+ }
+
+ $page = WikiPage::factory( $title );
+
+ if ( $baseText !== null ) {
+ $content = ContentHandler::makeContent( $baseText, $title );
+ $page->doEditContent( $content, "base text for test" );
+ $this->forceRevisionDate( $page, '20120101000000' );
+
+ //sanity check
+ $page->clear();
+ $currentText = ContentHandler::getContentText( $page->getContent() );
+
+ # EditPage rtrim() the user input, so we alter our expected text
+ # to reflect that.
+ $this->assertEditedTextEquals( $baseText, $currentText );
+ }
+
+ if ( $user == null ) {
+ $user = $GLOBALS['wgUser'];
+ } else {
+ $this->setMwGlobals( 'wgUser', $user );
+ }
+
+ if ( !isset( $edit['wpEditToken'] ) ) {
+ $edit['wpEditToken'] = $user->getEditToken();
+ }
+
+ if ( !isset( $edit['wpEdittime'] ) ) {
+ $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : '';
+ }
+
+ if ( !isset( $edit['wpStarttime'] ) ) {
+ $edit['wpStarttime'] = wfTimestampNow();
+ }
+
+ $req = new FauxRequest( $edit, true ); // session ??
+
+ $article = new Article( $title );
+ $article->getContext()->setTitle( $title );
+ $ep = new EditPage( $article );
+ $ep->setContextTitle( $title );
+ $ep->importFormData( $req );
+
+ $bot = isset( $edit['bot'] ) ? (bool)$edit['bot'] : false;
+
+ // this is where the edit happens!
+ // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
+ // and throws exceptions like PermissionsError
+ $status = $ep->internalAttemptSave( $result, $bot );
+
+ if ( $expectedCode !== null ) {
+ // check edit code
+ $this->assertEquals( $expectedCode, $status->value,
+ "Expected result code mismatch. $message" );
+ }
+
+ $page = WikiPage::factory( $title );
+
+ if ( $expectedText !== null ) {
+ // check resulting page text
+ $content = $page->getContent();
+ $text = ContentHandler::getContentText( $content );
+
+ # EditPage rtrim() the user input, so we alter our expected text
+ # to reflect that.
+ $this->assertEditedTextEquals( $expectedText, $text,
+ "Expected article text mismatch. $message" );
+ }
+
+ return $page;
+ }
+
+ public static function provideCreatePages() {
+ return array(
+ array( 'expected article being created',
+ 'EditPageTest_testCreatePage',
+ null,
+ 'Hello World!',
+ EditPage::AS_SUCCESS_NEW_ARTICLE,
+ 'Hello World!'
+ ),
+ array( 'expected article not being created if empty',
+ 'EditPageTest_testCreatePage',
+ null,
+ '',
+ EditPage::AS_BLANK_ARTICLE,
+ null
+ ),
+ array( 'expected MediaWiki: page being created',
+ 'MediaWiki:January',
+ 'UTSysop',
+ 'Not January',
+ EditPage::AS_SUCCESS_NEW_ARTICLE,
+ 'Not January'
+ ),
+ array( 'expected not-registered MediaWiki: page not being created if empty',
+ 'MediaWiki:EditPageTest_testCreatePage',
+ 'UTSysop',
+ '',
+ EditPage::AS_BLANK_ARTICLE,
+ null
+ ),
+ array( 'expected registered MediaWiki: page being created even if empty',
+ 'MediaWiki:January',
+ 'UTSysop',
+ '',
+ EditPage::AS_SUCCESS_NEW_ARTICLE,
+ ''
+ ),
+ array( 'expected registered MediaWiki: page whose default content is empty not being created if empty',
+ 'MediaWiki:Ipb-default-expiry',
+ 'UTSysop',
+ '',
+ EditPage::AS_BLANK_ARTICLE,
+ ''
+ ),
+ array( 'expected MediaWiki: page not being created if text equals default message',
+ 'MediaWiki:January',
+ 'UTSysop',
+ 'January',
+ EditPage::AS_BLANK_ARTICLE,
+ null
+ ),
+ array( 'expected empty article being created',
+ 'EditPageTest_testCreatePage',
+ null,
+ '',
+ EditPage::AS_SUCCESS_NEW_ARTICLE,
+ '',
+ true
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider provideCreatePages
+ * @covers EditPage
+ */
+ public function testCreatePage( $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false ) {
+ $edit = array( 'wpTextbox1' => $editText );
+ if ( $ignoreBlank ) {
+ $edit['wpIgnoreBlankArticle'] = 1;
+ }
+
+ $page = $this->assertEdit( $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
+
+ if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
+ $page->doDeleteArticleReal( $pageTitle );
+ }
+ }
+
+ public function testUpdatePage() {
+ $text = "one";
+ $edit = array(
+ 'wpTextbox1' => $text,
+ 'wpSummary' => 'first update',
+ );
+
+ $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
+ EditPage::AS_SUCCESS_UPDATE, $text,
+ "expected successfull update with given text" );
+
+ $this->forceRevisionDate( $page, '20120101000000' );
+
+ $text = "two";
+ $edit = array(
+ 'wpTextbox1' => $text,
+ 'wpSummary' => 'second update',
+ );
+
+ $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
+ EditPage::AS_SUCCESS_UPDATE, $text,
+ "expected successfull update with given text" );
+ }
+
+ public static function provideSectionEdit() {
+ $text = 'Intro
+
+== one ==
+first section.
+
+== two ==
+second section.
+';
+
+ $sectionOne = '== one ==
+hello
+';
+
+ $newSection = '== new section ==
+
+hello
+';
+
+ $textWithNewSectionOne = preg_replace(
+ '/== one ==.*== two ==/ms',
+ "$sectionOne\n== two ==", $text
+ );
+
+ $textWithNewSectionAdded = "$text\n$newSection";
+
+ return array(
+ array( #0
+ $text,
+ '',
+ 'hello',
+ 'replace all',
+ 'hello'
+ ),
+
+ array( #1
+ $text,
+ '1',
+ $sectionOne,
+ 'replace first section',
+ $textWithNewSectionOne,
+ ),
+
+ array( #2
+ $text,
+ 'new',
+ 'hello',
+ 'new section',
+ $textWithNewSectionAdded,
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider provideSectionEdit
+ * @covers EditPage
+ */
+ public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
+ $edit = array(
+ 'wpTextbox1' => $text,
+ 'wpSummary' => $summary,
+ 'wpSection' => $section,
+ );
+
+ $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
+ EditPage::AS_SUCCESS_UPDATE, $expected,
+ "expected successfull update of section" );
+ }
+
+ public static function provideAutoMerge() {
+ $tests = array();
+
+ $tests[] = array( #0: plain conflict
+ "Elmo", # base edit user
+ "one\n\ntwo\n\nthree\n",
+ array( #adam's edit
+ 'wpStarttime' => 1,
+ 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
+ ),
+ array( #berta's edit
+ 'wpStarttime' => 2,
+ 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
+ ),
+ EditPage::AS_CONFLICT_DETECTED, # expected code
+ "ONE\n\ntwo\n\nthree\n", # expected text
+ 'expected edit conflict', # message
+ );
+
+ $tests[] = array( #1: successful merge
+ "Elmo", # base edit user
+ "one\n\ntwo\n\nthree\n",
+ array( #adam's edit
+ 'wpStarttime' => 1,
+ 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
+ ),
+ array( #berta's edit
+ 'wpStarttime' => 2,
+ 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
+ ),
+ EditPage::AS_SUCCESS_UPDATE, # expected code
+ "ONE\n\ntwo\n\nTHREE\n", # expected text
+ 'expected automatic merge', # message
+ );
+
+ $text = "Intro\n\n";
+ $text .= "== first section ==\n\n";
+ $text .= "one\n\ntwo\n\nthree\n\n";
+ $text .= "== second section ==\n\n";
+ $text .= "four\n\nfive\n\nsix\n\n";
+
+ // extract the first section.
+ $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
+
+ // generate expected text after merge
+ $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
+
+ $tests[] = array( #2: merge in section
+ "Elmo", # base edit user
+ $text,
+ array( #adam's edit
+ 'wpStarttime' => 1,
+ 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
+ 'wpSection' => '1'
+ ),
+ array( #berta's edit
+ 'wpStarttime' => 2,
+ 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
+ 'wpSection' => '1'
+ ),
+ EditPage::AS_SUCCESS_UPDATE, # expected code
+ $expected, # expected text
+ 'expected automatic section merge', # message
+ );
+
+ // see whether it makes a difference who did the base edit
+ $testsWithAdam = array_map( function ( $test ) {
+ $test[0] = 'Adam'; // change base edit user
+ return $test;
+ }, $tests );
+
+ $testsWithBerta = array_map( function ( $test ) {
+ $test[0] = 'Berta'; // change base edit user
+ return $test;
+ }, $tests );
+
+ return array_merge( $tests, $testsWithAdam, $testsWithBerta );
+ }
+
+ /**
+ * @dataProvider provideAutoMerge
+ * @covers EditPage
+ */
+ public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
+ $expectedCode, $expectedText, $message = null
+ ) {
+ $this->checkHasDiff3();
+
+ //create page
+ $ns = $this->getDefaultWikitextNS();
+ $title = Title::newFromText( 'EditPageTest_testAutoMerge', $ns );
+ $page = WikiPage::factory( $title );
+
+ if ( $page->exists() ) {
+ $page->doDeleteArticle( "clean slate for testing" );
+ }
+
+ $baseEdit = array(
+ 'wpTextbox1' => $text,
+ );
+
+ $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
+ $baseUser, $baseEdit, null, null, __METHOD__ );
+
+ $this->forceRevisionDate( $page, '20120101000000' );
+
+ $edittime = $page->getTimestamp();
+
+ // start timestamps for conflict detection
+ if ( !isset( $adamsEdit['wpStarttime'] ) ) {
+ $adamsEdit['wpStarttime'] = 1;
+ }
+
+ if ( !isset( $bertasEdit['wpStarttime'] ) ) {
+ $bertasEdit['wpStarttime'] = 2;
+ }
+
+ $starttime = wfTimestampNow();
+ $adamsTime = wfTimestamp(
+ TS_MW,
+ (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$adamsEdit['wpStarttime']
+ );
+ $bertasTime = wfTimestamp(
+ TS_MW,
+ (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$bertasEdit['wpStarttime']
+ );
+
+ $adamsEdit['wpStarttime'] = $adamsTime;
+ $bertasEdit['wpStarttime'] = $bertasTime;
+
+ $adamsEdit['wpSummary'] = 'Adam\'s edit';
+ $bertasEdit['wpSummary'] = 'Bertas\'s edit';
+
+ $adamsEdit['wpEdittime'] = $edittime;
+ $bertasEdit['wpEdittime'] = $edittime;
+
+ // first edit
+ $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
+ EditPage::AS_SUCCESS_UPDATE, null, "expected successfull update" );
+
+ // second edit
+ $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
+ $expectedCode, $expectedText, $message );
+ }
+}