2012-09-29 10:36:33 +00:00
|
|
|
<?php
|
2019-05-28 14:04:23 +00:00
|
|
|
|
2024-02-17 00:26:05 +00:00
|
|
|
namespace MediaWiki\Tests\Parser;
|
|
|
|
|
|
|
|
|
|
use LogicException;
|
2024-02-08 14:56:54 +00:00
|
|
|
use MediaWiki\Context\RequestContext;
|
2024-05-03 19:28:04 +00:00
|
|
|
use MediaWiki\Debug\MWDebug;
|
2022-08-01 19:14:41 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2023-12-14 19:20:33 +00:00
|
|
|
use MediaWiki\Parser\ParserOutput;
|
2024-01-12 05:07:33 +00:00
|
|
|
use MediaWiki\Parser\ParserOutputFlags;
|
2023-07-27 19:47:00 +00:00
|
|
|
use MediaWiki\Parser\ParserOutputStringSets;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2023-09-18 13:56:39 +00:00
|
|
|
use MediaWiki\Title\TitleValue;
|
2023-08-19 03:35:06 +00:00
|
|
|
use MediaWiki\Utils\MWTimestamp;
|
2024-02-17 00:26:05 +00:00
|
|
|
use MediaWikiLangTestCase;
|
|
|
|
|
use ParserOptions;
|
Add ParserOutput::getLanguage()
Provide a way for backend code to determine the primary language of a
ParserOutput, eg for setting the Content-Language header of an API
response.
This is read-only and backed by extension data at the moment for
transition purposes; if this API sticks we'll graduate it to a
"real" property in the future, with appropriate serialization
to/from JSON (T303329).
Similarly, this patch only includes the most basic code to handle
the various ParserOutput merge cases in
ParserOutput::merge{Internal,Html,Tracking}MetaDataFrom(),
ParserOutput::collectMetadata(), and
OutputPage::addParserOutput{Content,Metadata,Text,}(); mostly
inherited from the fact that the storage is backed by extension
data at the moment.
Generally only the "top-level" parser output gets to set the
primary language; we'll presumably need to ensure that the
language is consistent during merge.
Change-Id: I767daba22805a877d9b806fd77334e508902844b
2022-09-28 21:21:13 +00:00
|
|
|
use Wikimedia\Bcp47Code\Bcp47CodeValue;
|
2022-09-01 23:07:29 +00:00
|
|
|
use Wikimedia\Parsoid\Core\SectionMetadata;
|
2023-01-19 20:37:16 +00:00
|
|
|
use Wikimedia\Parsoid\Core\TOCData;
|
2018-08-07 16:52:40 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2020-11-05 17:04:37 +00:00
|
|
|
use Wikimedia\Tests\SerializationTestTrait;
|
2012-09-29 10:36:33 +00:00
|
|
|
|
2015-01-09 19:16:00 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput
|
2024-02-16 18:04:47 +00:00
|
|
|
* @covers \CacheTime
|
2015-01-09 19:16:00 +00:00
|
|
|
* @group Database
|
|
|
|
|
* ^--- trigger DB shadowing because we are using Title magic
|
|
|
|
|
*/
|
2018-08-07 16:52:40 +00:00
|
|
|
class ParserOutputTest extends MediaWikiLangTestCase {
|
2020-10-02 20:36:23 +00:00
|
|
|
use SerializationTestTrait;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void {
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
MWTimestamp::setFakeTime( ParserCacheSerializationTestCases::FAKE_TIME );
|
2022-08-01 19:14:41 +00:00
|
|
|
$this->overrideConfigValue(
|
|
|
|
|
MainConfigNames::ParserCacheExpireTime,
|
|
|
|
|
ParserCacheSerializationTestCases::FAKE_CACHE_EXPIRY
|
|
|
|
|
);
|
2020-10-02 20:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overrides SerializationTestTrait::getClassToTest
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2020-11-26 18:02:57 +00:00
|
|
|
protected function getClassToTest(): string {
|
2020-10-02 20:36:23 +00:00
|
|
|
return ParserOutput::class;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overrides SerializationTestTrait::getSerializedDataPath
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
protected function getSerializedDataPath(): string {
|
|
|
|
|
return __DIR__ . '/../../data/ParserCache';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overrides SerializationTestTrait::getTestInstancesAndAssertions
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
protected function getTestInstancesAndAssertions(): array {
|
|
|
|
|
return ParserCacheSerializationTestCases::getParserOutputTestCases();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overrides SerializationTestTrait::getSupportedSerializationFormats
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
protected function getSupportedSerializationFormats(): array {
|
|
|
|
|
return ParserCacheSerializationTestCases::getSupportedSerializationFormats(
|
|
|
|
|
$this->getClassToTest() );
|
|
|
|
|
}
|
2012-09-29 10:36:33 +00:00
|
|
|
|
2013-10-22 23:50:38 +00:00
|
|
|
public static function provideIsLinkInternal() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
2012-09-29 10:36:33 +00:00
|
|
|
// Different domains
|
2016-02-17 09:09:32 +00:00
|
|
|
[ false, 'http://example.org', 'http://mediawiki.org' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// Same domains
|
2016-02-17 09:09:32 +00:00
|
|
|
[ true, 'http://example.org', 'http://example.org' ],
|
|
|
|
|
[ true, 'https://example.org', 'https://example.org' ],
|
|
|
|
|
[ true, '//example.org', '//example.org' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// Same domain different cases
|
2016-02-17 09:09:32 +00:00
|
|
|
[ true, 'http://example.org', 'http://EXAMPLE.ORG' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// Paths, queries, and fragments are not relevant
|
2016-02-17 09:09:32 +00:00
|
|
|
[ true, 'http://example.org', 'http://example.org/wiki/Main_Page' ],
|
|
|
|
|
[ true, 'http://example.org', 'http://example.org?my=query' ],
|
|
|
|
|
[ true, 'http://example.org', 'http://example.org#its-a-fragment' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// Different protocols
|
2016-02-17 09:09:32 +00:00
|
|
|
[ false, 'http://example.org', 'https://example.org' ],
|
|
|
|
|
[ false, 'https://example.org', 'http://example.org' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// Protocol relative servers always match http and https links
|
2016-02-17 09:09:32 +00:00
|
|
|
[ true, '//example.org', 'http://example.org' ],
|
|
|
|
|
[ true, '//example.org', 'https://example.org' ],
|
2012-09-29 10:36:33 +00:00
|
|
|
// But they don't match strange things like this
|
2016-02-17 09:09:32 +00:00
|
|
|
[ false, '//example.org', 'irc://example.org' ],
|
|
|
|
|
];
|
2012-09-29 10:36:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test to make sure ParserOutput::isLinkInternal behaves properly
|
2013-10-22 23:50:38 +00:00
|
|
|
* @dataProvider provideIsLinkInternal
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::isLinkInternal
|
2012-09-29 10:36:33 +00:00
|
|
|
*/
|
2013-10-22 23:50:38 +00:00
|
|
|
public function testIsLinkInternal( $shouldMatch, $server, $url ) {
|
2012-09-29 10:36:33 +00:00
|
|
|
$this->assertEquals( $shouldMatch, ParserOutput::isLinkInternal( $server, $url ) );
|
|
|
|
|
}
|
2013-01-22 11:59:23 +00:00
|
|
|
|
2022-01-27 19:44:35 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::appendJsConfigVar
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setJsConfigVar
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getJsConfigVars
|
2022-01-27 19:44:35 +00:00
|
|
|
*/
|
|
|
|
|
public function testJsConfigVars() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$po->setJsConfigVar( 'a', '1' );
|
|
|
|
|
$po->appendJsConfigVar( 'b', 'a' );
|
|
|
|
|
$po->appendJsConfigVar( 'b', '0' );
|
|
|
|
|
|
|
|
|
|
$this->assertEqualsCanonicalizing( [
|
|
|
|
|
'a' => 1,
|
|
|
|
|
'b' => [ 'a' => true, '0' => true ],
|
|
|
|
|
], $po->getJsConfigVars() );
|
|
|
|
|
|
|
|
|
|
$po->setJsConfigVar( 'c', '2' );
|
|
|
|
|
$po->appendJsConfigVar( 'b', 'b' );
|
|
|
|
|
$po->appendJsConfigVar( 'b', '1' );
|
|
|
|
|
|
|
|
|
|
$this->assertEqualsCanonicalizing( [
|
|
|
|
|
'a' => 1,
|
|
|
|
|
'b' => [ 'a' => true, 'b' => true, '0' => true, '1' => true ],
|
|
|
|
|
'c' => 2,
|
|
|
|
|
], $po->getJsConfigVars() );
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-22 23:50:38 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::appendExtensionData
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setExtensionData
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getExtensionData
|
2013-10-22 23:50:38 +00:00
|
|
|
*/
|
2013-01-22 11:59:23 +00:00
|
|
|
public function testExtensionData() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$po->setExtensionData( "one", "Foo" );
|
2022-01-28 17:09:22 +00:00
|
|
|
$po->appendExtensionData( "three", "abc" );
|
2013-01-22 11:59:23 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
|
|
|
|
|
$this->assertNull( $po->getExtensionData( "spam" ) );
|
|
|
|
|
|
|
|
|
|
$po->setExtensionData( "two", "Bar" );
|
|
|
|
|
$this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
|
|
|
|
|
$this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
|
|
|
|
|
|
2022-01-28 17:09:22 +00:00
|
|
|
// Note that overwriting extension data (as this test case
|
|
|
|
|
// does) is deprecated and will eventually throw an
|
|
|
|
|
// exception. However, at the moment it is still worth testing
|
|
|
|
|
// this case to ensure backward compatibility. (T300981)
|
2013-01-22 11:59:23 +00:00
|
|
|
$po->setExtensionData( "one", null );
|
|
|
|
|
$this->assertNull( $po->getExtensionData( "one" ) );
|
|
|
|
|
$this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
|
2022-01-28 17:09:22 +00:00
|
|
|
|
|
|
|
|
$this->assertEqualsCanonicalizing( [
|
|
|
|
|
'abc' => true,
|
|
|
|
|
], $po->getExtensionData( "three" ) );
|
|
|
|
|
|
|
|
|
|
$po->appendExtensionData( "three", "xyz" );
|
|
|
|
|
$this->assertEqualsCanonicalizing( [
|
|
|
|
|
'abc' => true,
|
|
|
|
|
'xyz' => true,
|
|
|
|
|
], $po->getExtensionData( "three" ) );
|
2013-01-22 11:59:23 +00:00
|
|
|
}
|
2014-04-16 09:31:29 +00:00
|
|
|
|
|
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setPageProperty
|
2024-04-12 16:29:24 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setNumericPageProperty
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setUnsortedPageProperty
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getPageProperty
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::unsetPageProperty
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getPageProperties
|
2024-02-16 15:12:12 +00:00
|
|
|
* @dataProvider providePageProperties
|
2014-04-16 09:31:29 +00:00
|
|
|
*/
|
2024-02-16 15:12:12 +00:00
|
|
|
public function testPageProperties( string $setPageProperty, $value1, $value2 ) {
|
2014-04-16 09:31:29 +00:00
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
2024-02-16 15:12:12 +00:00
|
|
|
$po->$setPageProperty( 'foo', $value1 );
|
2014-04-16 09:31:29 +00:00
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$properties = $po->getPageProperties();
|
2024-02-16 15:12:12 +00:00
|
|
|
$this->assertSame( $value1, $po->getPageProperty( 'foo' ) );
|
|
|
|
|
$this->assertSame( $value1, $properties['foo'] );
|
2014-04-16 09:31:29 +00:00
|
|
|
|
2024-02-16 15:12:12 +00:00
|
|
|
$po->$setPageProperty( 'foo', $value2 );
|
2014-04-16 09:31:29 +00:00
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$properties = $po->getPageProperties();
|
2024-02-16 15:12:12 +00:00
|
|
|
$this->assertSame( $value2, $po->getPageProperty( 'foo' ) );
|
|
|
|
|
$this->assertSame( $value2, $properties['foo'] );
|
2014-04-16 09:31:29 +00:00
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$po->unsetPageProperty( 'foo' );
|
2014-04-16 09:31:29 +00:00
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$properties = $po->getPageProperties();
|
2022-02-16 22:03:26 +00:00
|
|
|
$this->assertSame( null, $po->getPageProperty( 'foo' ) );
|
2014-04-16 09:31:29 +00:00
|
|
|
$this->assertArrayNotHasKey( 'foo', $properties );
|
|
|
|
|
}
|
2015-01-09 19:16:00 +00:00
|
|
|
|
2024-02-16 15:12:12 +00:00
|
|
|
public static function providePageProperties() {
|
2024-04-12 16:29:24 +00:00
|
|
|
yield 'Unsorted' => [ 'setUnsortedPageProperty', 'val', 'second val' ];
|
|
|
|
|
yield 'Numeric' => [ 'setNumericPageProperty', 42, 3.14 ];
|
|
|
|
|
yield 'Unsorted (old style)' => [ 'setPageProperty', 'val', 'second val' ];
|
|
|
|
|
yield 'Numeric (old style)' => [ 'setPageProperty', 123, 456 ];
|
2024-02-16 15:12:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-04-12 16:29:24 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setNumericPageProperty
|
2024-02-16 15:12:12 +00:00
|
|
|
*/
|
2024-04-12 16:29:24 +00:00
|
|
|
public function testNumericPageProperties() {
|
2024-02-16 15:12:12 +00:00
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
2024-04-12 16:29:24 +00:00
|
|
|
$po->setNumericPageProperty( 'foo', '123' );
|
2024-02-16 15:12:12 +00:00
|
|
|
|
|
|
|
|
$properties = $po->getPageProperties();
|
|
|
|
|
$this->assertSame( 123, $po->getPageProperty( 'foo' ) );
|
|
|
|
|
$this->assertSame( 123, $properties['foo'] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-04-12 16:29:24 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setUnsortedPageProperty
|
2024-02-16 15:12:12 +00:00
|
|
|
*/
|
2024-04-12 16:29:24 +00:00
|
|
|
public function testUnsortedPageProperties() {
|
2024-02-16 15:12:12 +00:00
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
2024-04-12 16:29:24 +00:00
|
|
|
$po->setUnsortedPageProperty( 'foo', 123 );
|
2024-02-16 15:12:12 +00:00
|
|
|
|
|
|
|
|
$properties = $po->getPageProperties();
|
|
|
|
|
$this->assertSame( '123', $po->getPageProperty( 'foo' ) );
|
|
|
|
|
$this->assertSame( '123', $properties['foo'] );
|
|
|
|
|
}
|
|
|
|
|
|
Add ParserOutput::getLanguage()
Provide a way for backend code to determine the primary language of a
ParserOutput, eg for setting the Content-Language header of an API
response.
This is read-only and backed by extension data at the moment for
transition purposes; if this API sticks we'll graduate it to a
"real" property in the future, with appropriate serialization
to/from JSON (T303329).
Similarly, this patch only includes the most basic code to handle
the various ParserOutput merge cases in
ParserOutput::merge{Internal,Html,Tracking}MetaDataFrom(),
ParserOutput::collectMetadata(), and
OutputPage::addParserOutput{Content,Metadata,Text,}(); mostly
inherited from the fact that the storage is backed by extension
data at the moment.
Generally only the "top-level" parser output gets to set the
primary language; we'll presumably need to ensure that the
language is consistent during merge.
Change-Id: I767daba22805a877d9b806fd77334e508902844b
2022-09-28 21:21:13 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setLanguage
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getLanguage
|
Add ParserOutput::getLanguage()
Provide a way for backend code to determine the primary language of a
ParserOutput, eg for setting the Content-Language header of an API
response.
This is read-only and backed by extension data at the moment for
transition purposes; if this API sticks we'll graduate it to a
"real" property in the future, with appropriate serialization
to/from JSON (T303329).
Similarly, this patch only includes the most basic code to handle
the various ParserOutput merge cases in
ParserOutput::merge{Internal,Html,Tracking}MetaDataFrom(),
ParserOutput::collectMetadata(), and
OutputPage::addParserOutput{Content,Metadata,Text,}(); mostly
inherited from the fact that the storage is backed by extension
data at the moment.
Generally only the "top-level" parser output gets to set the
primary language; we'll presumably need to ensure that the
language is consistent during merge.
Change-Id: I767daba22805a877d9b806fd77334e508902844b
2022-09-28 21:21:13 +00:00
|
|
|
*/
|
|
|
|
|
public function testLanguage() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$langFr = new Bcp47CodeValue( 'fr' );
|
|
|
|
|
$langCrhCyrl = new Bcp47CodeValue( 'crh-cyrl' );
|
|
|
|
|
|
|
|
|
|
// Fallback to null
|
|
|
|
|
$this->assertSame( null, $po->getLanguage() );
|
|
|
|
|
|
|
|
|
|
// Simple case
|
|
|
|
|
$po->setLanguage( $langFr );
|
|
|
|
|
$this->assertSame( $langFr->toBcp47Code(), $po->getLanguage()->toBcp47Code() );
|
|
|
|
|
|
|
|
|
|
// Language with a variant
|
|
|
|
|
$po->setLanguage( $langCrhCyrl );
|
|
|
|
|
$this->assertSame( $langCrhCyrl->toBcp47Code(), $po->getLanguage()->toBcp47Code() );
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 16:48:10 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getWrapperDivClass
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::addWrapperDivClass
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::clearWrapperDivClass
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getText
|
2018-08-28 16:48:10 +00:00
|
|
|
*/
|
|
|
|
|
public function testWrapperDivClass() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
2024-02-09 22:27:00 +00:00
|
|
|
$po->setRawText( 'Kittens' );
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $po->getText() );
|
|
|
|
|
$this->assertStringNotContainsString( '<div', $po->getText() );
|
2018-08-28 16:48:10 +00:00
|
|
|
$this->assertSame( 'Kittens', $po->getRawText() );
|
|
|
|
|
|
|
|
|
|
$po->addWrapperDivClass( 'foo' );
|
|
|
|
|
$text = $po->getText();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringContainsString( 'class="mw-content-ltr foo"', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
|
|
|
|
|
$po->addWrapperDivClass( 'bar' );
|
|
|
|
|
$text = $po->getText();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringContainsString( 'class="mw-content-ltr foo bar"', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
|
|
|
|
|
$po->addWrapperDivClass( 'bar' ); // second time does nothing, no "foo bar bar".
|
|
|
|
|
$text = $po->getText( [ 'unwrap' => true ] );
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringNotContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringNotContainsString( 'class="', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
|
|
|
|
|
$text = $po->getText( [ 'wrapperDivClass' => '' ] );
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringNotContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringNotContainsString( 'class="', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
|
|
|
|
|
$text = $po->getText( [ 'wrapperDivClass' => 'xyzzy' ] );
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringContainsString( 'class="mw-content-ltr xyzzy"', $text );
|
|
|
|
|
$this->assertStringNotContainsString( 'foo bar', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
|
|
|
|
|
$text = $po->getRawText();
|
|
|
|
|
$this->assertSame( 'Kittens', $text );
|
|
|
|
|
|
|
|
|
|
$po->clearWrapperDivClass();
|
|
|
|
|
$text = $po->getText();
|
2019-12-14 10:27:56 +00:00
|
|
|
$this->assertStringContainsString( 'Kittens', $text );
|
|
|
|
|
$this->assertStringNotContainsString( '<div', $text );
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
$this->assertStringNotContainsString( 'class="', $text );
|
2018-08-28 16:48:10 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-22 18:12:23 +00:00
|
|
|
/**
|
2023-10-10 15:05:08 +00:00
|
|
|
* This test aims at being replaced by its version in DefaultOutputTransformTest when ParserOutput::getText
|
|
|
|
|
* gets deprecated.
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getText
|
2017-11-22 18:12:23 +00:00
|
|
|
* @dataProvider provideGetText
|
|
|
|
|
* @param array $options Options to getText()
|
|
|
|
|
* @param string $text Parser text
|
|
|
|
|
* @param string $expect Expected output
|
|
|
|
|
*/
|
2017-11-22 23:12:40 +00:00
|
|
|
public function testGetText( $options, $text, $expect ) {
|
2023-10-24 01:33:44 +00:00
|
|
|
// Avoid other skins affecting the section edit links
|
|
|
|
|
$this->overrideConfigValue( MainConfigNames::DefaultSkin, 'fallback' );
|
|
|
|
|
RequestContext::resetMain();
|
|
|
|
|
|
2022-08-01 19:14:41 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::ScriptPath => '/w',
|
|
|
|
|
MainConfigNames::Script => '/w/index.php',
|
2017-11-22 18:12:23 +00:00
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$po = new ParserOutput( $text );
|
2023-01-19 20:37:16 +00:00
|
|
|
self::initSections( $po );
|
2017-11-22 18:12:23 +00:00
|
|
|
$actual = $po->getText( $options );
|
|
|
|
|
$this->assertSame( $expect, $actual );
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 20:37:16 +00:00
|
|
|
private static function initSections( ParserOutput $po ): void {
|
|
|
|
|
$po->setTOCData( new TOCData(
|
|
|
|
|
SectionMetadata::fromLegacy( [
|
|
|
|
|
'index' => "1",
|
|
|
|
|
'level' => 1,
|
|
|
|
|
'toclevel' => 1,
|
|
|
|
|
'number' => "1",
|
|
|
|
|
'line' => "Section 1",
|
|
|
|
|
'anchor' => "Section_1"
|
|
|
|
|
] ),
|
|
|
|
|
SectionMetadata::fromLegacy( [
|
|
|
|
|
'index' => "2",
|
|
|
|
|
'level' => 1,
|
|
|
|
|
'toclevel' => 1,
|
|
|
|
|
'number' => "2",
|
|
|
|
|
'line' => "Section 2",
|
|
|
|
|
'anchor' => "Section_2"
|
|
|
|
|
] ),
|
|
|
|
|
SectionMetadata::fromLegacy( [
|
|
|
|
|
'index' => "3",
|
|
|
|
|
'level' => 2,
|
|
|
|
|
'toclevel' => 2,
|
|
|
|
|
'number' => "2.1",
|
|
|
|
|
'line' => "Section 2.1",
|
|
|
|
|
'anchor' => "Section_2.1"
|
|
|
|
|
] ),
|
|
|
|
|
SectionMetadata::fromLegacy( [
|
|
|
|
|
'index' => "4",
|
|
|
|
|
'level' => 1,
|
|
|
|
|
'toclevel' => 1,
|
|
|
|
|
'number' => "3",
|
|
|
|
|
'line' => "Section 3",
|
|
|
|
|
'anchor' => "Section_3"
|
|
|
|
|
] ),
|
|
|
|
|
) );
|
2021-09-15 01:00:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideGetText() {
|
|
|
|
|
$text = <<<EOF
|
|
|
|
|
<p>Test document.
|
|
|
|
|
</p>
|
2021-12-21 03:26:38 +00:00
|
|
|
<meta property="mw:PageProp/toc" />
|
2017-11-22 18:12:23 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_1">Section 1</span><mw:editsection page="Test Page" section="1">Section 1</mw:editsection></h2>
|
|
|
|
|
<p>One
|
|
|
|
|
</p>
|
|
|
|
|
<h2><span class="mw-headline" id="Section_2">Section 2</span><mw:editsection page="Test Page" section="2">Section 2</mw:editsection></h2>
|
|
|
|
|
<p>Two
|
|
|
|
|
</p>
|
2024-01-15 21:43:49 +00:00
|
|
|
<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
|
2017-11-22 18:12:23 +00:00
|
|
|
<p>Two point one
|
|
|
|
|
</p>
|
|
|
|
|
<h2><span class="mw-headline" id="Section_3">Section 3</span><mw:editsection page="Test Page" section="4">Section 3</mw:editsection></h2>
|
|
|
|
|
<p>Three
|
2018-08-28 16:48:10 +00:00
|
|
|
</p>
|
2017-11-24 19:22:25 +00:00
|
|
|
EOF;
|
|
|
|
|
|
|
|
|
|
$dedupText = <<<EOF
|
|
|
|
|
<p>This is a test document.</p>
|
|
|
|
|
<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
|
|
|
|
|
<style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate1">.Same-attribute-different-content {}</style>
|
|
|
|
|
<style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
|
|
|
|
|
<style>.Duplicate1 {}</style>
|
2017-11-22 18:12:23 +00:00
|
|
|
EOF;
|
|
|
|
|
|
|
|
|
|
return [
|
2017-11-22 23:12:40 +00:00
|
|
|
'No options' => [
|
|
|
|
|
[], $text, <<<EOF
|
2018-08-28 16:48:10 +00:00
|
|
|
<p>Test document.
|
2017-11-22 18:12:23 +00:00
|
|
|
</p>
|
2023-01-19 20:37:16 +00:00
|
|
|
<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading"><input type="checkbox" role="button" id="toctogglecheckbox" class="toctogglecheckbox" style="display:none" /><div class="toctitle" lang="en" dir="ltr"><h2 id="mw-toc-heading">Contents</h2><span class="toctogglespan"><label class="toctogglelabel" for="toctogglecheckbox"></label></span></div>
|
2017-11-22 18:12:23 +00:00
|
|
|
<ul>
|
|
|
|
|
<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
|
|
|
|
|
<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
|
|
|
|
|
<ul>
|
|
|
|
|
<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</li>
|
|
|
|
|
<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-11-22 18:12:23 +00:00
|
|
|
<p>One
|
|
|
|
|
</p>
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-11-22 18:12:23 +00:00
|
|
|
<p>Two
|
|
|
|
|
</p>
|
2020-09-23 15:30:40 +00:00
|
|
|
<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
|
2017-11-22 18:12:23 +00:00
|
|
|
<p>Two point one
|
|
|
|
|
</p>
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-11-22 18:12:23 +00:00
|
|
|
<p>Three
|
2018-08-28 16:48:10 +00:00
|
|
|
</p>
|
2017-11-22 18:12:23 +00:00
|
|
|
EOF
|
|
|
|
|
],
|
2017-11-22 23:12:40 +00:00
|
|
|
'Disable section edit links' => [
|
|
|
|
|
[ 'enableSectionEditLinks' => false ], $text, <<<EOF
|
2018-08-28 16:48:10 +00:00
|
|
|
<p>Test document.
|
2017-11-22 18:12:23 +00:00
|
|
|
</p>
|
2023-01-19 20:37:16 +00:00
|
|
|
<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading"><input type="checkbox" role="button" id="toctogglecheckbox" class="toctogglecheckbox" style="display:none" /><div class="toctitle" lang="en" dir="ltr"><h2 id="mw-toc-heading">Contents</h2><span class="toctogglespan"><label class="toctogglelabel" for="toctogglecheckbox"></label></span></div>
|
2017-11-22 18:12:23 +00:00
|
|
|
<ul>
|
|
|
|
|
<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
|
|
|
|
|
<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
|
|
|
|
|
<ul>
|
|
|
|
|
<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</li>
|
|
|
|
|
<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
|
|
|
|
|
<p>One
|
|
|
|
|
</p>
|
|
|
|
|
<h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
|
|
|
|
|
<p>Two
|
|
|
|
|
</p>
|
|
|
|
|
<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
|
|
|
|
|
<p>Two point one
|
|
|
|
|
</p>
|
|
|
|
|
<h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
|
|
|
|
|
<p>Three
|
2018-08-28 16:48:10 +00:00
|
|
|
</p>
|
2017-11-22 18:12:23 +00:00
|
|
|
EOF
|
|
|
|
|
],
|
2018-08-28 16:48:10 +00:00
|
|
|
'Disable TOC, but wrap' => [
|
|
|
|
|
[ 'allowTOC' => false, 'wrapperDivClass' => 'mw-parser-output' ], $text, <<<EOF
|
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText
== Skin::wrapHTML ==
Skin::wrapHTML no longer has to perform any guessing of the
ParserOutput language. Nor does it have to special wiki pages vs
special pages in this regard. Yay, code removal.
== ImagePage ==
On URLs like /wiki/File:Example.jpg, the main output handler is
ImagePage::view. This calls the parent Article::view to handle most of
its output. Article::view obtains the ParserOptions, and then fetches
ParserOutput, and then adds `<div class=mw-parser-output>` and its
metadata to OutputPage.
Before this change, ImagePage::view was creating a wrapper based
on "predicting" what language the ParserOutput will contain. It
couldn't call the new OutputPage::getContentLanguage or some
equivalent as Article::view wouldn't have populated that yet.
This leaky abstraction is fixed by this change as now the `<div>`
from ParserOutput no longer comes with a "please wrap it properly"
contract that Article subclasses couldn't possibly implement correctly
(it coudln't wrap it after the fact because Article::view writes to
OutputPage directly).
RECENT (T310445):
A special case was recently added for file pages about translated SVGs.
For those, we decide which language to use for the "fullMedia" thumb
atop the page. This was recently changed as part of T310445 from a
hardcoded $wgLanguageCode (site content lang) to new problematic
Title::getPageViewLanguage, which tries to guestimate the page
language of the rendered ParserOutput and then gets the preferred
variant for the current user. The motivation for this was to support
language variants but used Title::getPageViewLanguage as a kitchen
sink to achieve that minor side-effect. The only part of this
now-deprecated method that we actually need is
LanguageConverter::getPreferredVariant().
Test plan: Covered by ImagePageTest.
== Skin mainpage-title ==
RECENT (T331095, T298715):
A special case was added to Skin::getTemplateData that powers the
mainpage-title interface message feature. This is empty by default,
but when created via MediaWiki:mainpage-title allows interface admins
to replace the H1 with a custom and localised page heading.
A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was
applied here to support language variants. Replace with the same
fix as for ImagePage. Revert back to Message::inContentLanguage()
but refactor to inLanguage() via MediaWikiServices::getContentLanguage
so that LanguageConverter::getPreferredVariant can be applied.
== EditPage ==
This was doing similar "predicting" of the ParserOutput language to
create an empty preview placeholder for use by preview.js. Now that
ApiParse (via ParserOutput::getText) returns a usable element without
any secret "you magically know the right class, lang, and dir" contract,
this placeholder is no longer needed.
Test Plan:
* EditPage: Default preview
1. index.php?title=Main_Page&action=edit
2. Show preview
3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
* EditPage: JS preview
1. Preferences > Editing > Show preview without reload
2. index.php?title=Main_Page&action=edit
3. Show preview
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
5. Type something and 'Show preview' again
6. Assert old element gone, new text is shown, and new element
attributes are the same as the above.
== McrUndoAction ==
Same as EditPage basically, but without the JS preview use case.
== DifferenceEngine ==
Test:
1. Open /w/index.php?title=Main_Page&diff=0
(this shows the latest diff, can do manually by viewing
/wiki/Main_Page, click "View history", click "Compare selected revisions")
2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
3. Open /w/index.php?title=Main_Page&diff=0&action=render
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
== Special:ExpandTemplates ==
Test:
1. /wiki/Special:ExpandTemplates
2. Write "Hello".
3. "OK"
4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr>
Bug: T341244
Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c
Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1
Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105
Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
|
|
|
<div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr"><p>Test document.
|
2017-12-22 18:32:49 +00:00
|
|
|
</p>
|
|
|
|
|
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-12-22 18:32:49 +00:00
|
|
|
<p>One
|
|
|
|
|
</p>
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-12-22 18:32:49 +00:00
|
|
|
<p>Two
|
|
|
|
|
</p>
|
2020-09-23 15:30:40 +00:00
|
|
|
<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
|
2017-12-22 18:32:49 +00:00
|
|
|
<p>Two point one
|
|
|
|
|
</p>
|
2023-10-24 01:33:44 +00:00
|
|
|
<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&action=edit&section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
|
2017-12-22 18:32:49 +00:00
|
|
|
<p>Three
|
|
|
|
|
</p></div>
|
|
|
|
|
EOF
|
2018-02-09 21:30:12 +00:00
|
|
|
],
|
2017-11-24 19:22:25 +00:00
|
|
|
'Style deduplication' => [
|
2017-11-22 23:12:40 +00:00
|
|
|
[], $dedupText, <<<EOF
|
2017-11-24 19:22:25 +00:00
|
|
|
<p>This is a test document.</p>
|
|
|
|
|
<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
|
2023-12-22 10:24:19 +00:00
|
|
|
<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1">
|
2017-11-24 19:22:25 +00:00
|
|
|
<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
|
2023-12-22 10:24:19 +00:00
|
|
|
<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1">
|
|
|
|
|
<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate2">
|
2017-11-24 19:22:25 +00:00
|
|
|
<style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
|
2023-12-22 10:24:19 +00:00
|
|
|
<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1">
|
2017-11-24 19:22:25 +00:00
|
|
|
<style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
|
|
|
|
|
<style>.Duplicate1 {}</style>
|
|
|
|
|
EOF
|
|
|
|
|
],
|
|
|
|
|
'Style deduplication disabled' => [
|
2017-11-22 23:12:40 +00:00
|
|
|
[ 'deduplicateStyles' => false ], $dedupText, $dedupText
|
2017-11-24 19:22:25 +00:00
|
|
|
],
|
2017-11-22 18:12:23 +00:00
|
|
|
];
|
2018-01-01 13:10:16 +00:00
|
|
|
// phpcs:enable
|
2017-11-22 18:12:23 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-13 20:33:31 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::hasText
|
2018-08-13 20:33:31 +00:00
|
|
|
*/
|
|
|
|
|
public function testHasText() {
|
2022-04-22 05:47:40 +00:00
|
|
|
$po = new ParserOutput( '' );
|
2018-08-13 20:33:31 +00:00
|
|
|
$this->assertTrue( $po->hasText() );
|
|
|
|
|
|
|
|
|
|
$po = new ParserOutput( null );
|
|
|
|
|
$this->assertFalse( $po->hasText() );
|
|
|
|
|
|
2022-04-22 05:47:40 +00:00
|
|
|
$po = new ParserOutput();
|
|
|
|
|
$this->assertFalse( $po->hasText() );
|
|
|
|
|
|
2018-08-13 20:33:31 +00:00
|
|
|
$po = new ParserOutput( '' );
|
|
|
|
|
$this->assertTrue( $po->hasText() );
|
|
|
|
|
|
|
|
|
|
$po = new ParserOutput( null );
|
2024-02-09 22:27:00 +00:00
|
|
|
$po->setRawText( '' );
|
2018-08-13 20:33:31 +00:00
|
|
|
$this->assertTrue( $po->hasText() );
|
2022-04-22 05:47:40 +00:00
|
|
|
|
|
|
|
|
$po = new ParserOutput( 'foo' );
|
2024-02-09 22:27:00 +00:00
|
|
|
$po->setRawText( null );
|
2022-04-22 05:47:40 +00:00
|
|
|
$this->assertFalse( $po->hasText() );
|
2018-08-13 20:33:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-10-10 15:05:08 +00:00
|
|
|
* This test aims at being replaced by its version in DefaultOutputTransformTest when ParserOutput::getText
|
|
|
|
|
* gets deprecated.
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getText
|
2018-08-13 20:33:31 +00:00
|
|
|
*/
|
|
|
|
|
public function testGetText_failsIfNoText() {
|
|
|
|
|
$po = new ParserOutput( null );
|
|
|
|
|
|
2019-10-05 15:39:46 +00:00
|
|
|
$this->expectException( LogicException::class );
|
2018-08-13 20:33:31 +00:00
|
|
|
$po->getText();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideGetText_absoluteURLs() {
|
2021-09-21 14:34:30 +00:00
|
|
|
yield 'empty' => [
|
|
|
|
|
'text' => '',
|
|
|
|
|
'expectedText' => '',
|
|
|
|
|
];
|
|
|
|
|
yield 'no-links' => [
|
|
|
|
|
'text' => '<p>test</p>',
|
|
|
|
|
'expectedText' => '<p>test</p>',
|
|
|
|
|
];
|
|
|
|
|
yield 'simple link' => [
|
|
|
|
|
'text' => '<a href="/wiki/Test">test</a>',
|
|
|
|
|
'expectedText' => '<a href="//TEST_SERVER/wiki/Test">test</a>',
|
|
|
|
|
];
|
|
|
|
|
yield 'already absolute, relative' => [
|
|
|
|
|
'text' => '<a href="//TEST_SERVER/wiki/Test">test</a>',
|
|
|
|
|
'expectedText' => '<a href="//TEST_SERVER/wiki/Test">test</a>',
|
|
|
|
|
];
|
|
|
|
|
yield 'already absolute, https' => [
|
|
|
|
|
'text' => '<a href="https://TEST_SERVER/wiki/Test">test</a>',
|
|
|
|
|
'expectedText' => '<a href="https://TEST_SERVER/wiki/Test">test</a>',
|
|
|
|
|
];
|
|
|
|
|
yield 'external' => [
|
|
|
|
|
'text' => '<a href="https://en.wikipedia.org/wiki/Test">test</a>',
|
|
|
|
|
'expectedText' => '<a href="https://en.wikipedia.org/wiki/Test">test</a>',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-10-10 15:05:08 +00:00
|
|
|
* This test aims at being replaced by its version in DefaultOutputTransformTest when ParserOutput::getText
|
|
|
|
|
* gets deprecated.
|
2021-09-21 14:34:30 +00:00
|
|
|
* @dataProvider provideGetText_absoluteURLs
|
|
|
|
|
*/
|
|
|
|
|
public function testGetText_absoluteURLs( string $text, string $expectedText ) {
|
2022-08-01 19:14:41 +00:00
|
|
|
$this->overrideConfigValue( MainConfigNames::Server, '//TEST_SERVER' );
|
2021-09-21 14:34:30 +00:00
|
|
|
$parserOutput = new ParserOutput( $text );
|
|
|
|
|
$this->assertSame( $expectedText, $parserOutput->getText( [ 'absoluteURLs' => true ] ) );
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 20:33:31 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getRawText
|
2018-08-13 20:33:31 +00:00
|
|
|
*/
|
|
|
|
|
public function testGetRawText_failsIfNoText() {
|
|
|
|
|
$po = new ParserOutput( null );
|
|
|
|
|
|
2019-10-05 15:39:46 +00:00
|
|
|
$this->expectException( LogicException::class );
|
2018-08-13 20:33:31 +00:00
|
|
|
$po->getRawText();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideMergeHtmlMetaDataFrom() {
|
2018-08-07 16:52:40 +00:00
|
|
|
// title text ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setTitleText( 'X' );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left title text' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setTitleText( 'Y' );
|
|
|
|
|
yield 'only right title text' => [ $a, $b, [ 'getTitleText' => 'Y' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setTitleText( 'X' );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setTitleText( 'Y' );
|
|
|
|
|
yield 'left title text wins' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
|
|
|
|
|
|
|
|
|
|
// index policy ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setIndexPolicy( 'index' );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setIndexPolicy( 'index' );
|
|
|
|
|
yield 'only right index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setIndexPolicy( 'noindex' );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setIndexPolicy( 'index' );
|
|
|
|
|
yield 'left noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setIndexPolicy( 'index' );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setIndexPolicy( 'noindex' );
|
|
|
|
|
yield 'right noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
|
|
|
|
|
|
Add ParserOutput::getLanguage()
Provide a way for backend code to determine the primary language of a
ParserOutput, eg for setting the Content-Language header of an API
response.
This is read-only and backed by extension data at the moment for
transition purposes; if this API sticks we'll graduate it to a
"real" property in the future, with appropriate serialization
to/from JSON (T303329).
Similarly, this patch only includes the most basic code to handle
the various ParserOutput merge cases in
ParserOutput::merge{Internal,Html,Tracking}MetaDataFrom(),
ParserOutput::collectMetadata(), and
OutputPage::addParserOutput{Content,Metadata,Text,}(); mostly
inherited from the fact that the storage is backed by extension
data at the moment.
Generally only the "top-level" parser output gets to set the
primary language; we'll presumably need to ensure that the
language is consistent during merge.
Change-Id: I767daba22805a877d9b806fd77334e508902844b
2022-09-28 21:21:13 +00:00
|
|
|
$crhCyrl = new Bcp47CodeValue( 'crh-cyrl' );
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setLanguage( $crhCyrl );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left language' => [ $a, $b, [ 'getLanguage' => $crhCyrl ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setLanguage( $crhCyrl );
|
|
|
|
|
yield 'only right language' => [ $a, $b, [ 'getLanguage' => $crhCyrl ] ];
|
|
|
|
|
|
2018-08-07 16:52:40 +00:00
|
|
|
// head items and friends ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->addHeadItem( '<foo1>' );
|
|
|
|
|
$a->addHeadItem( '<bar1>', 'bar' );
|
2022-01-21 10:40:46 +00:00
|
|
|
$a->addModules( [ 'test-module-a' ] );
|
|
|
|
|
$a->addModuleStyles( [ 'test-module-styles-a' ] );
|
2022-01-27 19:44:35 +00:00
|
|
|
$a->setJsConfigVar( 'test-config-var-a', 'a' );
|
|
|
|
|
$a->appendJsConfigVar( 'test-config-var-c', 'abc' );
|
|
|
|
|
$a->appendJsConfigVar( 'test-config-var-c', 'def' );
|
2020-02-03 09:50:14 +00:00
|
|
|
$a->addExtraCSPStyleSrc( 'css.com' );
|
|
|
|
|
$a->addExtraCSPStyleSrc( 'css2.com' );
|
|
|
|
|
$a->addExtraCSPScriptSrc( 'js.com' );
|
|
|
|
|
$a->addExtraCSPDefaultSrc( 'img.com' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setIndexPolicy( 'noindex' );
|
|
|
|
|
$b->addHeadItem( '<foo2>' );
|
|
|
|
|
$b->addHeadItem( '<bar2>', 'bar' );
|
2022-01-21 10:40:46 +00:00
|
|
|
$b->addModules( [ 'test-module-b' ] );
|
|
|
|
|
$b->addModuleStyles( [ 'test-module-styles-b' ] );
|
2022-01-27 19:44:35 +00:00
|
|
|
$b->setJsConfigVar( 'test-config-var-b', 'b' );
|
|
|
|
|
$b->setJsConfigVar( 'test-config-var-a', 'X' );
|
|
|
|
|
$a->appendJsConfigVar( 'test-config-var-c', 'xyz' );
|
|
|
|
|
$a->appendJsConfigVar( 'test-config-var-c', 'def' );
|
2020-02-03 09:50:14 +00:00
|
|
|
$b->addExtraCSPStyleSrc( 'https://css.ca' );
|
|
|
|
|
$b->addExtraCSPScriptSrc( 'jscript.com' );
|
|
|
|
|
$b->addExtraCSPScriptSrc( 'vbscript.com' );
|
|
|
|
|
$b->addExtraCSPDefaultSrc( 'img.com/foo.jpg' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2022-01-27 19:44:35 +00:00
|
|
|
// Note that overwriting test-config-var-a during the merge
|
|
|
|
|
// (as this test case does) is deprecated and will eventually
|
|
|
|
|
// throw an exception. However, at the moment it is still worth
|
|
|
|
|
// testing this case to ensure backward compatibility. (T300307)
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'head items and friends' => [ $a, $b, [
|
|
|
|
|
'getHeadItems' => [
|
|
|
|
|
'<foo1>',
|
|
|
|
|
'<foo2>',
|
|
|
|
|
'bar' => '<bar2>', // overwritten
|
|
|
|
|
],
|
|
|
|
|
'getModules' => [
|
|
|
|
|
'test-module-a',
|
|
|
|
|
'test-module-b',
|
|
|
|
|
],
|
|
|
|
|
'getModuleStyles' => [
|
|
|
|
|
'test-module-styles-a',
|
|
|
|
|
'test-module-styles-b',
|
|
|
|
|
],
|
|
|
|
|
'getJsConfigVars' => [
|
|
|
|
|
'test-config-var-a' => 'X', // overwritten
|
|
|
|
|
'test-config-var-b' => 'b',
|
2022-01-27 19:44:35 +00:00
|
|
|
'test-config-var-c' => [ // merged safely
|
|
|
|
|
'abc' => true, 'def' => true, 'xyz' => true,
|
|
|
|
|
],
|
2018-08-07 16:52:40 +00:00
|
|
|
],
|
2020-02-03 09:50:14 +00:00
|
|
|
'getExtraCSPStyleSrcs' => [
|
|
|
|
|
'css.com',
|
|
|
|
|
'css2.com',
|
|
|
|
|
'https://css.ca'
|
|
|
|
|
],
|
|
|
|
|
'getExtraCSPScriptSrcs' => [
|
|
|
|
|
'js.com',
|
|
|
|
|
'jscript.com',
|
|
|
|
|
'vbscript.com'
|
|
|
|
|
],
|
|
|
|
|
'getExtraCSPDefaultSrcs' => [
|
|
|
|
|
'img.com',
|
|
|
|
|
'img.com/foo.jpg'
|
|
|
|
|
]
|
2018-08-07 16:52:40 +00:00
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
// TOC ------------
|
2023-01-19 20:37:16 +00:00
|
|
|
$a = new ParserOutput( '' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->setSections( [ [ 'fromtitle' => 'A1' ], [ 'fromtitle' => 'A2' ] ] );
|
|
|
|
|
|
2023-01-19 20:37:16 +00:00
|
|
|
$b = new ParserOutput( '' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b->setSections( [ [ 'fromtitle' => 'B1' ], [ 'fromtitle' => 'B2' ] ] );
|
|
|
|
|
|
|
|
|
|
yield 'concat TOC' => [ $a, $b, [
|
|
|
|
|
'getSections' => [
|
2022-09-01 23:07:29 +00:00
|
|
|
SectionMetadata::fromLegacy( [ 'fromtitle' => 'A1' ] )->toLegacy(),
|
|
|
|
|
SectionMetadata::fromLegacy( [ 'fromtitle' => 'A2' ] )->toLegacy(),
|
|
|
|
|
SectionMetadata::fromLegacy( [ 'fromtitle' => 'B1' ] )->toLegacy(),
|
|
|
|
|
SectionMetadata::fromLegacy( [ 'fromtitle' => 'B2' ] )->toLegacy()
|
2018-08-07 16:52:40 +00:00
|
|
|
],
|
|
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
// Skin Control ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setNewSection( true );
|
2021-09-29 20:48:33 +00:00
|
|
|
$a->setHideNewSection( true );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->setNoGallery( true );
|
|
|
|
|
$a->addWrapperDivClass( 'foo' );
|
|
|
|
|
|
|
|
|
|
$a->setIndicator( 'foo', 'Foo!' );
|
|
|
|
|
$a->setIndicator( 'bar', 'Bar!' );
|
|
|
|
|
|
|
|
|
|
$a->setExtensionData( 'foo', 'Foo!' );
|
|
|
|
|
$a->setExtensionData( 'bar', 'Bar!' );
|
2022-01-28 17:09:22 +00:00
|
|
|
$a->appendExtensionData( 'bat', 'abc' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setNoGallery( true );
|
|
|
|
|
$b->setEnableOOUI( true );
|
2021-09-29 20:58:59 +00:00
|
|
|
$b->setPreventClickjacking( true );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->addWrapperDivClass( 'bar' );
|
|
|
|
|
|
|
|
|
|
$b->setIndicator( 'zoo', 'Zoo!' );
|
|
|
|
|
$b->setIndicator( 'bar', 'Barrr!' );
|
|
|
|
|
|
|
|
|
|
$b->setExtensionData( 'zoo', 'Zoo!' );
|
|
|
|
|
$b->setExtensionData( 'bar', 'Barrr!' );
|
2022-01-28 17:09:22 +00:00
|
|
|
$b->appendExtensionData( 'bat', 'xyz' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2022-01-28 17:09:22 +00:00
|
|
|
// Note that overwriting extension data during the merge
|
|
|
|
|
// (as this test case does for 'bar') is deprecated and will eventually
|
|
|
|
|
// throw an exception. However, at the moment it is still worth
|
|
|
|
|
// testing this case to ensure backward compatibility. (T300981)
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'skin control flags' => [ $a, $b, [
|
|
|
|
|
'getNewSection' => true,
|
|
|
|
|
'getHideNewSection' => true,
|
|
|
|
|
'getNoGallery' => true,
|
|
|
|
|
'getEnableOOUI' => true,
|
2021-09-29 20:58:59 +00:00
|
|
|
'getPreventClickjacking' => true,
|
2018-08-07 16:52:40 +00:00
|
|
|
'getIndicators' => [
|
|
|
|
|
'foo' => 'Foo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
'bar' => 'Barrr!', // overwritten
|
2018-08-07 16:52:40 +00:00
|
|
|
'zoo' => 'Zoo!',
|
|
|
|
|
],
|
|
|
|
|
'getWrapperDivClass' => 'foo bar',
|
|
|
|
|
'$mExtensionData' => [
|
|
|
|
|
'foo' => 'Foo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
'bar' => 'Barrr!', // overwritten
|
2018-08-07 16:52:40 +00:00
|
|
|
'zoo' => 'Zoo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
// internal strategy key is exposed here because we're looking
|
|
|
|
|
// at the raw property value, not using getExtensionData()
|
|
|
|
|
'bat' => [ 'abc' => true, 'xyz' => true, '_mw-strategy' => 'union' ],
|
2018-08-07 16:52:40 +00:00
|
|
|
],
|
|
|
|
|
] ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideMergeHtmlMetaDataFrom
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::mergeHtmlMetaDataFrom
|
2018-08-07 16:52:40 +00:00
|
|
|
*
|
|
|
|
|
* @param ParserOutput $a
|
|
|
|
|
* @param ParserOutput $b
|
|
|
|
|
* @param array $expected
|
|
|
|
|
*/
|
|
|
|
|
public function testMergeHtmlMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
|
|
|
|
|
$a->mergeHtmlMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
|
|
|
|
|
// test twice, to make sure the operation is idempotent (except for the TOC, see below)
|
|
|
|
|
$a->mergeHtmlMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
// XXX: TOC joining should get smarter. Can we make it idempotent as well?
|
|
|
|
|
unset( $expected['getSections'] );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function assertFieldValues( ParserOutput $po, $expected ) {
|
|
|
|
|
$po = TestingAccessWrapper::newFromObject( $po );
|
|
|
|
|
|
|
|
|
|
foreach ( $expected as $method => $value ) {
|
2022-01-27 19:44:35 +00:00
|
|
|
$canonicalize = false;
|
2018-08-07 16:52:40 +00:00
|
|
|
if ( $method[0] === '$' ) {
|
|
|
|
|
$field = substr( $method, 1 );
|
|
|
|
|
$actual = $po->__get( $field );
|
|
|
|
|
} else {
|
|
|
|
|
$actual = $po->__call( $method, [] );
|
|
|
|
|
}
|
2022-01-27 19:44:35 +00:00
|
|
|
if ( $method === 'getJsConfigVars' ) {
|
|
|
|
|
$canonicalize = true;
|
|
|
|
|
}
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2022-01-27 19:44:35 +00:00
|
|
|
if ( $canonicalize ) {
|
|
|
|
|
// order of entries isn't significant
|
|
|
|
|
$this->assertEqualsCanonicalizing( $value, $actual, $method );
|
|
|
|
|
} else {
|
|
|
|
|
$this->assertEquals( $value, $actual, $method );
|
|
|
|
|
}
|
2018-08-07 16:52:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-12 09:39:32 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::addLink
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getLinks
|
2021-10-12 09:39:32 +00:00
|
|
|
*/
|
|
|
|
|
public function testAddLink() {
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->addLink( Title::makeTitle( NS_MAIN, 'Kittens' ), 6 );
|
|
|
|
|
$a->addLink( new TitleValue( NS_TALK, 'Kittens' ), 16 );
|
|
|
|
|
$a->addLink( new TitleValue( NS_MAIN, 'Goats_786827346' ) );
|
|
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
NS_MAIN => [ 'Kittens' => 6, 'Goats_786827346' => 0 ],
|
|
|
|
|
NS_TALK => [ 'Kittens' => 16 ]
|
|
|
|
|
];
|
|
|
|
|
$this->assertSame( $expected, $a->getLinks() );
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideMergeTrackingMetaDataFrom() {
|
2018-08-07 16:52:40 +00:00
|
|
|
// links ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->addLink( Title::makeTitle( NS_MAIN, 'Kittens' ), 6 );
|
2021-10-12 09:39:32 +00:00
|
|
|
$a->addLink( new TitleValue( NS_TALK, 'Kittens' ), 16 );
|
|
|
|
|
$a->addLink( new TitleValue( NS_MAIN, 'Goats' ), 7 );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$a->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Goats' ), 107, 1107 );
|
|
|
|
|
|
2023-12-07 15:59:28 +00:00
|
|
|
$a->addLanguageLink( new TitleValue( NS_MAIN, 'de' ) );
|
|
|
|
|
$a->addLanguageLink( new TitleValue( NS_MAIN, 'ru' ) );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens DE', '', 'de' ) );
|
2021-10-12 09:39:32 +00:00
|
|
|
$a->addInterwikiLink( new TitleValue( NS_MAIN, 'Kittens RU', '', 'ru' ) );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->addExternalLink( 'https://kittens.wikimedia.test' );
|
|
|
|
|
$a->addExternalLink( 'https://goats.wikimedia.test' );
|
|
|
|
|
|
|
|
|
|
$a->addCategory( 'Foo', 'X' );
|
2023-12-07 15:59:28 +00:00
|
|
|
$a->addImage( new TitleValue( NS_FILE, 'Billy.jpg' ), '20180101000013', 'DEAD' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->addLink( Title::makeTitle( NS_MAIN, 'Goats' ), 7 );
|
|
|
|
|
$b->addLink( Title::makeTitle( NS_TALK, 'Goats' ), 17 );
|
2021-10-12 09:39:32 +00:00
|
|
|
$b->addLink( new TitleValue( NS_MAIN, 'Dragons' ), 8 );
|
|
|
|
|
$b->addLink( new TitleValue( NS_FILE, 'Dragons.jpg' ), 28 );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Dragons' ), 108, 1108 );
|
2021-10-12 09:39:32 +00:00
|
|
|
$a->addTemplate( new TitleValue( NS_MAIN, 'Dragons' ), 118, 1118 );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2023-12-07 15:59:28 +00:00
|
|
|
$b->addLanguageLink( new TitleValue( NS_MAIN, 'fr' ) );
|
|
|
|
|
$b->addLanguageLink( new TitleValue( NS_MAIN, 'ru' ) );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens FR', '', 'fr' ) );
|
2021-10-12 09:39:32 +00:00
|
|
|
$b->addInterwikiLink( new TitleValue( NS_MAIN, 'Dragons RU', '', 'ru' ) );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b->addExternalLink( 'https://dragons.wikimedia.test' );
|
|
|
|
|
$b->addExternalLink( 'https://goats.wikimedia.test' );
|
|
|
|
|
|
|
|
|
|
$b->addCategory( 'Bar', 'Y' );
|
2023-12-07 15:59:28 +00:00
|
|
|
$b->addImage( new TitleValue( NS_FILE, 'Puff.jpg' ), '20180101000017', 'BEEF' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
yield 'all kinds of links' => [ $a, $b, [
|
|
|
|
|
'getLinks' => [
|
|
|
|
|
NS_MAIN => [
|
|
|
|
|
'Kittens' => 6,
|
|
|
|
|
'Goats' => 7,
|
|
|
|
|
'Dragons' => 8,
|
|
|
|
|
],
|
|
|
|
|
NS_TALK => [
|
|
|
|
|
'Kittens' => 16,
|
|
|
|
|
'Goats' => 17,
|
|
|
|
|
],
|
|
|
|
|
NS_FILE => [
|
|
|
|
|
'Dragons.jpg' => 28,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'getTemplates' => [
|
|
|
|
|
NS_MAIN => [
|
|
|
|
|
'Dragons' => 118,
|
|
|
|
|
],
|
|
|
|
|
NS_TEMPLATE => [
|
|
|
|
|
'Dragons' => 108,
|
|
|
|
|
'Goats' => 107,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'getTemplateIds' => [
|
|
|
|
|
NS_MAIN => [
|
|
|
|
|
'Dragons' => 1118,
|
|
|
|
|
],
|
|
|
|
|
NS_TEMPLATE => [
|
|
|
|
|
'Dragons' => 1108,
|
|
|
|
|
'Goats' => 1107,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'getLanguageLinks' => [ 'de', 'ru', 'fr' ],
|
|
|
|
|
'getInterwikiLinks' => [
|
|
|
|
|
'de' => [ 'Kittens_DE' => 1 ],
|
|
|
|
|
'ru' => [ 'Kittens_RU' => 1, 'Dragons_RU' => 1, ],
|
|
|
|
|
'fr' => [ 'Kittens_FR' => 1 ],
|
|
|
|
|
],
|
2023-09-21 17:06:50 +00:00
|
|
|
'getCategoryMap' => [ 'Foo' => 'X', 'Bar' => 'Y' ],
|
2018-08-07 16:52:40 +00:00
|
|
|
'getImages' => [ 'Billy.jpg' => 1, 'Puff.jpg' => 1 ],
|
|
|
|
|
'getFileSearchOptions' => [
|
|
|
|
|
'Billy.jpg' => [ 'time' => '20180101000013', 'sha1' => 'DEAD' ],
|
|
|
|
|
'Puff.jpg' => [ 'time' => '20180101000017', 'sha1' => 'BEEF' ],
|
|
|
|
|
],
|
|
|
|
|
'getExternalLinks' => [
|
|
|
|
|
'https://dragons.wikimedia.test' => 1,
|
|
|
|
|
'https://kittens.wikimedia.test' => 1,
|
|
|
|
|
'https://goats.wikimedia.test' => 1,
|
|
|
|
|
]
|
|
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
// properties ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$a->setPageProperty( 'foo', 'Foo!' );
|
|
|
|
|
$a->setPageProperty( 'bar', 'Bar!' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$a->setExtensionData( 'foo', 'Foo!' );
|
|
|
|
|
$a->setExtensionData( 'bar', 'Bar!' );
|
2022-01-28 17:09:22 +00:00
|
|
|
$a->appendExtensionData( 'bat', 'abc' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
|
2021-10-07 16:13:46 +00:00
|
|
|
$b->setPageProperty( 'zoo', 'Zoo!' );
|
|
|
|
|
$b->setPageProperty( 'bar', 'Barrr!' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b->setExtensionData( 'zoo', 'Zoo!' );
|
|
|
|
|
$b->setExtensionData( 'bar', 'Barrr!' );
|
2022-01-28 17:09:22 +00:00
|
|
|
$b->appendExtensionData( 'bat', 'xyz' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2022-01-28 17:09:22 +00:00
|
|
|
// Note that overwriting extension data during the merge
|
|
|
|
|
// (as this test case does for 'bar') is deprecated and will eventually
|
|
|
|
|
// throw an exception. However, at the moment it is still worth
|
|
|
|
|
// testing this case to ensure backward compatibility. (T300981)
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'properties' => [ $a, $b, [
|
2021-10-07 16:13:46 +00:00
|
|
|
'getPageProperties' => [
|
2018-08-07 16:52:40 +00:00
|
|
|
'foo' => 'Foo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
'bar' => 'Barrr!', // overwritten
|
2018-08-07 16:52:40 +00:00
|
|
|
'zoo' => 'Zoo!',
|
|
|
|
|
],
|
|
|
|
|
'$mExtensionData' => [
|
|
|
|
|
'foo' => 'Foo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
'bar' => 'Barrr!', // overwritten
|
2018-08-07 16:52:40 +00:00
|
|
|
'zoo' => 'Zoo!',
|
2022-01-28 17:09:22 +00:00
|
|
|
// internal strategy key is exposed here because we're looking
|
|
|
|
|
// at the raw property value, not using getExtensionData()
|
|
|
|
|
'bat' => [ 'abc' => true, 'xyz' => true, '_mw-strategy' => 'union' ],
|
2018-08-07 16:52:40 +00:00
|
|
|
],
|
|
|
|
|
] ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideMergeTrackingMetaDataFrom
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::mergeTrackingMetaDataFrom
|
2018-08-07 16:52:40 +00:00
|
|
|
*
|
|
|
|
|
* @param ParserOutput $a
|
|
|
|
|
* @param ParserOutput $b
|
|
|
|
|
* @param array $expected
|
|
|
|
|
*/
|
|
|
|
|
public function testMergeTrackingMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
|
|
|
|
|
$a->mergeTrackingMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
|
|
|
|
|
// test twice, to make sure the operation is idempotent
|
|
|
|
|
$a->mergeTrackingMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideMergeInternalMetaDataFrom() {
|
2024-02-24 21:59:05 +00:00
|
|
|
MWDebug::filterDeprecationForTest( '/^CacheTime::setCacheTime called with -1 as an argument/' );
|
|
|
|
|
|
2018-08-07 16:52:40 +00:00
|
|
|
// flags & co
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
|
2021-10-15 19:42:40 +00:00
|
|
|
$a->addWarningMsg( 'duplicate-args-warning', 'A', 'B', 'C' );
|
|
|
|
|
$a->addWarningMsg( 'template-loop-warning', 'D' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2023-07-27 22:53:16 +00:00
|
|
|
$a->setOutputFlag( 'foo' );
|
|
|
|
|
$a->setOutputFlag( 'bar' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$a->recordOption( 'Foo' );
|
|
|
|
|
$a->recordOption( 'Bar' );
|
|
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
|
2021-10-15 19:42:40 +00:00
|
|
|
$b->addWarningMsg( 'template-equals-warning' );
|
|
|
|
|
$b->addWarningMsg( 'template-loop-warning', 'D' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
2023-07-27 22:53:16 +00:00
|
|
|
$b->setOutputFlag( 'zoo' );
|
|
|
|
|
$b->setOutputFlag( 'bar' );
|
2018-08-07 16:52:40 +00:00
|
|
|
|
|
|
|
|
$b->recordOption( 'Zoo' );
|
|
|
|
|
$b->recordOption( 'Bar' );
|
|
|
|
|
|
|
|
|
|
yield 'flags' => [ $a, $b, [
|
2021-10-15 19:42:40 +00:00
|
|
|
'getWarnings' => [
|
|
|
|
|
wfMessage( 'duplicate-args-warning', 'A', 'B', 'C' )->text(),
|
|
|
|
|
wfMessage( 'template-loop-warning', 'D' )->text(),
|
|
|
|
|
wfMessage( 'template-equals-warning' )->text(),
|
|
|
|
|
],
|
2018-08-07 16:52:40 +00:00
|
|
|
'$mFlags' => [ 'foo' => true, 'bar' => true, 'zoo' => true ],
|
|
|
|
|
'getUsedOptions' => [ 'Foo', 'Bar', 'Zoo' ],
|
|
|
|
|
] ];
|
|
|
|
|
|
Add ParserOutput::{get,set}RenderId() and set render id in ContentRenderer
Set the render ID for each parse stored into cache so that we are able
to identify a specific parse when there are dependencies (for example
in an edit based on that parse). This is recorded as a property added
to the ParserOutput, not the parent CacheTime interface. Even though
the render ID is /related/ to the CacheTime interface, CacheTime is
also used directly as a parser cache key, and the UUID should not be
part of the lookup key.
In general we are trying to move the location where these cache
properties are set as early as possible, so we check at each location
to ensure we don't overwrite a previously-set value. Eventually we
can convert most of these checks into assertions that the cache
properties have already been set (T350538). The primary location for
setting cache properties is the ContentRenderer.
Moved setting the revision timestamp into ContentRenderer as well, as
it was set along the same code paths. An extra parameter was added to
ContentRenderer::getParserOutput() to support this.
Added merge code to ParserOutput::mergeInternalMetaDataFrom() which
should ensure that cache time, revision, timestamp, and render id are
all set properly when multiple slots are combined together in MCR.
In order to ensure the render ID is set on all codepaths we needed to
plumb the GlobalIdGenerator service into ContentRenderer, ParserCache,
ParserCacheFactory, and RevisionOutputCache. Eventually (T350538) it
should only be necessary in the ContentRenderer.
Bug: T350538
Bug: T349868
Followup-To: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80
Change-Id: I72c5e6f86b7f081ab5ce7a56f5365d2f75067a78
2023-09-14 16:11:20 +00:00
|
|
|
// cache time
|
|
|
|
|
$someTime = "20240207202040";
|
|
|
|
|
$someLaterTime = "20240207202112";
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setCacheTime( $someTime );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left cache time' => [ $a, $b, [ 'getCacheTime' => $someTime ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setCacheTime( $someTime );
|
|
|
|
|
yield 'only right cache time' => [ $a, $b, [ 'getCacheTime' => $someTime ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$a->setCacheTime( $someLaterTime );
|
|
|
|
|
$b->setCacheTime( $someTime );
|
|
|
|
|
yield 'left has later cache time' => [ $a, $b, [ 'getCacheTime' => $someLaterTime ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$a->setCacheTime( $someTime );
|
|
|
|
|
$b->setCacheTime( $someLaterTime );
|
|
|
|
|
yield 'right has later cache time' => [ $a, $b, [ 'getCacheTime' => $someLaterTime ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$a->setCacheTime( -1 );
|
|
|
|
|
$b->setCacheTime( $someTime );
|
|
|
|
|
yield 'left is uncacheable' => [ $a, $b, [ 'getCacheTime' => "-1" ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$a->setCacheTime( $someTime );
|
|
|
|
|
$b->setCacheTime( -1 );
|
|
|
|
|
yield 'right is uncacheable' => [ $a, $b, [ 'getCacheTime' => "-1" ] ];
|
|
|
|
|
|
2018-08-07 16:52:40 +00:00
|
|
|
// timestamp ------------
|
|
|
|
|
$a = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$a->setRevisionTimestamp( '20180101000011' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$b->setRevisionTimestamp( '20180101000011' );
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'only right timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$a->setRevisionTimestamp( '20180101000011' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$b->setRevisionTimestamp( '20180101000001' );
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'left timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$a->setRevisionTimestamp( '20180101000001' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$b = new ParserOutput();
|
2023-11-06 21:25:07 +00:00
|
|
|
$b->setRevisionTimestamp( '20180101000011' );
|
2018-08-07 16:52:40 +00:00
|
|
|
yield 'right timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
|
|
|
|
|
|
|
|
|
|
// speculative rev id ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setSpeculativeRevIdUsed( 9 );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
yield 'only left speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setSpeculativeRevIdUsed( 9 );
|
|
|
|
|
yield 'only right speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
|
|
|
|
|
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a->setSpeculativeRevIdUsed( 9 );
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b->setSpeculativeRevIdUsed( 9 );
|
|
|
|
|
yield 'same speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
|
|
|
|
|
|
|
|
|
|
// limit report (recursive max) ------------
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$a->setLimitReportData( 'naive1', 7 );
|
|
|
|
|
$a->setLimitReportData( 'naive2', 27 );
|
|
|
|
|
|
|
|
|
|
$a->setLimitReportData( 'limitreport-simple1', 7 );
|
|
|
|
|
$a->setLimitReportData( 'limitreport-simple2', 27 );
|
|
|
|
|
|
|
|
|
|
$a->setLimitReportData( 'limitreport-pair1', [ 7, 9 ] );
|
|
|
|
|
$a->setLimitReportData( 'limitreport-pair2', [ 27, 29 ] );
|
|
|
|
|
|
|
|
|
|
$a->setLimitReportData( 'limitreport-more1', [ 7, 9, 1 ] );
|
|
|
|
|
$a->setLimitReportData( 'limitreport-more2', [ 27, 29, 21 ] );
|
|
|
|
|
|
|
|
|
|
$a->setLimitReportData( 'limitreport-only-a', 13 );
|
|
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$b->setLimitReportData( 'naive1', 17 );
|
|
|
|
|
$b->setLimitReportData( 'naive2', 17 );
|
|
|
|
|
|
|
|
|
|
$b->setLimitReportData( 'limitreport-simple1', 17 );
|
|
|
|
|
$b->setLimitReportData( 'limitreport-simple2', 17 );
|
|
|
|
|
|
|
|
|
|
$b->setLimitReportData( 'limitreport-pair1', [ 17, 19 ] );
|
|
|
|
|
$b->setLimitReportData( 'limitreport-pair2', [ 17, 19 ] );
|
|
|
|
|
|
|
|
|
|
$b->setLimitReportData( 'limitreport-more1', [ 17, 19, 11 ] );
|
|
|
|
|
$b->setLimitReportData( 'limitreport-more2', [ 17, 19, 11 ] );
|
|
|
|
|
|
|
|
|
|
$b->setLimitReportData( 'limitreport-only-b', 23 );
|
|
|
|
|
|
|
|
|
|
// first write wins
|
|
|
|
|
yield 'limit report' => [ $a, $b, [
|
|
|
|
|
'getLimitReportData' => [
|
|
|
|
|
'naive1' => 7,
|
|
|
|
|
'naive2' => 27,
|
|
|
|
|
'limitreport-simple1' => 7,
|
|
|
|
|
'limitreport-simple2' => 27,
|
|
|
|
|
'limitreport-pair1' => [ 7, 9 ],
|
|
|
|
|
'limitreport-pair2' => [ 27, 29 ],
|
|
|
|
|
'limitreport-more1' => [ 7, 9, 1 ],
|
|
|
|
|
'limitreport-more2' => [ 27, 29, 21 ],
|
|
|
|
|
'limitreport-only-a' => 13,
|
|
|
|
|
],
|
|
|
|
|
'getLimitReportJSData' => [
|
|
|
|
|
'naive1' => 7,
|
|
|
|
|
'naive2' => 27,
|
|
|
|
|
'limitreport' => [
|
|
|
|
|
'simple1' => 7,
|
|
|
|
|
'simple2' => 27,
|
|
|
|
|
'pair1' => [ 'value' => 7, 'limit' => 9 ],
|
|
|
|
|
'pair2' => [ 'value' => 27, 'limit' => 29 ],
|
|
|
|
|
'more1' => [ 7, 9, 1 ],
|
|
|
|
|
'more2' => [ 27, 29, 21 ],
|
|
|
|
|
'only-a' => 13,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
] ];
|
2024-02-24 21:59:05 +00:00
|
|
|
|
|
|
|
|
MWDebug::clearDeprecationFilters();
|
2018-08-07 16:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideMergeInternalMetaDataFrom
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::mergeInternalMetaDataFrom
|
2018-08-07 16:52:40 +00:00
|
|
|
*
|
|
|
|
|
* @param ParserOutput $a
|
|
|
|
|
* @param ParserOutput $b
|
|
|
|
|
* @param array $expected
|
|
|
|
|
*/
|
|
|
|
|
public function testMergeInternalMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
|
Add ParserOutput::{get,set}RenderId() and set render id in ContentRenderer
Set the render ID for each parse stored into cache so that we are able
to identify a specific parse when there are dependencies (for example
in an edit based on that parse). This is recorded as a property added
to the ParserOutput, not the parent CacheTime interface. Even though
the render ID is /related/ to the CacheTime interface, CacheTime is
also used directly as a parser cache key, and the UUID should not be
part of the lookup key.
In general we are trying to move the location where these cache
properties are set as early as possible, so we check at each location
to ensure we don't overwrite a previously-set value. Eventually we
can convert most of these checks into assertions that the cache
properties have already been set (T350538). The primary location for
setting cache properties is the ContentRenderer.
Moved setting the revision timestamp into ContentRenderer as well, as
it was set along the same code paths. An extra parameter was added to
ContentRenderer::getParserOutput() to support this.
Added merge code to ParserOutput::mergeInternalMetaDataFrom() which
should ensure that cache time, revision, timestamp, and render id are
all set properly when multiple slots are combined together in MCR.
In order to ensure the render ID is set on all codepaths we needed to
plumb the GlobalIdGenerator service into ContentRenderer, ParserCache,
ParserCacheFactory, and RevisionOutputCache. Eventually (T350538) it
should only be necessary in the ContentRenderer.
Bug: T350538
Bug: T349868
Followup-To: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80
Change-Id: I72c5e6f86b7f081ab5ce7a56f5365d2f75067a78
2023-09-14 16:11:20 +00:00
|
|
|
$this->filterDeprecated( '/^CacheTime::setCacheTime called with -1 as an argument/' );
|
2018-08-07 16:52:40 +00:00
|
|
|
$a->mergeInternalMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
|
|
|
|
|
// test twice, to make sure the operation is idempotent
|
|
|
|
|
$a->mergeInternalMetaDataFrom( $b );
|
|
|
|
|
|
|
|
|
|
$this->assertFieldValues( $a, $expected );
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-01 20:48:33 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::mergeInternalMetaDataFrom
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getTimes
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::resetParseStartTime
|
2019-02-01 20:48:33 +00:00
|
|
|
*/
|
2018-08-07 16:52:40 +00:00
|
|
|
public function testMergeInternalMetaDataFrom_parseStartTime() {
|
|
|
|
|
/** @var object $a */
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a = TestingAccessWrapper::newFromObject( $a );
|
|
|
|
|
|
|
|
|
|
$a->resetParseStartTime();
|
|
|
|
|
$aClocks = $a->mParseStartTime;
|
|
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$a->mergeInternalMetaDataFrom( $b );
|
|
|
|
|
$mergedClocks = $a->mParseStartTime;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $timestamp ) {
|
|
|
|
|
$this->assertSame( $aClocks[$clock], $timestamp, $clock );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try again, with times in $b also set, and later than $a's
|
|
|
|
|
usleep( 1234 );
|
|
|
|
|
|
|
|
|
|
/** @var object $b */
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
$b = TestingAccessWrapper::newFromObject( $b );
|
|
|
|
|
|
|
|
|
|
$b->resetParseStartTime();
|
|
|
|
|
|
|
|
|
|
$bClocks = $b->mParseStartTime;
|
|
|
|
|
|
2019-06-27 01:33:18 +00:00
|
|
|
$a->mergeInternalMetaDataFrom( $b->object );
|
2018-08-07 16:52:40 +00:00
|
|
|
$mergedClocks = $a->mParseStartTime;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $timestamp ) {
|
|
|
|
|
$this->assertSame( $aClocks[$clock], $timestamp, $clock );
|
|
|
|
|
$this->assertLessThanOrEqual( $bClocks[$clock], $timestamp, $clock );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try again, with $a's times being later
|
|
|
|
|
usleep( 1234 );
|
|
|
|
|
$a->resetParseStartTime();
|
|
|
|
|
$aClocks = $a->mParseStartTime;
|
|
|
|
|
|
2019-06-27 01:33:18 +00:00
|
|
|
$a->mergeInternalMetaDataFrom( $b->object );
|
2018-08-07 16:52:40 +00:00
|
|
|
$mergedClocks = $a->mParseStartTime;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $timestamp ) {
|
|
|
|
|
$this->assertSame( $bClocks[$clock], $timestamp, $clock );
|
|
|
|
|
$this->assertLessThanOrEqual( $aClocks[$clock], $timestamp, $clock );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try again, with no times in $a set
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a = TestingAccessWrapper::newFromObject( $a );
|
|
|
|
|
|
2019-06-27 01:33:18 +00:00
|
|
|
$a->mergeInternalMetaDataFrom( $b->object );
|
2018-08-07 16:52:40 +00:00
|
|
|
$mergedClocks = $a->mParseStartTime;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $timestamp ) {
|
|
|
|
|
$this->assertSame( $bClocks[$clock], $timestamp, $clock );
|
|
|
|
|
}
|
2023-09-28 15:03:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::mergeInternalMetaDataFrom
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getTimes
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::resetParseStartTime
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::recordTimeProfile
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getTimeProfile
|
2023-09-28 15:03:33 +00:00
|
|
|
*/
|
|
|
|
|
public function testMergeInternalMetaDataFrom_timeProfile() {
|
|
|
|
|
/** @var object $a */
|
|
|
|
|
$a = new ParserOutput();
|
|
|
|
|
$a = TestingAccessWrapper::newFromObject( $a );
|
|
|
|
|
|
|
|
|
|
$a->resetParseStartTime();
|
|
|
|
|
usleep( 1234 );
|
|
|
|
|
$a->recordTimeProfile();
|
|
|
|
|
|
|
|
|
|
$aClocks = $a->mTimeProfile;
|
|
|
|
|
|
|
|
|
|
// make sure a second call to recordTimeProfile has no effect
|
|
|
|
|
usleep( 1234 );
|
|
|
|
|
$a->recordTimeProfile();
|
|
|
|
|
|
|
|
|
|
foreach ( $aClocks as $clock => $duration ) {
|
|
|
|
|
$this->assertNotNull( $duration );
|
|
|
|
|
$this->assertGreaterThan( 0, $duration );
|
|
|
|
|
$this->assertSame( $aClocks[$clock], $a->getTimeProfile( $clock ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$b = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
$a->mergeInternalMetaDataFrom( $b );
|
|
|
|
|
$mergedClocks = $a->mTimeProfile;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $duration ) {
|
|
|
|
|
$this->assertSame( $aClocks[$clock], $duration, $clock );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try again, with times in $b also set, and later than $a's
|
|
|
|
|
$b->resetParseStartTime();
|
|
|
|
|
usleep( 1234 );
|
|
|
|
|
$b->recordTimeProfile();
|
|
|
|
|
|
|
|
|
|
$b = TestingAccessWrapper::newFromObject( $b );
|
|
|
|
|
$bClocks = $b->mTimeProfile;
|
|
|
|
|
|
|
|
|
|
$a->mergeInternalMetaDataFrom( $b->object );
|
|
|
|
|
$mergedClocks = $a->mTimeProfile;
|
|
|
|
|
|
|
|
|
|
foreach ( $mergedClocks as $clock => $duration ) {
|
|
|
|
|
$this->assertGreaterThanOrEqual( $aClocks[$clock], $duration, $clock );
|
|
|
|
|
$this->assertGreaterThanOrEqual( $bClocks[$clock], $duration, $clock );
|
|
|
|
|
}
|
2018-08-07 16:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-04 10:32:06 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getCacheTime
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setCacheTime
|
2018-10-04 10:32:06 +00:00
|
|
|
*/
|
|
|
|
|
public function testGetCacheTime() {
|
|
|
|
|
$clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
|
2021-02-07 13:10:36 +00:00
|
|
|
MWTimestamp::setFakeTime( static function () use ( &$clock ) {
|
2018-10-04 10:32:06 +00:00
|
|
|
return $clock++;
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
$time = $po->getCacheTime();
|
|
|
|
|
|
Replace all instances of "per default" with "by default"
According to the dictionary, "per" (or more conventionally "as per")
means "according to". Refer OED "per" sense II.3.a. For example:
"No value was passed, so return null, as per default".
In this sentence, we are not specifying the default, we are referring
to the default. This correct usage of "per default" was used nowhere
in MediaWiki core as far as I can see.
Instead we have "per default" being used to mean "by default", that is,
giving the value to use when no explicit value was specified.
In OED, the phrase "by default" is blessed with its own section just
for computing usage:
"P.1.e. Computing. As an option or setting adopted automatically by a
computer program whenever an alternative is not specified by the user
or programmer. Cf. sense I.7a."
There are highly similar pre-computing usages of the same phrase,
whereas the phrase "per default" is not mentioned.
As a matter of style, I think "per default" should not be used even
when it is strictly correct, since the common incorrect usage makes it
ambiguous and misleading.
Change-Id: Ibcccc65ead864d082677b472b34ff32ff41c60ae
2024-04-29 00:13:28 +00:00
|
|
|
// Use current (fake) time by default. Ignore the last digit.
|
2018-10-04 10:32:06 +00:00
|
|
|
// Subsequent calls must yield the exact same timestamp as the first.
|
|
|
|
|
$this->assertStringStartsWith( '2010010100000', $time );
|
|
|
|
|
$this->assertSame( $time, $po->getCacheTime() );
|
|
|
|
|
|
|
|
|
|
// After setting, the getter must return the time that was set.
|
|
|
|
|
$time = '20110606112233';
|
|
|
|
|
$po->setCacheTime( $time );
|
|
|
|
|
$this->assertSame( $time, $po->getCacheTime() );
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 09:50:14 +00:00
|
|
|
/**
|
2024-01-27 00:11:07 +00:00
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::addExtraCSPScriptSrc
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::addExtraCSPDefaultSrc
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::addExtraCSPStyleSrc
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getExtraCSPScriptSrcs
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getExtraCSPDefaultSrcs
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getExtraCSPStyleSrcs
|
2020-02-03 09:50:14 +00:00
|
|
|
*/
|
|
|
|
|
public function testCSPSources() {
|
|
|
|
|
$po = new ParserOutput;
|
|
|
|
|
|
2023-03-11 19:04:01 +00:00
|
|
|
$this->assertEquals( [], $po->getExtraCSPScriptSrcs(), 'empty Script' );
|
|
|
|
|
$this->assertEquals( [], $po->getExtraCSPStyleSrcs(), 'empty Style' );
|
|
|
|
|
$this->assertEquals( [], $po->getExtraCSPDefaultSrcs(), 'empty Default' );
|
2020-02-03 09:50:14 +00:00
|
|
|
|
|
|
|
|
$po->addExtraCSPScriptSrc( 'foo.com' );
|
|
|
|
|
$po->addExtraCSPScriptSrc( 'bar.com' );
|
|
|
|
|
$po->addExtraCSPDefaultSrc( 'baz.com' );
|
|
|
|
|
$po->addExtraCSPStyleSrc( 'fred.com' );
|
|
|
|
|
$po->addExtraCSPStyleSrc( 'xyzzy.com' );
|
|
|
|
|
|
2023-03-11 19:04:01 +00:00
|
|
|
$this->assertEquals( [ 'foo.com', 'bar.com' ], $po->getExtraCSPScriptSrcs(), 'Script' );
|
|
|
|
|
$this->assertEquals( [ 'baz.com' ], $po->getExtraCSPDefaultSrcs(), 'Default' );
|
|
|
|
|
$this->assertEquals( [ 'fred.com', 'xyzzy.com' ], $po->getExtraCSPStyleSrcs(), 'Style' );
|
2020-02-03 09:50:14 +00:00
|
|
|
}
|
2021-10-12 09:39:32 +00:00
|
|
|
|
2023-07-25 17:10:54 +00:00
|
|
|
public function testOutputStrings() {
|
|
|
|
|
$po = new ParserOutput;
|
|
|
|
|
|
2023-07-27 19:47:00 +00:00
|
|
|
$this->assertEquals( [], $po->getOutputStrings( ParserOutputStringSets::MODULE ) );
|
|
|
|
|
$this->assertEquals( [], $po->getOutputStrings( ParserOutputStringSets::MODULE_STYLE ) );
|
|
|
|
|
$this->assertEquals( [], $po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_SCRIPT_SRC ) );
|
|
|
|
|
$this->assertEquals( [], $po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_STYLE_SRC ) );
|
|
|
|
|
$this->assertEquals( [], $po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_DEFAULT_SRC ) );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( [], $po->getModules() );
|
|
|
|
|
$this->assertEquals( [], $po->getModuleStyles() );
|
|
|
|
|
$this->assertEquals( [], $po->getExtraCSPScriptSrcs() );
|
|
|
|
|
$this->assertEquals( [], $po->getExtraCSPStyleSrcs() );
|
|
|
|
|
$this->assertEquals( [], $po->getExtraCSPDefaultSrcs() );
|
|
|
|
|
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::MODULE, [ 'a' ] );
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::MODULE_STYLE, [ 'b' ] );
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::EXTRA_CSP_SCRIPT_SRC, [ 'foo.com', 'bar.com' ] );
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::EXTRA_CSP_DEFAULT_SRC, [ 'baz.com' ] );
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::EXTRA_CSP_STYLE_SRC, [ 'fred.com' ] );
|
|
|
|
|
$po->appendOutputStrings( ParserOutputStringSets::EXTRA_CSP_STYLE_SRC, [ 'xyzzy.com' ] );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( [ 'a' ], $po->getOutputStrings( ParserOutputStringSets::MODULE ) );
|
|
|
|
|
$this->assertEquals( [ 'b' ], $po->getOutputStrings( ParserOutputStringSets::MODULE_STYLE ) );
|
2023-07-25 17:10:54 +00:00
|
|
|
$this->assertEquals( [ 'foo.com', 'bar.com' ],
|
2023-07-27 19:47:00 +00:00
|
|
|
$po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_SCRIPT_SRC ) );
|
2023-07-25 17:10:54 +00:00
|
|
|
$this->assertEquals( [ 'baz.com' ],
|
2023-07-27 19:47:00 +00:00
|
|
|
$po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_DEFAULT_SRC ) );
|
2023-07-25 17:10:54 +00:00
|
|
|
$this->assertEquals( [ 'fred.com', 'xyzzy.com' ],
|
2023-07-27 19:47:00 +00:00
|
|
|
$po->getOutputStrings( ParserOutputStringSets::EXTRA_CSP_STYLE_SRC ) );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( [ 'a' ], $po->getModules() );
|
|
|
|
|
$this->assertEquals( [ 'b' ], $po->getModuleStyles() );
|
|
|
|
|
$this->assertEquals( [ 'foo.com', 'bar.com' ], $po->getExtraCSPScriptSrcs() );
|
|
|
|
|
$this->assertEquals( [ 'baz.com' ], $po->getExtraCSPDefaultSrcs() );
|
|
|
|
|
$this->assertEquals( [ 'fred.com', 'xyzzy.com' ], $po->getExtraCSPStyleSrcs() );
|
2023-07-25 17:10:54 +00:00
|
|
|
}
|
Add ParserOutput::{get,set}RenderId() and set render id in ContentRenderer
Set the render ID for each parse stored into cache so that we are able
to identify a specific parse when there are dependencies (for example
in an edit based on that parse). This is recorded as a property added
to the ParserOutput, not the parent CacheTime interface. Even though
the render ID is /related/ to the CacheTime interface, CacheTime is
also used directly as a parser cache key, and the UUID should not be
part of the lookup key.
In general we are trying to move the location where these cache
properties are set as early as possible, so we check at each location
to ensure we don't overwrite a previously-set value. Eventually we
can convert most of these checks into assertions that the cache
properties have already been set (T350538). The primary location for
setting cache properties is the ContentRenderer.
Moved setting the revision timestamp into ContentRenderer as well, as
it was set along the same code paths. An extra parameter was added to
ContentRenderer::getParserOutput() to support this.
Added merge code to ParserOutput::mergeInternalMetaDataFrom() which
should ensure that cache time, revision, timestamp, and render id are
all set properly when multiple slots are combined together in MCR.
In order to ensure the render ID is set on all codepaths we needed to
plumb the GlobalIdGenerator service into ContentRenderer, ParserCache,
ParserCacheFactory, and RevisionOutputCache. Eventually (T350538) it
should only be necessary in the ContentRenderer.
Bug: T350538
Bug: T349868
Followup-To: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80
Change-Id: I72c5e6f86b7f081ab5ce7a56f5365d2f75067a78
2023-09-14 16:11:20 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getCacheTime()
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setCacheTime()
|
|
|
|
|
*/
|
|
|
|
|
public function testCacheTime() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
// Should not have a cache time yet
|
|
|
|
|
$this->assertFalse( $po->hasCacheTime() );
|
|
|
|
|
// But calling ::get assigns a cache time
|
|
|
|
|
$po->getCacheTime();
|
|
|
|
|
$this->assertTrue( $po->hasCacheTime() );
|
|
|
|
|
// Reset cache time
|
|
|
|
|
$po->setCacheTime( "20240207202040" );
|
|
|
|
|
$this->assertSame( "20240207202040", $po->getCacheTime() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getRenderId()
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::setRenderId()
|
|
|
|
|
*/
|
|
|
|
|
public function testRenderId() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
// Should be null when unset
|
|
|
|
|
$this->assertNull( $po->getRenderId() );
|
|
|
|
|
|
|
|
|
|
// Sanity check for setter and getter
|
|
|
|
|
$po->setRenderId( "TestRenderId" );
|
|
|
|
|
$this->assertEquals( "TestRenderId", $po->getRenderId() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\Parser\ParserOutput::getRenderId()
|
|
|
|
|
*/
|
|
|
|
|
public function testRenderIdBackCompat() {
|
|
|
|
|
$po = new ParserOutput();
|
|
|
|
|
|
|
|
|
|
// Parser cache used to contain extension data under a different name
|
|
|
|
|
$po->setExtensionData( 'parsoid-render-id', "1234/LegacyRenderId" );
|
|
|
|
|
$this->assertEquals( "LegacyRenderId", $po->getRenderId() );
|
|
|
|
|
}
|
2024-01-12 05:07:33 +00:00
|
|
|
|
|
|
|
|
public function testSetFromParserOptions() {
|
2023-11-06 20:43:31 +00:00
|
|
|
// parser output set from canonical parser options
|
2024-01-12 05:07:33 +00:00
|
|
|
$pOptions = ParserOptions::newFromAnon();
|
|
|
|
|
$pOutput = new ParserOutput;
|
|
|
|
|
$pOutput->setFromParserOptions( $pOptions );
|
|
|
|
|
$this->assertSame( 'mw-parser-output', $pOutput->getWrapperDivClass() );
|
|
|
|
|
$this->assertFalse( $pOutput->getOutputFlag( ParserOutputFlags::IS_PREVIEW ) );
|
|
|
|
|
$this->assertTrue( $pOutput->isCacheable() );
|
2023-11-06 20:43:31 +00:00
|
|
|
$this->assertFalse( $pOutput->getOutputFlag( ParserOutputFlags::NO_SECTION_EDIT_LINKS ) );
|
2024-04-05 22:46:57 +00:00
|
|
|
$this->assertFalse( $pOutput->getOutputFlag( ParserOutputFlags::COLLAPSIBLE_SECTIONS ) );
|
2024-01-12 05:07:33 +00:00
|
|
|
|
2023-11-06 20:43:31 +00:00
|
|
|
// set the various parser options and verify in parser output
|
2024-01-12 05:07:33 +00:00
|
|
|
$pOptions->setWrapOutputClass( 'test-wrapper' );
|
|
|
|
|
$pOptions->setIsPreview( true );
|
2023-11-06 20:43:31 +00:00
|
|
|
$pOptions->setSuppressSectionEditLinks();
|
2024-04-05 22:46:57 +00:00
|
|
|
$pOptions->setCollapsibleSections();
|
2024-01-12 05:07:33 +00:00
|
|
|
$pOutput = new ParserOutput;
|
|
|
|
|
$pOutput->setFromParserOptions( $pOptions );
|
|
|
|
|
$this->assertEquals( 'test-wrapper', $pOutput->getWrapperDivClass() );
|
|
|
|
|
$this->assertTrue( $pOutput->getOutputFlag( ParserOutputFlags::IS_PREVIEW ) );
|
|
|
|
|
$this->assertFalse( $pOutput->isCacheable() );
|
2023-11-06 20:43:31 +00:00
|
|
|
$this->assertTrue( $pOutput->getOutputFlag( ParserOutputFlags::NO_SECTION_EDIT_LINKS ) );
|
2024-04-05 22:46:57 +00:00
|
|
|
$this->assertTrue( $pOutput->getOutputFlag( ParserOutputFlags::COLLAPSIBLE_SECTIONS ) );
|
2024-01-12 05:07:33 +00:00
|
|
|
}
|
2012-09-29 10:36:33 +00:00
|
|
|
}
|