2020-05-11 15:52:06 +00:00
|
|
|
<?php
|
|
|
|
|
2020-05-14 22:44:49 +00:00
|
|
|
namespace MediaWiki\Extension\DiscussionTools\Tests;
|
|
|
|
|
2022-10-11 22:41:17 +00:00
|
|
|
use FormatJson;
|
2024-06-08 22:02:35 +00:00
|
|
|
use MediaWiki\Cache\GenderCache;
|
|
|
|
use MediaWiki\Config\HashConfig;
|
|
|
|
use MediaWiki\Config\MultiConfig;
|
2022-03-11 23:42:25 +00:00
|
|
|
use MediaWiki\Config\ServiceOptions;
|
2020-05-14 22:44:49 +00:00
|
|
|
use MediaWiki\Extension\DiscussionTools\CommentParser;
|
2022-03-11 23:42:25 +00:00
|
|
|
use MediaWiki\Interwiki\NullInterwikiLookup;
|
|
|
|
use MediaWiki\Languages\LanguageConverterFactory;
|
|
|
|
use MediaWiki\Languages\LanguageFactory;
|
2024-07-09 10:39:08 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2020-05-11 15:52:06 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2024-06-08 22:02:35 +00:00
|
|
|
use MediaWiki\Title\MediaWikiTitleCodec;
|
|
|
|
use MediaWiki\Title\NamespaceInfo;
|
|
|
|
use MediaWiki\User\Options\StaticUserOptionsLookup;
|
2021-07-29 02:16:15 +00:00
|
|
|
use Wikimedia\Parsoid\DOM\Document;
|
2022-03-09 19:32:51 +00:00
|
|
|
use Wikimedia\Parsoid\DOM\Element;
|
2020-07-30 23:34:56 +00:00
|
|
|
use Wikimedia\Parsoid\Utils\DOMCompat;
|
2020-06-16 20:13:31 +00:00
|
|
|
use Wikimedia\Parsoid\Utils\DOMUtils;
|
2020-05-11 15:52:06 +00:00
|
|
|
|
2021-02-02 14:12:51 +00:00
|
|
|
trait TestUtils {
|
2020-05-11 15:52:06 +00:00
|
|
|
|
|
|
|
/**
|
2022-03-09 19:32:51 +00:00
|
|
|
* Create a Document from a string.
|
2020-05-11 15:52:06 +00:00
|
|
|
*/
|
2021-07-29 02:16:15 +00:00
|
|
|
protected static function createDocument( string $html ): Document {
|
2022-03-09 19:32:51 +00:00
|
|
|
return DOMUtils::parseHTML( $html );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the node that is expected to contain thread items.
|
|
|
|
*/
|
|
|
|
protected static function getThreadContainer( Document $doc ): Element {
|
|
|
|
// In tests created from Parsoid output, comments are contained directly in <body>.
|
|
|
|
// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
|
|
|
|
$body = DOMCompat::getBody( $doc );
|
|
|
|
$wrapper = DOMCompat::querySelector( $body, 'div.mw-parser-output' );
|
|
|
|
return $wrapper ?: $body;
|
2020-05-11 15:52:06 +00:00
|
|
|
}
|
|
|
|
|
2021-02-27 00:15:42 +00:00
|
|
|
/**
|
|
|
|
* Get text from path
|
|
|
|
*/
|
2021-07-22 07:25:13 +00:00
|
|
|
protected static function getText( string $relativePath ): string {
|
2021-02-27 00:15:42 +00:00
|
|
|
return file_get_contents( __DIR__ . '/../' . $relativePath );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write text to path
|
|
|
|
*/
|
2021-07-22 07:25:13 +00:00
|
|
|
protected static function overwriteTextFile( string $relativePath, string $text ): void {
|
2021-02-27 00:15:42 +00:00
|
|
|
file_put_contents( __DIR__ . '/../' . $relativePath, $text );
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:06 +00:00
|
|
|
/**
|
|
|
|
* Get parsed JSON from path
|
|
|
|
*
|
|
|
|
* @param string $relativePath
|
2020-05-19 19:01:03 +00:00
|
|
|
* @param bool $assoc See json_decode()
|
2020-05-11 15:52:06 +00:00
|
|
|
* @return array
|
|
|
|
*/
|
2021-07-22 07:25:13 +00:00
|
|
|
protected static function getJson( string $relativePath, bool $assoc = true ): array {
|
2020-05-11 15:52:06 +00:00
|
|
|
$json = json_decode(
|
2020-05-18 20:07:00 +00:00
|
|
|
file_get_contents( __DIR__ . '/' . $relativePath ),
|
2020-05-19 19:01:03 +00:00
|
|
|
$assoc
|
2020-05-11 15:52:06 +00:00
|
|
|
);
|
|
|
|
return $json;
|
|
|
|
}
|
|
|
|
|
2020-07-30 23:34:56 +00:00
|
|
|
/**
|
|
|
|
* Write JSON to path
|
|
|
|
*/
|
2021-07-22 07:25:13 +00:00
|
|
|
protected static function overwriteJsonFile( string $relativePath, array $data ): void {
|
2022-10-11 22:41:17 +00:00
|
|
|
$json = FormatJson::encode( $data, "\t", FormatJson::ALL_OK );
|
2020-07-30 23:34:56 +00:00
|
|
|
file_put_contents( __DIR__ . '/' . $relativePath, $json . "\n" );
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:06 +00:00
|
|
|
/**
|
|
|
|
* Get HTML from path
|
|
|
|
*/
|
2021-07-22 07:25:13 +00:00
|
|
|
protected static function getHtml( string $relativePath ): string {
|
2022-03-09 19:32:51 +00:00
|
|
|
return file_get_contents( __DIR__ . '/../' . $relativePath );
|
2020-05-11 15:52:06 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 23:34:56 +00:00
|
|
|
/**
|
|
|
|
* Write HTML to path
|
|
|
|
*/
|
2022-03-09 19:32:51 +00:00
|
|
|
protected static function overwriteHtmlFile( string $relPath, Element $container, string $origRelPath ): void {
|
2020-07-30 23:34:56 +00:00
|
|
|
// Do not use $doc->saveHtml(), it outputs an awful soup of HTML entities for documents with
|
|
|
|
// non-ASCII characters
|
2020-08-10 21:31:49 +00:00
|
|
|
$html = file_get_contents( __DIR__ . '/../' . $origRelPath );
|
2020-07-30 23:34:56 +00:00
|
|
|
|
2022-03-09 19:32:51 +00:00
|
|
|
$newInnerHtml = DOMCompat::getInnerHTML( $container );
|
|
|
|
|
|
|
|
if ( strtolower( $container->tagName ) === 'body' ) {
|
|
|
|
// Apparently <body> innerHTML always has a trailing newline, even if the source HTML did not,
|
|
|
|
// and we need to preserve whatever whitespace was there to avoid test failures
|
|
|
|
preg_match( '`(\s*)(</body>|\z)`s', $html, $matches );
|
|
|
|
$newInnerHtml = rtrim( $newInnerHtml ) . $matches[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Quote \ and $ in the replacement text
|
|
|
|
$quotedNewInnerHtml = strtr( $newInnerHtml, [ '\\' => '\\\\', '$' => '\\$' ] );
|
|
|
|
|
|
|
|
if ( strtolower( $container->tagName ) === 'body' ) {
|
2023-05-19 07:36:50 +00:00
|
|
|
if ( str_contains( $html, '<body' ) ) {
|
2022-03-09 19:32:51 +00:00
|
|
|
$html = preg_replace(
|
|
|
|
'`(<body[^>]*>)(.*)(</body>)`s',
|
|
|
|
'$1' . $quotedNewInnerHtml . '$3',
|
|
|
|
$html
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$html = $newInnerHtml;
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-30 23:34:56 +00:00
|
|
|
$html = preg_replace(
|
2022-03-09 19:32:51 +00:00
|
|
|
'`(<div class="mw-parser-output">)(.*)(</div>)`s',
|
|
|
|
'$1' . $quotedNewInnerHtml . '$3',
|
2020-07-30 23:34:56 +00:00
|
|
|
$html
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-10 21:31:49 +00:00
|
|
|
file_put_contents( __DIR__ . '/../' . $relPath, $html );
|
2020-07-30 23:34:56 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 22:40:27 +00:00
|
|
|
private static function prepareConfig( array $config, array $data ): array {
|
2022-03-11 23:42:25 +00:00
|
|
|
return [
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::LanguageCode => $config['wgContentLanguage'],
|
|
|
|
MainConfigNames::ArticlePath => $config['wgArticlePath'],
|
2022-03-11 23:42:25 +00:00
|
|
|
// TODO: Move this to $config
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::Localtimezone => $data['localTimezone'],
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
// Defaults for NamespaceInfo
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::CanonicalNamespaceNames => NamespaceInfo::CANONICAL_NAMES,
|
|
|
|
MainConfigNames::CapitalLinkOverrides => [],
|
|
|
|
MainConfigNames::CapitalLinks => true,
|
|
|
|
MainConfigNames::ContentNamespaces => [ NS_MAIN ],
|
|
|
|
MainConfigNames::ExtraSignatureNamespaces => [],
|
|
|
|
MainConfigNames::NamespaceContentModels => [],
|
|
|
|
MainConfigNames::NamespacesWithSubpages => [
|
2022-03-11 23:42:25 +00:00
|
|
|
NS_TALK => true,
|
|
|
|
NS_USER => true,
|
|
|
|
NS_USER_TALK => true,
|
|
|
|
NS_PROJECT => true,
|
|
|
|
NS_PROJECT_TALK => true,
|
|
|
|
NS_FILE_TALK => true,
|
|
|
|
NS_MEDIAWIKI => true,
|
|
|
|
NS_MEDIAWIKI_TALK => true,
|
|
|
|
NS_TEMPLATE => true,
|
|
|
|
NS_TEMPLATE_TALK => true,
|
|
|
|
NS_HELP => true,
|
|
|
|
NS_HELP_TALK => true,
|
|
|
|
NS_CATEGORY_TALK => true
|
|
|
|
],
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::NonincludableNamespaces => [],
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
// Defaults for LanguageFactory
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::DummyLanguageCodes => [],
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
// Defaults for LanguageConverterFactory
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::UsePigLatinVariant => false,
|
|
|
|
MainConfigNames::DisableLangConversion => false,
|
|
|
|
MainConfigNames::DisableTitleConversion => false,
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
// Defaults for Language
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::ExtraGenderNamespaces => [],
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
// Overrides
|
2024-07-09 10:39:08 +00:00
|
|
|
MainConfigNames::ExtraNamespaces => array_diff_key(
|
|
|
|
$config['wgFormattedNamespaces'], NamespaceInfo::CANONICAL_NAMES ),
|
|
|
|
MainConfigNames::MetaNamespace => strtr( $config['wgFormattedNamespaces'][NS_PROJECT], ' ', '_' ),
|
|
|
|
MainConfigNames::MetaNamespaceTalk => strtr( $config['wgFormattedNamespaces'][NS_PROJECT_TALK], ' ', '_' ),
|
|
|
|
MainConfigNames::NamespaceAliases => $config['wgNamespaceIds'],
|
2022-03-11 23:42:25 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function createParser( array $config, array $data ): CommentParser {
|
|
|
|
// TODO: Derive everything from $config and $data without using global services
|
2020-05-11 15:52:06 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2022-03-11 23:42:25 +00:00
|
|
|
|
|
|
|
$config = self::prepareConfig( $config, $data );
|
|
|
|
|
|
|
|
$langConvFactory = new LanguageConverterFactory(
|
|
|
|
new ServiceOptions( LanguageConverterFactory::CONSTRUCTOR_OPTIONS, $config ),
|
|
|
|
$services->getObjectFactory(),
|
|
|
|
static function () use ( $services ) {
|
|
|
|
return $services->getLanguageFactory()->getLanguage( $config['LanguageCode'] );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-05-14 22:44:49 +00:00
|
|
|
return new CommentParser(
|
2022-03-11 23:42:25 +00:00
|
|
|
new HashConfig( $config ),
|
|
|
|
$services->getLanguageFactory()->getLanguage( $config['LanguageCode'] ),
|
|
|
|
$langConvFactory,
|
2022-02-21 22:07:38 +00:00
|
|
|
new MockLanguageData( $data ),
|
2022-03-11 23:42:25 +00:00
|
|
|
$this->createTitleParser( $config )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function createTitleParser( array $config ): MediaWikiTitleCodec {
|
|
|
|
// TODO: Derive everything from $config and $data without using global services
|
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
|
|
|
if ( isset( $config['wgArticlePath'] ) ) {
|
|
|
|
$config = self::prepareConfig( $config, [ 'localTimezone' => '' ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
$nsInfo = new NamespaceInfo(
|
|
|
|
new ServiceOptions( NamespaceInfo::CONSTRUCTOR_OPTIONS, $config ),
|
|
|
|
$services->getHookContainer(),
|
|
|
|
[],
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
$langFactory = new LanguageFactory(
|
|
|
|
new ServiceOptions( LanguageFactory::CONSTRUCTOR_OPTIONS, $config ),
|
|
|
|
$nsInfo,
|
|
|
|
$services->getLocalisationCache(),
|
|
|
|
$services->getLanguageNameUtils(),
|
|
|
|
$services->getLanguageFallback(),
|
|
|
|
$services->getLanguageConverterFactory(),
|
|
|
|
$services->getHookContainer(),
|
|
|
|
new MultiConfig( [ new HashConfig( $config ), $services->getMainConfig() ] )
|
|
|
|
);
|
|
|
|
|
|
|
|
$contLang = $langFactory->getLanguage( $config['LanguageCode'] );
|
|
|
|
|
|
|
|
return new MediaWikiTitleCodec(
|
|
|
|
$contLang,
|
|
|
|
new GenderCache( $nsInfo, null, new StaticUserOptionsLookup( [] ) ),
|
|
|
|
[],
|
|
|
|
new NullInterwikiLookup(),
|
|
|
|
$nsInfo
|
2020-05-11 15:52:06 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|