getServiceContainer()->getWikiPageFactory()
->newFromLinkTarget( new TitleValue( NS_MAIN, __CLASS__ ) );
$status = $this->editPage( $page, 'Test for revdel' );
self::$pageId = $status->getNewRevision()->getPageId();
self::$revIds['revdel'] = $status->getNewRevision()->getId();
$status = $this->editPage( $page, 'Test for suppressed' );
self::$revIds['suppressed'] = $status->getNewRevision()->getId();
$status = $this->editPage( $page, 'Test for oldid' );
self::$revIds['oldid'] = $status->getNewRevision()->getId();
$status = $this->editPage( $page, 'Test for latest' );
self::$revIds['latest'] = $status->getNewRevision()->getId();
// Set a user for modifying the visibility, this is needed because
// setVisibility generates a log, which cannot be an anonymous user actor
// when temporary accounts are enabled.
RequestContext::getMain()->setUser( $this->getTestUser()->getUser() );
$this->revisionDelete( self::$revIds['revdel'] );
$this->revisionDelete(
self::$revIds['suppressed'],
[
RevisionRecord::DELETED_TEXT => 1,
RevisionRecord::DELETED_RESTRICTED => 1
]
);
}
/**
* Assert that the given result of calling $this->doApiRequest() with
* action=parse resulted in $html, accounting for the boilerplate that the
* parser adds around the parsed page. Also asserts that warnings match
* the provided $warning.
*
* @param string $expected Expected HTML
* @param array $res Returned from doApiRequest()
* @param string|null $warnings Exact value of expected warnings, null for
* no warnings
*/
protected function assertParsedTo( $expected, array $res, $warnings = null ) {
$this->doAssertParsedTo( $expected, $res, $warnings, [ $this, 'assertSame' ] );
}
/**
* Same as above, but asserts that the HTML matches a regexp instead of a
* literal string match.
*
* @param string $expected Expected HTML
* @param array $res Returned from doApiRequest()
* @param string|null $warnings Exact value of expected warnings, null for
* no warnings
*/
protected function assertParsedToRegExp( $expected, array $res, $warnings = null ) {
$this->doAssertParsedTo( $expected, $res, $warnings, [ $this, 'assertMatchesRegularExpression' ] );
}
private function doAssertParsedTo( $expected, array $res, $warnings, callable $callback ) {
$html = $res[0]['parse']['text'];
$expectedStart = '
assertSame( $expectedStart, substr( $html, 0, strlen( $expectedStart ) ) );
$html = substr( $html, strlen( $expectedStart ) );
# Parsoid-based transformations may add ID and data-mw-parsoid-version
# attributes to the wrapper div
$possibleIdAttr = '/^( (id|data-mw[^=]*)="[^"]+")*>/';
$html = preg_replace( $possibleIdAttr, '', $html );
$possibleParserCache = '/\n)\n/';
$html = preg_replace( $possibleParserCache, '', $html );
if ( $res[1]->getBool( 'disablelimitreport' ) ) {
$expectedEnd = "
";
$this->assertSame( $expectedEnd, substr( $html, -strlen( $expectedEnd ) ) );
$unexpectedEnd = '#)\n' .
')\n' .
'$#s';
$this->assertMatchesRegularExpression( $expectedEnd, $html );
$html = preg_replace( $expectedEnd, '', $html );
}
$callback( $expected, $html );
if ( $warnings === null ) {
$this->assertCount( 1, $res[0] );
} else {
$this->assertCount( 2, $res[0] );
$this->assertSame( [ 'warnings' => $warnings ], $res[0]['warnings']['parse'] );
}
}
/**
* Set up an interwiki entry for testing.
*/
protected function setupInterwiki() {
$this->getDb()->newInsertQueryBuilder()
->insertInto( 'interwiki' )
->ignore()
->row( [
'iw_prefix' => 'madeuplanguage',
'iw_url' => "https://example.com/wiki/$1",
'iw_api' => '',
'iw_wikiid' => '',
'iw_local' => false,
] )
// This deliberately conflicts with the Talk namespace
// (T204792/T363538)
->row( [
'iw_prefix' => 'talk',
'iw_url' => "https://talk.example.com/wiki/$1",
'iw_api' => '',
'iw_wikiid' => '',
'iw_local' => false,
] )
->caller( __METHOD__ )
->execute();
$this->overrideConfigValue(
MainConfigNames::ExtraInterlanguageLinkPrefixes,
[ 'madeuplanguage', 'talk' ]
);
}
/**
* Set up a skin for testing.
*
* @todo Should this code be in MediaWikiIntegrationTestCase or something?
*/
protected function setupSkin() {
$factory = new SkinFactory( $this->getDummyObjectFactory(), [] );
$factory->register( 'testing', 'Testing', function () {
$skin = $this->getMockBuilder( SkinFallback::class )
->onlyMethods( [ 'getDefaultModules' ] )
->getMock();
$skin->expects( $this->once() )->method( 'getDefaultModules' )
->willReturn( [
'styles' => [ 'core' => [ 'quux.styles' ] ],
'core' => [ 'foo', 'bar' ],
'content' => [ 'baz' ]
] );
return $skin;
} );
$this->setService( 'SkinFactory', $factory );
}
public function testParseByName() {
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
] );
$this->assertParsedTo( "Test for latest\n
", $res );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
'disablelimitreport' => 1,
] );
$this->assertParsedTo( "Test for latest\n
", $res );
}
public function testParseById() {
$res = $this->doApiRequest( [
'action' => 'parse',
'pageid' => self::$pageId,
] );
$this->assertParsedTo( "Test for latest\n
", $res );
}
public function testParseByOldId() {
$res = $this->doApiRequest( [
'action' => 'parse',
'oldid' => self::$revIds['oldid'],
] );
$this->assertParsedTo( "Test for oldid\n
", $res );
$this->assertArrayNotHasKey( 'textdeleted', $res[0]['parse'] );
$this->assertArrayNotHasKey( 'textsuppressed', $res[0]['parse'] );
}
public function testRevDel() {
$res = $this->doApiRequest( [
'action' => 'parse',
'oldid' => self::$revIds['revdel'],
] );
$this->assertParsedTo( "Test for revdel\n
", $res );
$this->assertArrayHasKey( 'textdeleted', $res[0]['parse'] );
$this->assertArrayNotHasKey( 'textsuppressed', $res[0]['parse'] );
}
public function testRevDelNoPermission() {
$this->expectApiErrorCode( 'permissiondenied' );
$this->doApiRequest( [
'action' => 'parse',
'oldid' => self::$revIds['revdel'],
], null, null, static::getTestUser()->getAuthority() );
}
public function testSuppressed() {
$this->setGroupPermissions( 'sysop', 'viewsuppressed', true );
$res = $this->doApiRequest( [
'action' => 'parse',
'oldid' => self::$revIds['suppressed']
] );
$this->assertParsedTo( "Test for suppressed\n
", $res );
$this->assertArrayHasKey( 'textsuppressed', $res[0]['parse'] );
$this->assertArrayHasKey( 'textdeleted', $res[0]['parse'] );
}
public function testNonexistentPage() {
try {
$this->doApiRequest( [
'action' => 'parse',
'page' => 'DoesNotExist',
] );
$this->fail( "API did not return an error when parsing a nonexistent page" );
} catch ( ApiUsageException $ex ) {
$this->assertApiErrorCode( 'missingtitle', $ex );
}
}
public function testTitleProvided() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => 'Some interesting page',
'text' => '{{PAGENAME}} has attracted my attention',
] );
$this->assertParsedTo( "Some interesting page has attracted my attention\n
", $res );
}
public function testSection() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( $name,
"Intro\n\n== Section 1 ==\n\nContent 1\n\n== Section 2 ==\n\nContent 2" );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => $name,
'section' => 1,
] );
$this->assertParsedToRegExp( '!]*>.*Section 1.*
.*\nContent 1\n
!', $res );
}
public function testInvalidSection() {
$this->expectApiErrorCode( 'invalidsection' );
$this->doApiRequest( [
'action' => 'parse',
'section' => 'T-new',
] );
}
public function testSectionNoContent() {
$name = ucfirst( __FUNCTION__ );
$status = $this->editPage( $name,
"Intro\n\n== Section 1 ==\n\nContent 1\n\n== Section 2 ==\n\nContent 2" );
$this->expectApiErrorCode( 'missingcontent-pageid' );
$this->getDb()->newDeleteQueryBuilder()
->deleteFrom( 'revision' )
->where( [ 'rev_id' => $status->getNewRevision()->getId() ] )
->caller( __METHOD__ )
->execute();
// Ignore warning from WikiPage::getContentModel
@$this->doApiRequest( [
'action' => 'parse',
'page' => $name,
'section' => 1,
] );
}
public function testNewSectionWithPage() {
$this->expectApiErrorCode( 'invalidparammix' );
$this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
'section' => 'new',
] );
}
public function testNonexistentOldId() {
$this->expectApiErrorCode( 'nosuchrevid' );
$this->doApiRequest( [
'action' => 'parse',
'oldid' => pow( 2, 31 ) - 1,
] );
}
public function testUnfollowedRedirect() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( $name, "#REDIRECT [[$name 2]]" );
$this->editPage( "$name 2", "Some ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => $name,
] );
// Can't use assertParsedTo because the parser output is different for
// redirects
$this->assertMatchesRegularExpression( "/Redirect to:.*$name 2/", $res[0]['parse']['text'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testFollowedRedirect() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( $name, "#REDIRECT [[$name 2]]" );
$this->editPage( "$name 2", "Some ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => $name,
'redirects' => true,
] );
$this->assertParsedTo( "Some text\n
", $res );
}
public function testFollowedRedirectById() {
$name = ucfirst( __FUNCTION__ );
$id = $this->editPage( $name, "#REDIRECT [[$name 2]]" )
->getNewRevision()->getPageId();
$this->editPage( "$name 2", "Some ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'pageid' => $id,
'redirects' => true,
] );
$this->assertParsedTo( "Some text\n
", $res );
}
public function testNonRedirectOk() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( $name, "Some ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => $name,
'redirects' => true,
] );
$this->assertParsedTo( "Some text\n
", $res );
}
public function testNonRedirectByIdOk() {
$name = ucfirst( __FUNCTION__ );
$id = $this->editPage( $name, "Some ''text''" )->getNewRevision()->getPageId();
$res = $this->doApiRequest( [
'action' => 'parse',
'pageid' => $id,
'redirects' => true,
] );
$this->assertParsedTo( "Some text\n
", $res );
}
public function testInvalidTitle() {
$this->expectApiErrorCode( 'invalidtitle' );
$this->doApiRequest( [
'action' => 'parse',
'title' => '|',
] );
}
public function testTitleWithNonexistentRevId() {
$this->expectApiErrorCode( 'nosuchrevid' );
$this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'revid' => pow( 2, 31 ) - 1,
] );
}
public function testTitleWithNonMatchingRevId() {
$name = ucfirst( __FUNCTION__ );
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => $name,
'revid' => self::$revIds['latest'],
'text' => 'Some text',
] );
$this->assertParsedTo( "Some text\n
", $res,
'r' . self::$revIds['latest'] . " is not a revision of $name." );
}
public function testRevId() {
$res = $this->doApiRequest( [
'action' => 'parse',
'revid' => self::$revIds['latest'],
'text' => 'My revid is {{REVISIONID}}!',
] );
$this->assertParsedTo( "My revid is " . self::$revIds['latest'] . "!\n
", $res );
}
public function testTitleNoText() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => 'Special:AllPages',
] );
$this->assertParsedTo( '', $res,
'"title" used without "text", and parsed page properties were requested. ' .
'Did you mean to use "page" instead of "title"?' );
}
public function testRevidNoText() {
$res = $this->doApiRequest( [
'action' => 'parse',
'revid' => self::$revIds['latest'],
] );
$this->assertParsedTo( '', $res,
'"revid" used without "text", and parsed page properties were requested. ' .
'Did you mean to use "oldid" instead of "revid"?' );
}
public function testTextNoContentModel() {
$res = $this->doApiRequest( [
'action' => 'parse',
'text' => "Some ''text''",
] );
$this->assertParsedTo( "Some text\n
", $res,
'No "title" or "contentmodel" was given, assuming wikitext.' );
}
public function testSerializationError() {
$this->expectApiErrorCode( 'parseerror' );
$this->mergeMwGlobalArrayValue( 'wgContentHandlers',
[ 'testing-serialize-error' => 'DummySerializeErrorContentHandler' ] );
$this->doApiRequest( [
'action' => 'parse',
'text' => "Some ''text''",
'contentmodel' => 'testing-serialize-error',
] );
}
public function testNewSection() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'section' => 'new',
'sectiontitle' => 'Title',
'text' => 'Content',
] );
$this->assertParsedToRegExp( '!]*>.*Title.*
.*\nContent\n
!', $res );
}
public function testExistingSection() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'section' => 1,
'text' => "Intro\n\n== Section 1 ==\n\nContent\n\n== Section 2 ==\n\nMore content",
] );
$this->assertParsedToRegExp( '!]*>.*Section 1.*
.*\nContent\n
!', $res );
}
public function testNoPst() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( "Template:$name", "Template ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'text' => "{{subst:$name}}",
'contentmodel' => 'wikitext',
] );
$this->assertParsedTo( "{{subst:$name}}\n
", $res );
}
public function testPst() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( "Template:$name", "Template ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'pst' => '',
'text' => "{{subst:$name}}",
'contentmodel' => 'wikitext',
'prop' => 'text|wikitext',
] );
$this->assertParsedTo( "Template text\n
", $res );
$this->assertSame( "{{subst:$name}}", $res[0]['parse']['wikitext'] );
}
public function testOnlyPst() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( "Template:$name", "Template ''text''" );
$res = $this->doApiRequest( [
'action' => 'parse',
'onlypst' => '',
'text' => "{{subst:$name}}",
'contentmodel' => 'wikitext',
'prop' => 'text|wikitext',
'summary' => 'Summary',
] );
$this->assertSame(
[ 'parse' => [
'text' => "Template ''text''",
'wikitext' => "{{subst:$name}}",
'parsedsummary' => 'Summary',
] ],
$res[0]
);
}
/** @dataProvider providerTestParsoid */
public function testParsoid( $parsoid, $existing, $expected ) {
# For simplicity, ensure that [[Foo]] isn't a redlink.
$this->editPage( "Foo", __FUNCTION__ );
$res = $this->doApiRequest( [
# check that we're using the contents of 'text' not the contents of
# [[]] by using pre-existing title __CLASS__ sometimes
'title' => $existing ? __CLASS__ : 'Bar',
'action' => 'parse',
'text' => "[[Foo]]",
'contentmodel' => 'wikitext',
'parsoid' => $parsoid ?: null,
'disablelimitreport' => true,
] );
$this->assertParsedToRegexp( $expected, $res );
}
public static function providerTestParsoid() {
// Legacy parses, with and without pre-existing content.
$expected = '!^Foo\n
$!';
yield [ false, false, $expected ];
yield [ false, true, $expected ];
// Parsoid parses, with and without pre-existing content.
$expected = '!^!';
yield [ true, false, $expected ];
yield [ true, true, $expected ];
}
/** @dataProvider providerTestParsoid */
public function testUseArticle( $parsoid, $existing, $expected ) {
# For simplicity, ensure that [[Foo]] isn't a redlink.
$this->editPage( "Foo", __FUNCTION__ );
# Use an ArticleParserOptions hook to set the useParsoid option
$this->setTemporaryHook( 'ArticleParserOptions',
static function ( $unused, $po ) use ( $parsoid ) {
if ( $parsoid ) {
$po->setUseParsoid();
}
}
);
$res = $this->doApiRequest( [
# check that we're using the contents of 'text' not the contents of
# [[]] by using pre-existing title __CLASS__ sometimes
'title' => $existing ? __CLASS__ : 'Bar',
'action' => 'parse',
'text' => "[[Foo]]",
'contentmodel' => 'wikitext',
'usearticle' => true,
# Note that we're not passing the 'parsoid' parameter here.
'disablelimitreport' => true,
] );
$this->assertParsedToRegexp( $expected, $res );
}
public function testHeadHtml() {
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
'prop' => 'headhtml',
] );
// Just do a rough check
$this->assertMatchesRegularExpression( '#.*assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testCategoriesHtml() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( $name, "[[Category:$name]]" );
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => $name,
'prop' => 'categorieshtml',
] );
$this->assertMatchesRegularExpression( "#Category.*Category:$name.*$name#",
$res[0]['parse']['categorieshtml'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testEffectiveLangLinks() {
$hookRan = false;
$this->setTemporaryHook( 'LanguageLinks',
static function () use ( &$hookRan ) {
$hookRan = true;
}
);
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' => '[[zh:' . __CLASS__ . ']]',
'effectivelanglinks' => '',
] );
$this->assertTrue( $hookRan );
$this->assertSame( 'The parameter "effectivelanglinks" has been deprecated.',
$res[0]['warnings']['parse']['warnings'] );
}
/**
* @param array $arr Extra params to add to API request
*/
private function doTestLangLinks( array $arr = [] ) {
$this->setTemporaryHook( 'ParserAfterParse',
static function ( $parser ) {
$parserOutput = $parser->getOutput();
$parserOutput->addLanguageLink( 'talk:Page' ); // T363538
}
);
$res = $this->doApiRequest( array_merge( [
'action' => 'parse',
'title' => 'Omelette',
'text' => '[[madeuplanguage:Omelette]]',
'prop' => 'langlinks',
], $arr ) );
$langLinks = $res[0]['parse']['langlinks'];
$this->assertCount( 2, $langLinks );
$this->assertSame( 'madeuplanguage', $langLinks[0]['lang'] );
$this->assertSame( 'Omelette', $langLinks[0]['title'] );
$this->assertSame( 'https://example.com/wiki/Omelette', $langLinks[0]['url'] );
$this->assertSame( 'talk', $langLinks[1]['lang'] );
$this->assertSame( 'Page', $langLinks[1]['title'] );
$this->assertSame( 'https://talk.example.com/wiki/Page', $langLinks[1]['url'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testLangLinks() {
$this->setupInterwiki();
$this->doTestLangLinks();
}
public function testLangLinksWithSkin() {
$this->setupInterwiki();
$this->setupSkin();
$this->doTestLangLinks( [ 'useskin' => 'testing' ] );
}
public function testHeadItems() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' => '',
'prop' => 'headitems',
] );
$this->assertSame( [], $res[0]['parse']['headitems'] );
$this->assertSame(
'"prop=headitems" is deprecated since MediaWiki 1.28. ' .
'Use "prop=headhtml" when creating new HTML documents, ' .
'or "prop=modules|jsconfigvars" when updating a document client-side.',
$res[0]['warnings']['parse']['warnings']
);
}
public function testHeadItemsWithSkin() {
$this->setupSkin();
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' => '',
'prop' => 'headitems',
'useskin' => 'testing',
] );
$this->assertSame( [], $res[0]['parse']['headitems'] );
$this->assertSame(
'"prop=headitems" is deprecated since MediaWiki 1.28. ' .
'Use "prop=headhtml" when creating new HTML documents, ' .
'or "prop=modules|jsconfigvars" when updating a document client-side.',
$res[0]['warnings']['parse']['warnings']
);
}
public function testModules() {
$this->setTemporaryHook( 'ParserAfterParse',
static function ( $parser ) {
$parserOutput = $parser->getOutput();
$parserOutput->addModules( [ 'foo', 'bar' ] );
$parserOutput->addModuleStyles( [ 'aaa', 'zzz' ] );
$parserOutput->setJsConfigVar( 'x', 'y' );
$parserOutput->setJsConfigVar( 'z', -3 );
}
);
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' => 'Content',
'prop' => 'modules|jsconfigvars|encodedjsconfigvars',
] );
$this->assertSame( [ 'foo', 'bar' ], $res[0]['parse']['modules'] );
$this->assertSame( [], $res[0]['parse']['modulescripts'] );
$this->assertSame( [ 'aaa', 'zzz' ], $res[0]['parse']['modulestyles'] );
$this->assertSame( [ 'x' => 'y', 'z' => -3 ], $res[0]['parse']['jsconfigvars'] );
$this->assertSame( '{"x":"y","z":-3}', $res[0]['parse']['encodedjsconfigvars'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testModulesWithSkin() {
$this->setupSkin();
$res = $this->doApiRequest( [
'action' => 'parse',
'pageid' => self::$pageId,
'useskin' => 'testing',
'prop' => 'modules',
] );
$this->assertSame(
[ 'foo', 'bar', 'baz' ],
$res[0]['parse']['modules'],
'resp.parse.modules'
);
$this->assertSame(
[],
$res[0]['parse']['modulescripts'],
'resp.parse.modulescripts'
);
$this->assertSame(
[ 'quux.styles' ],
$res[0]['parse']['modulestyles'],
'resp.parse.modulestyles'
);
$this->assertSame(
[ 'parse' =>
[ 'warnings' =>
'Property "modules" was set but not "jsconfigvars" or ' .
'"encodedjsconfigvars". Configuration variables are necessary for ' .
'proper module usage.'
]
],
$res[0]['warnings']
);
}
public function testIndicators() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' =>
'BBB!Some textaaa',
'prop' => 'indicators',
] );
$this->assertSame(
// It seems we return in markup order and not display order
[ 'b' => 'BBB!', 'a' => 'aaa' ],
$res[0]['parse']['indicators']
);
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testIndicatorsWithSkin() {
$this->setupSkin();
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' =>
'BBB!Some textaaa',
'prop' => 'indicators',
'useskin' => 'testing',
] );
$this->assertSame(
// Now we return in display order rather than markup order
[
'a' => 'aaa
',
'b' => 'BBB!
',
],
$res[0]['parse']['indicators']
);
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testIwlinks() {
$this->setupInterwiki();
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => 'Omelette',
'text' => '[[:madeuplanguage:Omelette]][[madeuplanguage:Spaghetti]]',
'prop' => 'iwlinks',
] );
$iwlinks = $res[0]['parse']['iwlinks'];
$this->assertCount( 1, $iwlinks );
$this->assertSame( 'madeuplanguage', $iwlinks[0]['prefix'] );
$this->assertSame( 'https://example.com/wiki/Omelette', $iwlinks[0]['url'] );
$this->assertSame( 'madeuplanguage:Omelette', $iwlinks[0]['title'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testLimitReports() {
$res = $this->doApiRequest( [
'action' => 'parse',
'pageid' => self::$pageId,
'prop' => 'limitreportdata|limitreporthtml',
] );
// We don't bother testing the actual values here
$this->assertIsArray( $res[0]['parse']['limitreportdata'] );
$this->assertIsString( $res[0]['parse']['limitreporthtml'] );
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testParseTreeNonWikitext() {
$this->expectApiErrorCode( 'notwikitext' );
$this->doApiRequest( [
'action' => 'parse',
'text' => '',
'contentmodel' => 'json',
'prop' => 'parsetree',
] );
}
public function testParseTree() {
$res = $this->doApiRequest( [
'action' => 'parse',
'text' => "Some ''text'' is {{nice|to have|i=think}}",
'contentmodel' => 'wikitext',
'prop' => 'parsetree',
] );
$this->assertEquals(
'Some \'\'text\'\' is nice' .
'to have' .
'i=think' .
'',
$res[0]['parse']['parsetree']
);
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testFormatCategories() {
$name = ucfirst( __FUNCTION__ );
$this->editPage( "Category:$name", 'Content' );
$this->editPage( 'Category:Hidden', '__HIDDENCAT__' );
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'text' => "[[Category:$name]][[Category:Foo|Sort me]][[Category:Hidden]]",
'prop' => 'categories',
] );
$this->assertSame(
[ [ 'sortkey' => '', 'category' => $name ],
[ 'sortkey' => 'Sort me', 'category' => 'Foo', 'missing' => true ],
[ 'sortkey' => '', 'category' => 'Hidden', 'hidden' => true ] ],
$res[0]['parse']['categories']
);
$this->assertArrayNotHasKey( 'warnings', $res[0] );
}
public function testConcurrentLimitPageParse() {
$this->overrideConfigValue(
MainConfigNames::PoolCounterConf,
[
'ApiParser' => [
'class' => MockPoolCounterFailing::class,
]
]
);
try {
$this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
] );
$this->fail( "API did not return an error when concurrency exceeded" );
} catch ( ApiUsageException $ex ) {
$this->assertApiErrorCode( 'concurrency-limit', $ex );
}
}
public function testConcurrentLimitContentParse() {
$this->overrideConfigValue(
MainConfigNames::PoolCounterConf,
[
'ApiParser' => [
'class' => MockPoolCounterFailing::class,
]
]
);
try {
$this->doApiRequest( [
'action' => 'parse',
'oldid' => self::$revIds['revdel'],
] );
$this->fail( "API did not return an error when concurrency exceeded" );
} catch ( ApiUsageException $ex ) {
$this->assertApiErrorCode( 'concurrency-limit', $ex );
}
}
public function testDisplayTitle() {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => 'Art©',
'text' => '{{DISPLAYTITLE:art©}}foo',
'prop' => 'displaytitle',
] );
$this->assertSame(
'art©',
$res[0]['parse']['displaytitle']
);
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => 'Art©',
'text' => 'foo',
'prop' => 'displaytitle',
] );
$this->assertSame(
'Art©',
$res[0]['parse']['displaytitle']
);
}
public function testIncompatFormat() {
$this->expectApiErrorCode( 'badformat-generic' );
$this->doApiRequest( [
'action' => 'parse',
'prop' => 'categories',
'title' => __CLASS__,
'text' => '',
'contentformat' => 'application/json',
] );
}
public function testIgnoreFormatUsingPage() {
$res = $this->doApiRequest( [
'action' => 'parse',
'page' => __CLASS__,
'prop' => 'wikitext',
'contentformat' => 'text/plain',
] );
$this->assertArrayHasKey( 'wikitext', $res[0]['parse'] );
}
public function testShouldCastNumericImageLinksToString(): void {
$res = $this->doApiRequest( [
'action' => 'parse',
'title' => __CLASS__,
'prop' => 'images',
'text' => '[[File:1]]',
] );
$this->assertSame( [ '1' ], $res[0]['parse']['images'] );
}
}