Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
8.33% covered (danger)
8.33%
4 / 48
CRAP
20.83% covered (danger)
20.83%
20 / 96
MockSiteConfig
0.00% covered (danger)
0.00%
0 / 1
8.33% covered (danger)
8.33%
4 / 48
2227.30
20.83% covered (danger)
20.83%
20 / 96
 __construct
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (warning)
85.71%
12 / 14
 tidyWhitespaceBugMaxLength
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 allowedExternalImagePrefixes
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 baseURI
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 exportMetadataToHead
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 12
 redirectRegexp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 categoryRegexp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 bswRegexp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 canonicalNamespaceId
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 namespaceId
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 namespaceName
0.00% covered (danger)
0.00%
0 / 1
4.59
66.67% covered (warning)
66.67%
4 / 6
 namespaceHasSubpages
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 namespaceCase
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 specialPageLocalName
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 setInterwikiMagic
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 interwikiMagic
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 interwikiMap
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 iwp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 legalTitleChars
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 linkPrefixRegex
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 linkTrail
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 linkTrailRegex
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 lang
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 mainpage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 responsiveReferences
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 rtl
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 langConverterEnabled
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 script
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 scriptpath
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 server
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 timezoneOffset
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 variants
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 widthOption
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getVariableIDs
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 haveComputedFunctionSynonyms
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 updateFunctionSynonym
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getMagicWords
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getMagicWordMatcher
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getParameterizedAliasMatcher
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 11
 getNonNativeExtensionTags
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getMaxTemplateDepth
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getSpecialPageAliases
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 getSpecialNSAliases
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getProtocols
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 fakeTimestamp
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 setFakeTimestamp
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
 setTimezoneOffset
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 scrubBidiChars
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Mocks;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Logger;
use Wikimedia\Parsoid\Config\SiteConfig;
use Wikimedia\Parsoid\Config\StubMetadataCollector;
use Wikimedia\Parsoid\Core\ContentMetadataCollector;
use Wikimedia\Parsoid\DOM\Document;
use Wikimedia\Parsoid\Utils\Utils;
class MockSiteConfig extends SiteConfig {
    /** @var int Unix timestamp */
    private $fakeTimestamp = 946782245; // 2000-01-02T03:04:05Z
    /** @var int */
    private $timezoneOffset = 0; // UTC
    /** @var bool */
    private $interwikiMagic = true;
    /** @var int|null */
    private $tidyWhitespaceBugMaxLength = null;
    protected $namespaceMap = [
        'media' => -2,
        'special' => -1,
        '' => 0,
        'talk' => 1,
        'user' => 2,
        'user_talk' => 3,
        // Last one will be used by namespaceName
        'project' => 4, 'wp' => 4, 'wikipedia' => 4,
        'project_talk' => 5, 'wt' => 5, 'wikipedia_talk' => 5,
        'file' => 6,
        'file_talk' => 7,
        'category' => 14,
        'category_talk' => 15,
    ];
    /** @var array<int,bool> */
    protected $namespacesWithSubpages = [];
    /** @var array */
    protected $interwikiMap = [];
    /** @var int */
    private $maxDepth = 40;
    /** @var string|null */
    private $linkPrefixRegex = null;
    /**
     * @param array $opts
     */
    public function __construct( array $opts ) {
        parent::__construct();
        if ( isset( $opts['linting'] ) ) {
            $this->linterEnabled = $opts['linting'];
        }
        if ( isset( $opts['maxDepth'] ) ) {
            $this->maxDepth = $opts['maxDepth'];
        }
        $this->tidyWhitespaceBugMaxLength = $opts['tidyWhitespaceBugMaxLength'] ?? null;
        $this->linkPrefixRegex = $opts['linkPrefixRegex'] ?? null;
        $this->linkTrailRegex = $opts['linkTrailRegex'] ?? '/^([a-z]+)/sD'; // enwiki default
        // Use Monolog's PHP console handler
        $logger = new Logger( "Parsoid CLI" );
        $handler = new ErrorLogHandler();
        $handler->setFormatter( new LineFormatter( '%message%' ) );
        $logger->pushHandler( $handler );
        $this->setLogger( $logger );
    }
    public function tidyWhitespaceBugMaxLength(): int {
        return $this->tidyWhitespaceBugMaxLength ?? parent::tidyWhitespaceBugMaxLength();
    }
    public function allowedExternalImagePrefixes(): array {
        return [];
    }
    public function baseURI(): string {
        return '//my.wiki.example/wikix/';
    }
    /**
     * @inheritDoc
     */
    public function exportMetadataToHead(
        Document $document,
        ContentMetadataCollector $metadata,
        string $defaultTitle,
        string $lang
    ): void {
        '@phan-var StubMetadataCollector $metadata'; // @var StubMetadataCollector $metadata
        $moduleLoadURI = $this->server() . $this->scriptpath() . '/load.php';
        // Look for a displaytitle.
        $displayTitle = $metadata->getPageProperty( 'displaytitle' ) ??
            // Use the default title, properly escaped
            Utils::escapeHtml( $defaultTitle );
        $this->exportMetadataHelper(
            $document,
            $moduleLoadURI,
            $metadata->getModules(),
            $metadata->getModuleStyles(),
            $metadata->getJsConfigVars(),
            $displayTitle,
            $lang
        );
    }
    public function redirectRegexp(): string {
        return '/(?i:#REDIRECT)/';
    }
    public function categoryRegexp(): string {
        return '/Category/';
    }
    public function bswRegexp(): string {
        return '/' .
                'NOGLOBAL|DISAMBIG|NOCOLLABORATIONHUBTOC|nocollaborationhubtoc|NOTOC|notoc|' .
                'NOGALLERY|nogallery|FORCETOC|forcetoc|TOC|toc|NOEDITSECTION|noeditsection|' .
                'NOTITLECONVERT|notitleconvert|NOTC|notc|NOCONTENTCONVERT|nocontentconvert|' .
                'NOCC|nocc|NEWSECTIONLINK|NONEWSECTIONLINK|HIDDENCAT|INDEX|NOINDEX|STATICREDIRECT' .
            '/';
    }
    /** @inheritDoc */
    public function canonicalNamespaceId( string $name ): ?int {
        return $this->namespaceMap[$name] ?? null;
    }
    /** @inheritDoc */
    public function namespaceId( string $name ): ?int {
        $name = Utils::normalizeNamespaceName( $name );
        return $this->namespaceMap[$name] ?? null;
    }
    /** @inheritDoc */
    public function namespaceName( int $ns ): ?string {
        static $map = null;
        if ( $map === null ) {
            $map = array_flip( $this->namespaceMap );
        }
        if ( !isset( $map[$ns] ) ) {
            return null;
        }
        return ucwords( strtr( $map[$ns], '_', ' ' ) );
    }
    /** @inheritDoc */
    public function namespaceHasSubpages( int $ns ): bool {
        return !empty( $this->namespacesWithSubpages[$ns] );
    }
    /** @inheritDoc */
    public function namespaceCase( int $ns ): string {
        return 'first-letter';
    }
    /** @inheritDoc */
    public function specialPageLocalName( string $alias ): ?string {
        // @phan-suppress-previous-line PhanPluginNeverReturnMethod
        throw new \BadMethodCallException( 'Not implemented' );
    }
    /**
     * @param bool $val
     */
    public function setInterwikiMagic( bool $val ): void {
        $this->interwikiMagic = $val;
    }
    public function interwikiMagic(): bool {
        return $this->interwikiMagic;
    }
    public function interwikiMap(): array {
        return $this->interwikiMap;
    }
    public function iwp(): string {
        return 'mywiki';
    }
    public function legalTitleChars(): string {
        return ' %!"$&\'()*,\-.\/0-9:;=?@A-Z\\\\^_`a-z~\x80-\xFF+';
    }
    public function linkPrefixRegex(): ?string {
        return $this->linkPrefixRegex;
    }
    protected function linkTrail(): string {
        // @phan-suppress-previous-line PhanPluginNeverReturnMethod
        throw new \BadMethodCallException(
            'Should not be used. linkTrailRegex() is overridden here.' );
    }
    public function linkTrailRegex(): ?string {
        return $this->linkTrailRegex;
    }
    public function lang(): string {
        return 'en';
    }
    public function mainpage(): string {
        return 'Main Page';
    }
    public function responsiveReferences(): array {
        return [
            'enabled' => true,
            'threshold' => 10,
        ];
    }
    public function rtl(): bool {
        return false;
    }
    /** @inheritDoc */
    public function langConverterEnabled( string $lang ): bool {
        return $lang === 'sr';
    }
    public function script(): string {
        return '/wx/index.php';
    }
    public function scriptpath(): string {
        return '/wx';
    }
    public function server(): string {
        return '//my.wiki.example';
    }
    public function timezoneOffset(): int {
        return $this->timezoneOffset;
    }
    public function variants(): array {
        return [
            'sr' => [
                'base' => 'sr',
                'fallbacks' => [
                    'sr-ec'
                ]
            ],
            'sr-ec' => [
                'base' => 'sr',
                'fallbacks' => [
                    'sr'
                ]
            ],
            'sr-el' => [
                'base' => 'sr',
                'fallbacks' => [
                    'sr'
                ]
            ]
        ];
    }
    public function widthOption(): int {
        return 220;
    }
    /** @inheritDoc */
    protected function getVariableIDs(): array {
        return []; // None for now
    }
    /** @inheritDoc */
    protected function haveComputedFunctionSynonyms(): bool {
        return false;
    }
    /** @inheritDoc */
    protected function updateFunctionSynonym( string $func, string $magicword, bool $caseSensitive ): void {
        /* Nothing for now. Look at src/Config/Api/SiteConfig when mocking is needed. */
    }
    /** @inheritDoc */
    protected function getMagicWords(): array {
        return [
            'toc'             => [ 0, '__TOC__' ],
            'img_thumbnail'   => [ 1, 'thumb' ],
            'img_framed'      => [ 1, 'frame', 'framed' ],
            'img_frameless'   => [ 1, 'frameless' ],
            'img_manualthumb' => [ 1, 'thumbnail=$1', 'thumb=$1' ],
            'img_none'        => [ 1, 'none' ],
            'notoc'           => [ 0, '__NOTOC__' ],
        ];
    }
    /** @inheritDoc */
    public function getMagicWordMatcher( string $id ): string {
        if ( $id === 'toc' ) {
            return '/^TOC$/';
        } else {
            return '/(?!)/';
        }
    }
    /** @inheritDoc */
    public function getParameterizedAliasMatcher( array $words ): callable {
        $paramMWs = [
            'img_lossy' => "/^(?:(?i:lossy\=(.*?)))$/uS",
            'timedmedia_thumbtime' => "/^(?:(?i:thumbtime\=(.*?)))$/uS",
            'timedmedia_starttime' => "/^(?:(?i:start\=(.*?)))$/uS",
            'timedmedia_endtime' => "/^(?:(?i:end\=(.*?)))$/uS",
            'timedmedia_disablecontrols' => "/^(?:(?i:disablecontrols\=(.*?)))$/uS",
            'img_manualthumb' => "/^(?:(?:thumbnail\=(.*?)|thumb\=(.*?)))$/uS",
            'img_width' => "/^(?:(?:(.*?)px))$/uS",
            'img_lang' => "/^(?:(?:lang\=(.*?)))$/uS",
            'img_page' => "/^(?:(?:page\=(.*?)|page (.*?)))$/uS",
            'img_upright' => "/^(?:(?:upright\=(.*?)|upright (.*?)))$/uS",
            'img_link' => "/^(?:(?:link\=(.*?)))$/uS",
            'img_alt' => "/^(?:(?:alt\=(.*?)))$/uS",
            'img_class' => "/^(?:(?:class\=(.*?)))$/uS"
        ];
        $regexes = array_intersect_key( $paramMWs, array_flip( $words ) );
        return static function ( $text ) use ( $regexes ) {
            /**
             * $name is the canonical magic word name
             * $re has patterns for matching aliases
             */
            foreach ( $regexes as $name => $re ) {
                if ( preg_match( $re, $text, $m ) ) {
                    unset( $m[0] );
                    // Ex. regexp here is, /^(?:(?:|vinculo\=(.*?)|enlace\=(.*?)|link\=(.*?)))$/uS
                    // Check all the capture groups for a value, if not, it's safe to return an
                    // empty string since we did get a match.
                    foreach ( $m as $v ) {
                        if ( $v !== '' ) {
                            return [ 'k' => $name, 'v' => $v ];
                        }
                    }
                    return [ 'k' => $name, 'v' => '' ];
                }
            }
            return null;
        };
    }
    /** @inheritDoc */
    protected function getNonNativeExtensionTags(): array {
        return [
            'indicator' => true,
            'timeline' => true,
            'hiero' => true,
            'charinsert' => true,
            'inputbox' => true,
            'source' => true,
            'syntaxhighlight' => true,
            'section' => true,
            'score' => true,
            'templatedata' => true,
            'math' => true,
            'ce' => true,
            'chem' => true,
            'graph' => true,
            'maplink' => true,
            'categorytree' => true,
            'templatestyles' => true
        ];
    }
    /** @inheritDoc */
    public function getMaxTemplateDepth(): int {
        return $this->maxDepth;
    }
    /** @inheritDoc */
    protected function getSpecialPageAliases( string $specialPage ): array {
        if ( $specialPage === 'Booksources' ) {
            return [ 'Booksources', 'BookSources' ]; // Mock value
        } else {
            throw new \BadMethodCallException( 'Not implemented' );
        }
    }
    /** @inheritDoc */
    protected function getSpecialNSAliases(): array {
        return [ "Special", "special" ]; // Mock value
    }
    /** @inheritDoc */
    protected function getProtocols(): array {
        return [ "http:", "https:", "irc:", "ircs:", "news:", "ftp:", "mailto:", "gopher:", "//" ];
    }
    public function fakeTimestamp(): ?int {
        return $this->fakeTimestamp;
    }
    /**
     * Set the fake timestamp for testing
     * @param ?int $ts Unix timestamp
     */
    public function setFakeTimestamp( ?int $ts ): void {
        $this->fakeTimestamp = $ts;
    }
    /**
     * Set the timezone offset for testing
     * @param int $offset Offset from UTC
     */
    public function setTimezoneOffset( int $offset ): void {
        $this->timezoneOffset = $offset;
    }
    public function scrubBidiChars(): bool {
        return true;
    }
}