Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 113 |
|
0.00% |
0 / 27 |
CRAP | |
0.00% |
0 / 1 |
| SiteConfig | |
0.00% |
0 / 113 |
|
0.00% |
0 / 27 |
1560 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| reset | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
6 | |||
| deleteNamespace | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
| disableSubpagesForNS | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| enableSubpagesForNS | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| updateNamespace | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
| setupInterwikiMap | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| interwikiMap | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| server | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| script | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| scriptpath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| baseURI | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| allowedExternalImagePrefixes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getMWConfigValue | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
| setInterwikiMagic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| interwikiMagic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| setMagicLinkEnabled | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| magicLinkEnabled | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| fakeTimestamp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| timezoneOffset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| widthOption | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| registerParserTestExtension | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| unregisterParserTestExtension | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 | |||
| setExternalLinkTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getExternalLinkTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| setNoFollowConfig | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| getNoFollowConfig | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | declare( strict_types = 1 ); |
| 3 | |
| 4 | namespace Wikimedia\Parsoid\ParserTests; |
| 5 | |
| 6 | use Monolog\Handler\FilterHandler; |
| 7 | use Monolog\Logger; |
| 8 | use Psr\Log\LoggerInterface; |
| 9 | use Wikimedia\Assert\Assert; |
| 10 | use Wikimedia\Parsoid\Config\Api\ApiHelper; |
| 11 | use Wikimedia\Parsoid\Config\Api\SiteConfig as ApiSiteConfig; |
| 12 | use Wikimedia\Parsoid\Ext\ExtensionModule; |
| 13 | use Wikimedia\Parsoid\Utils\ConfigUtils; |
| 14 | use Wikimedia\Parsoid\Utils\Utils; |
| 15 | |
| 16 | class SiteConfig extends ApiSiteConfig { |
| 17 | /** @var array overrides parent-class info */ |
| 18 | private $interwikiMap; |
| 19 | |
| 20 | /** @var bool overrides parent-class info */ |
| 21 | private $interwikiMagic; |
| 22 | |
| 23 | /** @var array<string,bool> overrides parent-class info */ |
| 24 | private $enabledMagicLinks = []; |
| 25 | |
| 26 | /** @var array overrides parent-class server info */ |
| 27 | private $serverData; |
| 28 | |
| 29 | /** @var array overrides parent-class info */ |
| 30 | public $allowedExternalImagePrefixes = [ '' ]; |
| 31 | |
| 32 | /** |
| 33 | * Init to default value for parserTests. Overrides parent-class info. |
| 34 | * @var array |
| 35 | */ |
| 36 | public $responsiveReferences; |
| 37 | |
| 38 | /** @var ?int */ |
| 39 | public $thumbsize; |
| 40 | |
| 41 | /** @var LoggerInterface */ |
| 42 | public $suppressLogger; |
| 43 | |
| 44 | /** @var string|false */ |
| 45 | private $externalLinkTarget = false; |
| 46 | |
| 47 | /** @var ?array */ |
| 48 | private $noFollowConfig; |
| 49 | |
| 50 | /** @inheritDoc */ |
| 51 | public function __construct( ApiHelper $api, array $opts ) { |
| 52 | $logger = self::createLogger(); |
| 53 | $opts['logger'] = $logger; |
| 54 | parent::__construct( $api, $opts ); |
| 55 | |
| 56 | // Needed for bidi-char-scrubbing html2wt tests. |
| 57 | $this->scrubBidiChars = true; |
| 58 | |
| 59 | // Logger to suppress all logs but fatals (critical errors) |
| 60 | $this->suppressLogger = new Logger( "ParserTests" ); |
| 61 | $errorLogHandler = $logger->getHandlers()[0]; |
| 62 | $filterHandler = new FilterHandler( $errorLogHandler, Logger::CRITICAL ); |
| 63 | $this->suppressLogger->pushHandler( $filterHandler ); |
| 64 | } |
| 65 | |
| 66 | public function reset() { |
| 67 | parent::reset(); |
| 68 | |
| 69 | // adjust config to match that used for PHP tests |
| 70 | // see core/tests/parser/parserTest.inc:setupGlobals() for |
| 71 | // full set of config normalizations done. |
| 72 | $this->serverData = [ |
| 73 | 'server' => 'http://example.org', |
| 74 | 'scriptpath' => '/', |
| 75 | 'script' => '/index.php', |
| 76 | 'articlepath' => '/wiki/$1', |
| 77 | 'baseURI' => 'http://example.org/wiki/' |
| 78 | ]; |
| 79 | |
| 80 | // Add 'MemoryAlpha' namespace (T53680) |
| 81 | $this->updateNamespace( [ |
| 82 | 'id' => 100, |
| 83 | 'case' => 'first-letter', |
| 84 | 'subpages' => false, |
| 85 | 'canonical' => 'MemoryAlpha', |
| 86 | 'name' => 'MemoryAlpha', |
| 87 | ] ); |
| 88 | |
| 89 | // Testing |
| 90 | if ( $this->iwp() === 'enwiki' ) { |
| 91 | $this->updateNamespace( [ |
| 92 | 'id' => 4, |
| 93 | 'case' => 'first-letter', |
| 94 | 'subpages' => true, |
| 95 | 'canonical' => 'Project', |
| 96 | 'name' => 'Base MW' |
| 97 | ] ); |
| 98 | $this->updateNamespace( [ |
| 99 | 'id' => 5, |
| 100 | 'case' => 'first-letter', |
| 101 | 'subpages' => true, |
| 102 | 'canonical' => 'Project talk', |
| 103 | 'name' => 'Base MW talk' |
| 104 | ] ); |
| 105 | } |
| 106 | |
| 107 | // Reset other values to defaults |
| 108 | $this->responsiveReferences = [ 'enabled' => true, 'threshold' => 10 ]; |
| 109 | $this->disableSubpagesForNS( 0 ); |
| 110 | $this->unregisterParserTestExtension( new StyleTag() ); |
| 111 | $this->unregisterParserTestExtension( new RawHTML() ); |
| 112 | $this->unregisterParserTestExtension( new ParserHook() ); |
| 113 | $this->unregisterParserTestExtension( new DummyAnnotation() ); |
| 114 | $this->unregisterParserTestExtension( new I18nTag() ); |
| 115 | $this->thumbsize = null; |
| 116 | $this->externalLinkTarget = false; |
| 117 | $this->noFollowConfig = null; |
| 118 | } |
| 119 | |
| 120 | private function deleteNamespace( string $name ): void { |
| 121 | $normName = Utils::normalizeNamespaceName( $name ); |
| 122 | $id = $this->namespaceId( $normName ); |
| 123 | |
| 124 | if ( !$id ) { |
| 125 | $normName = $name; |
| 126 | $id = $this->namespaceId( $normName ); |
| 127 | } |
| 128 | |
| 129 | if ( $id ) { |
| 130 | unset( $this->nsCanon[$normName] ); |
| 131 | unset( $this->nsIds[$normName] ); |
| 132 | unset( $this->nsNames[$id] ); |
| 133 | unset( $this->nsCase[$id] ); |
| 134 | unset( $this->nsWithSubpages[$id] ); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | public function disableSubpagesForNS( int $ns ): void { |
| 139 | $this->nsWithSubpages[$ns] = false; |
| 140 | } |
| 141 | |
| 142 | public function enableSubpagesForNS( int $ns ): void { |
| 143 | $this->nsWithSubpages[$ns] = true; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Update namespace info. |
| 148 | * |
| 149 | * Delete any existing namespace with the same id. |
| 150 | * Add new namespaces. |
| 151 | * |
| 152 | * @param array $ns |
| 153 | */ |
| 154 | private function updateNamespace( array $ns ): void { |
| 155 | $old = $this->namespaceName( (int)$ns['id'] ); |
| 156 | if ( $old ) { // Id may already be defined; if so, clear it. |
| 157 | if ( $old === Utils::normalizeNamespaceName( $ns['name'] ) ) { |
| 158 | // ParserTests does a lot redundantly. |
| 159 | return; |
| 160 | } |
| 161 | $this->deleteNamespace( $old ); |
| 162 | } |
| 163 | $this->addNamespace( $ns ); |
| 164 | Assert::invariant( $ns['case'] === 'first-letter', |
| 165 | 'ParserTests/SiteConfig only supports first-letter case currently' ); |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * Compute the interwiki map based on mock raw data. |
| 170 | * This replaces the previously computed interwiki map |
| 171 | * based on data from MockApiHelper |
| 172 | * |
| 173 | * @param array $iwData |
| 174 | */ |
| 175 | public function setupInterwikiMap( array $iwData ): void { |
| 176 | $this->interwikiMap = ConfigUtils::computeInterwikiMap( $iwData ); |
| 177 | $this->interwikiMapNoNamespaces = null; |
| 178 | $this->iwMatcher = null; |
| 179 | } |
| 180 | |
| 181 | public function interwikiMap(): array { |
| 182 | return $this->interwikiMap; |
| 183 | } |
| 184 | |
| 185 | public function server(): string { |
| 186 | return $this->serverData['server']; |
| 187 | } |
| 188 | |
| 189 | public function script(): string { |
| 190 | return $this->serverData['script']; |
| 191 | } |
| 192 | |
| 193 | public function scriptpath(): string { |
| 194 | return $this->serverData['scriptpath']; |
| 195 | } |
| 196 | |
| 197 | public function baseURI(): string { |
| 198 | return $this->serverData['baseURI']; |
| 199 | } |
| 200 | |
| 201 | public function allowedExternalImagePrefixes(): array { |
| 202 | return $this->allowedExternalImagePrefixes; |
| 203 | } |
| 204 | |
| 205 | /** @inheritDoc */ |
| 206 | public function getMWConfigValue( string $key ) { |
| 207 | switch ( $key ) { |
| 208 | case 'CiteResponsiveReferences': |
| 209 | return $this->responsiveReferences['enabled']; |
| 210 | |
| 211 | case 'CiteResponsiveReferencesThreshold': |
| 212 | return $this->responsiveReferences['threshold']; |
| 213 | |
| 214 | default: |
| 215 | return null; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | public function setInterwikiMagic( bool $val ): void { |
| 220 | $this->interwikiMagic = $val; |
| 221 | } |
| 222 | |
| 223 | public function interwikiMagic(): bool { |
| 224 | return $this->interwikiMagic; |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * @param string $which One of "RFC", "PMID", or "ISBN". |
| 229 | * @param bool $val |
| 230 | */ |
| 231 | public function setMagicLinkEnabled( string $which, bool $val ): void { |
| 232 | $this->enabledMagicLinks[$which] = $val; |
| 233 | } |
| 234 | |
| 235 | public function magicLinkEnabled( string $which ): bool { |
| 236 | // defaults to enabled |
| 237 | return $this->enabledMagicLinks[$which] ?? true; |
| 238 | } |
| 239 | |
| 240 | public function fakeTimestamp(): ?int { |
| 241 | return 123; |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Hardcode value for parser tests |
| 246 | * |
| 247 | * @return int |
| 248 | */ |
| 249 | public function timezoneOffset(): int { |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | public function widthOption(): int { |
| 254 | return $this->thumbsize ?? 180; // wgThumbLimits setting in core ParserTestRunner |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Register an extension for use in parser tests |
| 259 | * @param ExtensionModule $ext |
| 260 | */ |
| 261 | public function registerParserTestExtension( ExtensionModule $ext ): void { |
| 262 | $this->getExtConfig(); // ensure $this->extConfig is initialized |
| 263 | $this->processExtensionModule( $ext ); |
| 264 | } |
| 265 | |
| 266 | /** |
| 267 | * Unregister a previously registered extension. |
| 268 | * @param ExtensionModule $ext |
| 269 | */ |
| 270 | private function unregisterParserTestExtension( ExtensionModule $ext ): void { |
| 271 | $extConfig = $ext->getConfig(); |
| 272 | $name = $extConfig['name']; |
| 273 | |
| 274 | $this->getExtConfig(); // ensure $this->extConfig is initialized |
| 275 | foreach ( ( $extConfig['tags'] ?? [] ) as $tagConfig ) { |
| 276 | $lowerTagName = mb_strtolower( $tagConfig['name'] ); |
| 277 | unset( $this->extConfig['allTags'][$lowerTagName] ); |
| 278 | unset( $this->extConfig['parsoidExtTags'][$lowerTagName] ); |
| 279 | } |
| 280 | |
| 281 | foreach ( ( $extConfig['annotations'] ?? [] ) as $annotationTag ) { |
| 282 | $lowerTagName = mb_strtolower( $annotationTag ); |
| 283 | unset( $this->extConfig['allTags'][$lowerTagName] ); |
| 284 | unset( $this->extConfig['annotationTags'][$lowerTagName] ); |
| 285 | } |
| 286 | |
| 287 | if ( isset( $extConfig['domProcessors'] ) ) { |
| 288 | unset( $this->extConfig['domProcessors'][$name] ); |
| 289 | } |
| 290 | |
| 291 | /* |
| 292 | * FIXME: Unsetting contentmodels is also tricky with the current |
| 293 | * state tracked during registration. We will have to reprocess all |
| 294 | * extensions or maintain a linked list of applicable extensions |
| 295 | * for every content model |
| 296 | */ |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * @param string|false $value |
| 301 | */ |
| 302 | public function setExternalLinkTarget( $value ): void { |
| 303 | $this->externalLinkTarget = $value; |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * @inheritDoc |
| 308 | */ |
| 309 | public function getExternalLinkTarget() { |
| 310 | return $this->externalLinkTarget; |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * @param string $key |
| 315 | * @param mixed $value |
| 316 | */ |
| 317 | public function setNoFollowConfig( string $key, $value ): void { |
| 318 | $noFollowConfig = $this->getNoFollowConfig(); |
| 319 | $noFollowConfig[$key] = $value; |
| 320 | $this->noFollowConfig = $noFollowConfig; |
| 321 | } |
| 322 | |
| 323 | /** |
| 324 | * @inheritDoc |
| 325 | */ |
| 326 | public function getNoFollowConfig(): array { |
| 327 | if ( $this->noFollowConfig === null ) { |
| 328 | $this->noFollowConfig = parent::getNoFollowConfig(); |
| 329 | } |
| 330 | return $this->noFollowConfig; |
| 331 | } |
| 332 | } |