Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 27
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteConfig
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 27
1560
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 reset
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
6
 deleteNamespace
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 disableSubpagesForNS
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 enableSubpagesForNS
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateNamespace
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 setupInterwikiMap
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 interwikiMap
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 server
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 script
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 scriptpath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 baseURI
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 allowedExternalImagePrefixes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMWConfigValue
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 setInterwikiMagic
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 interwikiMagic
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMagicLinkEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 magicLinkEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fakeTimestamp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 timezoneOffset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 widthOption
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 registerParserTestExtension
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 unregisterParserTestExtension
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 setExternalLinkTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExternalLinkTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNoFollowConfig
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getNoFollowConfig
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\ParserTests;
5
6use Monolog\Handler\FilterHandler;
7use Monolog\Logger;
8use Psr\Log\LoggerInterface;
9use Wikimedia\Assert\Assert;
10use Wikimedia\Parsoid\Config\Api\ApiHelper;
11use Wikimedia\Parsoid\Config\Api\SiteConfig as ApiSiteConfig;
12use Wikimedia\Parsoid\Ext\ExtensionModule;
13use Wikimedia\Parsoid\Utils\ConfigUtils;
14use Wikimedia\Parsoid\Utils\Utils;
15
16class 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}