wiki.techinc.nl/tests/phpunit/includes/ExtraParserTest.php
C. Scott Ananian 0eaaceea3e Hard-deprecate direct calls to Parser::__construct()
These were deprecated in 1.34, but let's put in some hard deprecation
warnings for 1.35 since this class is certainly going to be refactored
in the future to allow both the legacy parser and Parsoid to extend
Parser as a base class.  Access via the ParserFactory will be fine,
but cut down on the number of different ways Parsers can be constructed
and initialized.

Code search:
https://codesearch.wmflabs.org/deployed/?q=new%20Parser%5C%28&i=nope&files=&repos=
https://codesearch.wmflabs.org/deployed/?q=getMockBuilder%5C%28%20Parser%3A%3A&i=nope&files=&repos=
https://codesearch.wmflabs.org/deployed/?q=new%20Parser%3B&i=nope&files=&repos=

Bug: T236811
Depends-On: Ib3be450c55e1793b027d9b4dae692ba5891b0328
Depends-On: I9d16513f8bd449a43b0a0afbd73651a5c0afa588
Depends-On: I74efda708470efeb82f8f80346ec1ee7e9fd8f2b
Depends-On: I777475d0ab0144e53240173f501d6c8da35d33fb
Change-Id: If36283ec0b78b188b61f658639105d1ed7653e0a
2020-04-16 16:34:34 -04:00

349 lines
9.3 KiB
PHP

<?php
use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
/**
* Parser-related tests that don't suit for parserTests.txt
*
* @group Database
*/
class ExtraParserTest extends MediaWikiTestCase {
/** @var ParserOptions */
protected $options;
/** @var Parser */
protected $parser;
protected function setUp() : void {
parent::setUp();
$this->setMwGlobals( [
'wgShowExceptionDetails' => true,
'wgCleanSignatures' => true,
] );
$this->setUserLang( 'en' );
$this->setContentLang( 'en' );
$services = MediaWikiServices::getInstance();
$contLang = $services->getContentLanguage();
// FIXME: This test should pass without setting global content language
$this->options = ParserOptions::newFromUserAndLang( new User, $contLang );
$this->options->setTemplateCallback( [ __CLASS__, 'statelessFetchTemplate' ] );
$services->resetServiceForTesting( 'MagicWordFactory' );
$services->resetServiceForTesting( 'ParserFactory' );
$this->parser = $services->getParserFactory()->create();
}
/**
* @see T10689
* @covers Parser::parse
*/
public function testLongNumericLinesDontKillTheParser() {
$longLine = '1.' . str_repeat( '1234567890', 100000 ) . "\n";
$title = Title::newFromText( 'Unit test' );
$options = ParserOptions::newFromUser( new User() );
$this->assertEquals( "<p>$longLine</p>",
$this->parser->parse( $longLine, $title, $options )->getText( [ 'unwrap' => true ] ) );
}
/**
* @covers Parser::braceSubstitution
* @covers SpecialPageFactory::capturePath
*/
public function testSpecialPageTransclusionRestoresGlobalState() {
$text = "{{Special:ApiHelp/help}}";
$title = Title::newFromText( 'testSpecialPageTransclusionRestoresGlobalState' );
$options = ParserOptions::newFromUser( new User() );
RequestContext::getMain()->setTitle( $title );
RequestContext::getMain()->getWikiPage()->CustomTestProp = true;
$parsed = $this->parser->parse( $text, $title, $options )->getText();
$this->assertStringContainsString( 'apihelp-header', $parsed );
// Verify that this property wasn't wiped out by the parse
$this->assertTrue( RequestContext::getMain()->getWikiPage()->CustomTestProp );
}
/**
* Test the parser entry points
* @covers Parser::parse
*/
public function testParse() {
$title = Title::newFromText( __FUNCTION__ );
$parserOutput = $this->parser->parse( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options );
$this->assertEquals(
"<p>Test\nContent of <i>Template:Foo</i>\nContent of <i>Template:Bar</i>\n</p>",
$parserOutput->getText( [ 'unwrap' => true ] )
);
}
/**
* @covers Parser::preSaveTransform
*/
public function testPreSaveTransform() {
$title = Title::newFromText( __FUNCTION__ );
$outputText = $this->parser->preSaveTransform(
"Test\r\n{{subst:Foo}}\n{{Bar}}",
$title,
new User(),
$this->options
);
$this->assertEquals( "Test\nContent of ''Template:Foo''\n{{Bar}}", $outputText );
}
/**
* @covers Parser::preprocess
*/
public function testPreprocess() {
$title = Title::newFromText( __FUNCTION__ );
$outputText = $this->parser->preprocess( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options );
$this->assertEquals(
"Test\nContent of ''Template:Foo''\nContent of ''Template:Bar''",
$outputText
);
}
/**
* cleanSig() makes all templates substs and removes tildes
* @covers Parser::cleanSig
*/
public function testCleanSig() {
$title = Title::newFromText( __FUNCTION__ );
$outputText = $this->parser->cleanSig( "{{Foo}} ~~~~" );
$this->assertEquals( "{{SUBST:Foo}} ", $outputText );
}
/**
* cleanSig() should do nothing if disabled
* @covers Parser::cleanSig
*/
public function testCleanSigDisabled() {
$this->setMwGlobals( 'wgCleanSignatures', false );
$title = Title::newFromText( __FUNCTION__ );
$outputText = $this->parser->cleanSig( "{{Foo}} ~~~~" );
$this->assertEquals( "{{Foo}} ~~~~", $outputText );
}
/**
* cleanSigInSig() just removes tildes
* @dataProvider provideStringsForCleanSigInSig
* @covers Parser::cleanSigInSig
*/
public function testCleanSigInSig( $in, $out ) {
$this->assertEquals( Parser::cleanSigInSig( $in ), $out );
}
public static function provideStringsForCleanSigInSig() {
return [
[ "{{Foo}} ~~~~", "{{Foo}} " ],
[ "~~~", "" ],
[ "~~~~~", "" ],
];
}
/**
* @covers Parser::getSection
*/
public function testGetSection() {
$outputText2 = $this->parser->getSection(
"Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\n"
. "Section 2\n== Heading 3 ==\nSection 3\n",
2
);
$outputText1 = $this->parser->getSection(
"Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\n"
. "Section 2\n== Heading 3 ==\nSection 3\n",
1
);
$this->assertEquals( "=== Heading 2 ===\nSection 2", $outputText2 );
$this->assertEquals( "== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2", $outputText1 );
}
/**
* @covers Parser::replaceSection
*/
public function testReplaceSection() {
$outputText = $this->parser->replaceSection(
"Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\n"
. "Section 2\n== Heading 3 ==\nSection 3\n",
1,
"New section 1"
);
$this->assertEquals( "Section 0\nNew section 1\n\n== Heading 3 ==\nSection 3", $outputText );
}
/**
* Templates and comments are not affected, but noinclude/onlyinclude is.
* @covers Parser::getPreloadText
*/
public function testGetPreloadText() {
$title = Title::newFromText( __FUNCTION__ );
$outputText = $this->parser->getPreloadText(
"{{Foo}}<noinclude> censored</noinclude> information <!-- is very secret -->",
$title,
$this->options
);
$this->assertEquals( "{{Foo}} information <!-- is very secret -->", $outputText );
}
/**
* @param Title $title
* @param bool $parser
*
* @return array
*/
public static function statelessFetchTemplate( $title, $parser = false ) {
$text = "Content of ''" . $title->getFullText() . "''";
$deps = [];
return [
'text' => $text,
'finalTitle' => $title,
'deps' => $deps ];
}
/**
* @covers Parser::parse
*/
public function testTrackingCategory() {
$title = Title::newFromText( __FUNCTION__ );
$catName = wfMessage( 'broken-file-category' )->inContentLanguage()->text();
$cat = Title::makeTitleSafe( NS_CATEGORY, $catName );
$expected = [ $cat->getDBkey() ];
$parserOutput = $this->parser->parse( "[[file:nonexistent]]", $title, $this->options );
$result = $parserOutput->getCategoryLinks();
$this->assertEquals( $expected, $result );
}
/**
* @covers Parser::parse
*/
public function testTrackingCategorySpecial() {
// Special pages shouldn't have tracking cats.
$title = SpecialPage::getTitleFor( 'Contributions' );
$parserOutput = $this->parser->parse( "[[file:nonexistent]]", $title, $this->options );
$result = $parserOutput->getCategoryLinks();
$this->assertSame( [], $result );
}
/**
* @covers Parser::parseLinkParameter
* @dataProvider provideParseLinkParameter
*/
public function testParseLinkParameter( $input, $expected, $expectedLinks, $desc ) {
$this->setTemporaryHook( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
static $testInterwikis = [
'local' => [
'iw_url' => 'http://doesnt.matter.invalid/$1',
'iw_api' => '',
'iw_wikiid' => '',
'iw_local' => 0
],
'mw' => [
'iw_url' => 'https://www.mediawiki.org/wiki/$1',
'iw_api' => 'https://www.mediawiki.org/w/api.php',
'iw_wikiid' => '',
'iw_local' => 0
]
];
if ( array_key_exists( $prefix, $testInterwikis ) ) {
$iwData = $testInterwikis[$prefix];
}
// We only want to rely on the above fixtures
return false;
} );
Title::clearCaches();
$this->parser->startExternalParse(
Title::newFromText( __FUNCTION__ ),
$this->options,
Parser::OT_HTML
);
$output = TestingAccessWrapper::newFromObject( $this->parser )
->parseLinkParameter( $input );
$this->assertEquals( $expected[0], $output[0], "$desc (type)" );
if ( $expected[0] === 'link-title' ) {
$this->assertTrue(
$output[1]->equals( Title::newFromText( $expected[1] ) ),
"$desc (target); link list title instance matches new title instance"
);
} else {
$this->assertEquals( $expected[1], $output[1], "$desc (target)" );
}
foreach ( $expectedLinks as $func => $expected ) {
$output = $this->parser->getOutput()->$func();
$this->assertEquals( $expected, $output, "$desc ($func)" );
}
}
public static function provideParseLinkParameter() {
return [
[
'',
[ 'no-link', false ],
[],
'Return no link when requested',
],
[
'https://example.com/',
[ 'link-url', 'https://example.com/' ],
[ 'getExternalLinks' => [ 'https://example.com/' => 1 ] ],
'External link',
],
[
'//example.com/',
[ 'link-url', '//example.com/' ],
[ 'getExternalLinks' => [ '//example.com/' => 1 ] ],
'External link',
],
[
'Test',
[ 'link-title', 'Test' ],
[ 'getLinks' => [ 0 => [ 'Test' => 0 ] ] ],
'Internal link',
],
[
'mw:Test',
[ 'link-title', 'mw:Test' ],
[ 'getInterwikiLinks' => [ 'mw' => [ 'Test' => 1 ] ] ],
'Internal link (interwiki)',
],
[
'https://',
[ null, false ],
[],
'Invalid link target',
],
[
'<>',
[ null, false ],
[],
'Invalid link target',
],
[
' ',
[ null, false ],
[],
'Invalid link target',
],
];
}
}