mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-12-03 19:57:19 +00:00
475 lines
14 KiB
PHP
475 lines
14 KiB
PHP
<?php
|
|
namespace MediaWiki\Skins\Vector\Tests\Integration;
|
|
|
|
use Exception;
|
|
use HashConfig;
|
|
use MediaWiki\MediaWikiServices;
|
|
use MediaWiki\Skins\Vector\SkinVector22;
|
|
use MediaWiki\Skins\Vector\SkinVectorLegacy;
|
|
use MediaWikiIntegrationTestCase;
|
|
use ReflectionMethod;
|
|
use RequestContext;
|
|
use Title;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* Class VectorTemplateTest
|
|
* @package MediaWiki\Skins\Vector\Tests\Unit
|
|
* @group Vector
|
|
* @group Skins
|
|
*/
|
|
class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|
|
|
/**
|
|
* @return SkinVectorLegacy
|
|
*/
|
|
private function provideVectorTemplateObject() {
|
|
$skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
|
|
$template = $skinFactory->makeSkin( 'vector' );
|
|
return $template;
|
|
}
|
|
|
|
/**
|
|
* @param string $nodeString an HTML of the node we want to verify
|
|
* @param string $tag Tag of the element we want to check
|
|
* @param string $attribute Attribute of the element we want to check
|
|
* @param string $search Value of the attribute we want to verify
|
|
* @return bool
|
|
*/
|
|
private function expectNodeAttribute( $nodeString, $tag, $attribute, $search ) {
|
|
$node = new \DOMDocument();
|
|
$node->loadHTML( $nodeString );
|
|
$element = $node->getElementsByTagName( $tag )->item( 0 );
|
|
if ( !$element ) {
|
|
return false;
|
|
}
|
|
|
|
$values = explode( ' ', $element->getAttribute( $attribute ) );
|
|
return in_array( $search, $values );
|
|
}
|
|
|
|
public function provideGetTocData() {
|
|
$config = [
|
|
'VectorTableOfContentsBeginning' => true,
|
|
'VectorTableOfContentsCollapseAtCount' => 1
|
|
];
|
|
$tocData = [
|
|
'number-section-count' => 2,
|
|
'array-sections' => [
|
|
[
|
|
'toclevel' => 1,
|
|
'level' => '2',
|
|
'line' => 'A',
|
|
'number' => '1',
|
|
'index' => '1',
|
|
'fromtitle' => 'Test',
|
|
'byteoffset' => 231,
|
|
'anchor' => 'A',
|
|
'array-sections' => [],
|
|
'is-top-level-section' => true,
|
|
'is-parent-section' => false,
|
|
],
|
|
[
|
|
'toclevel' => 1,
|
|
'level' => '4',
|
|
'line' => 'B',
|
|
'number' => '2',
|
|
'index' => '2',
|
|
'fromtitle' => 'Test',
|
|
'byteoffset' => 245,
|
|
'anchor' => 'B',
|
|
'array-sections' => [],
|
|
'is-top-level-section' => true,
|
|
'is-parent-section' => false,
|
|
]
|
|
]
|
|
];
|
|
$nestedTocData = [
|
|
'number-section-count' => 2,
|
|
'array-sections' => [
|
|
[
|
|
'toclevel' => 1,
|
|
'level' => '2',
|
|
'line' => 'A',
|
|
'number' => '1',
|
|
'index' => '1',
|
|
'fromtitle' => 'Test',
|
|
'byteoffset' => 231,
|
|
'anchor' => 'A',
|
|
'array-sections' => [
|
|
'toclevel' => 2,
|
|
'level' => '4',
|
|
'line' => 'A1',
|
|
'number' => '1.1',
|
|
'index' => '2',
|
|
'fromtitle' => 'Test',
|
|
'byteoffset' => 245,
|
|
'anchor' => 'A1',
|
|
'array-sections' => [],
|
|
'is-top-level-section' => false,
|
|
'is-parent-section' => false,
|
|
],
|
|
'is-top-level-section' => true,
|
|
'is-parent-section' => true,
|
|
],
|
|
]
|
|
];
|
|
|
|
$expectedConfigData = [
|
|
'is-vector-toc-beginning-enabled' => $config[ 'VectorTableOfContentsBeginning' ],
|
|
'vector-is-collapse-sections-enabled' =>
|
|
$tocData[ 'number-section-count' ] >= $config[ 'VectorTableOfContentsCollapseAtCount' ]
|
|
];
|
|
$expectedNestedTocData = array_merge( $nestedTocData, $expectedConfigData );
|
|
$context = RequestContext::getMain();
|
|
$buttonLabel = $context->msg( 'vector-toc-toggle-button-label',
|
|
$expectedNestedTocData[ 'array-sections' ][ 0 ][ 'line' ]
|
|
)->text();
|
|
$expectedNestedTocData[ 'array-sections' ][ 0 ][ 'vector-button-label' ] = $buttonLabel;
|
|
|
|
return [
|
|
// When zero sections
|
|
[
|
|
[],
|
|
$config,
|
|
// TOC data is empty when given an empty array
|
|
[]
|
|
],
|
|
// When number of multiple sections is lower than configured value
|
|
[
|
|
$tocData,
|
|
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 3 ] ),
|
|
// 'vector-is-collapse-sections-enabled' value is false
|
|
array_merge( $tocData, $expectedConfigData, [
|
|
'vector-is-collapse-sections-enabled' => false
|
|
] )
|
|
],
|
|
// When number of multiple sections is equal to the configured value
|
|
[
|
|
$tocData,
|
|
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 2 ] ),
|
|
// 'vector-is-collapse-sections-enabled' value is true
|
|
array_merge( $tocData, $expectedConfigData )
|
|
],
|
|
// When number of multiple sections is higher than configured value
|
|
[
|
|
$tocData,
|
|
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 1 ] ),
|
|
// 'vector-is-collapse-sections-enabled' value is true
|
|
array_merge( $tocData, $expectedConfigData )
|
|
],
|
|
// When "Beginning" TOC section is configured to be turned off
|
|
[
|
|
$tocData,
|
|
array_merge( $config, [ 'VectorTableOfContentsBeginning' => false ] ),
|
|
// 'is-vector-toc-beginning-enabled' value is false
|
|
array_merge( $tocData, $expectedConfigData, [
|
|
'is-vector-toc-beginning-enabled' => false
|
|
] )
|
|
],
|
|
// When TOC has sections with top level parent sections
|
|
[
|
|
$nestedTocData,
|
|
$config,
|
|
// 'vector-button-label' is provided for top level parent sections
|
|
$expectedNestedTocData
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Skins\Vector\SkinVector22::getTocData
|
|
* @dataProvider provideGetTOCData
|
|
*/
|
|
public function testGetTocData(
|
|
array $tocData,
|
|
array $config,
|
|
array $expected
|
|
) {
|
|
$this->setMwGlobals( [
|
|
'wgVectorTableOfContentsCollapseAtCount' => $config['VectorTableOfContentsCollapseAtCount'],
|
|
'wgVectorTableOfContentsBeginning' => $config['VectorTableOfContentsBeginning'],
|
|
] );
|
|
|
|
$skinVector = new SkinVector22( [ 'name' => 'vector-2022' ] );
|
|
$openSkinVector = TestingAccessWrapper::newFromObject( $skinVector );
|
|
$data = $openSkinVector->getTocData( $tocData );
|
|
$this->assertEquals( $expected, $data );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Skins\Vector\SkinVector::getTemplateData
|
|
*/
|
|
public function testGetTemplateData() {
|
|
$title = Title::newFromText( 'SkinVector' );
|
|
$context = RequestContext::getMain();
|
|
$context->setTitle( $title );
|
|
$context->setLanguage( 'fr' );
|
|
$vectorTemplate = $this->provideVectorTemplateObject();
|
|
$this->setTemporaryHook( 'PersonalUrls', [
|
|
static function ( &$personal_urls, &$title, $skin ) {
|
|
$personal_urls = [
|
|
'pt-1' => [ 'text' => 'pt1' ],
|
|
];
|
|
}
|
|
] );
|
|
$this->setTemporaryHook( 'SkinTemplateNavigation::Universal', [
|
|
static function ( &$skinTemplate, &$content_navigation ) {
|
|
$content_navigation['actions'] = [
|
|
'action-1' => []
|
|
];
|
|
$content_navigation['namespaces'] = [
|
|
'ns-1' => []
|
|
];
|
|
$content_navigation['variants'] = [
|
|
[
|
|
'class' => 'selected',
|
|
'text' => 'Language variant',
|
|
'href' => '/url/to/variant',
|
|
'lang' => 'zh-hant',
|
|
'hreflang' => 'zh-hant',
|
|
]
|
|
];
|
|
$content_navigation['views'] = [];
|
|
}
|
|
] );
|
|
$openVectorTemplate = TestingAccessWrapper::newFromObject( $vectorTemplate );
|
|
|
|
$props = $openVectorTemplate->getTemplateData()['data-portlets'];
|
|
$views = $props['data-views'];
|
|
$namespaces = $props['data-namespaces'];
|
|
|
|
// The mediawiki core specification might change at any time
|
|
// so let's limit the values we test to those we are aware of.
|
|
$keysToTest = [
|
|
'id', 'class', 'html-tooltip', 'html-items',
|
|
'html-after-portal', 'html-before-portal',
|
|
'label', 'heading-class', 'is-dropdown'
|
|
];
|
|
foreach ( $views as $key => $value ) {
|
|
if ( !in_array( $key, $keysToTest ) ) {
|
|
unset( $views[ $key] );
|
|
}
|
|
}
|
|
$this->assertSame(
|
|
[
|
|
// Provided by core
|
|
'id' => 'p-views',
|
|
'class' => 'mw-portlet mw-portlet-views emptyPortlet vector-menu vector-menu-tabs',
|
|
'html-tooltip' => '',
|
|
'html-items' => '',
|
|
'html-after-portal' => '',
|
|
'html-before-portal' => '',
|
|
'label' => $context->msg( 'views' )->text(),
|
|
'heading-class' => '',
|
|
'is-dropdown' => false,
|
|
],
|
|
$views
|
|
);
|
|
|
|
$variants = $props['data-variants'];
|
|
$actions = $props['data-actions'];
|
|
$this->assertSame(
|
|
'mw-portlet mw-portlet-namespaces vector-menu vector-menu-tabs',
|
|
$namespaces['class']
|
|
);
|
|
$this->assertSame(
|
|
'mw-portlet mw-portlet-variants vector-menu-dropdown-noicon vector-menu vector-menu-dropdown',
|
|
$variants['class']
|
|
);
|
|
$this->assertSame(
|
|
'mw-portlet mw-portlet-cactions vector-menu-dropdown-noicon vector-menu vector-menu-dropdown',
|
|
$actions['class']
|
|
);
|
|
$this->assertSame(
|
|
'mw-portlet mw-portlet-personal vector-user-menu-legacy vector-menu',
|
|
$props['data-personal']['class']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Standard config for Language Alert in Sidebar
|
|
* @return array
|
|
*/
|
|
private function enableLanguageAlertFeatureConfig(): array {
|
|
return [
|
|
'VectorLanguageInHeader' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
],
|
|
'VectorLanguageInMainPageHeader' => [
|
|
'logged_in' => false,
|
|
'logged_out' => false
|
|
],
|
|
'VectorLanguageAlertInSidebar' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
],
|
|
];
|
|
}
|
|
|
|
public function providerLanguageAlertRequirements() {
|
|
$testTitle = Title::makeTitle( NS_MAIN, 'Test' );
|
|
$testTitleMainPage = Title::makeTitle( NS_MAIN, 'MAIN_PAGE' );
|
|
return [
|
|
'When none of the requirements are present, do not show alert' => [
|
|
// Configuration requirements for language in header and alert in sidebar
|
|
[],
|
|
// Title instance
|
|
$testTitle,
|
|
// Cached languages
|
|
[],
|
|
// Is the language selector at the top of the content?
|
|
false,
|
|
// Should the language button be hidden?
|
|
false,
|
|
// Expected
|
|
false
|
|
],
|
|
'When the feature is enabled and languages should be hidden, do not show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitle,
|
|
[], true, true, false
|
|
],
|
|
'When the language alert feature is disabled, do not show alert' => [
|
|
[
|
|
'VectorLanguageInHeader' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
],
|
|
'VectorLanguageAlertInSidebar' => [
|
|
'logged_in' => false,
|
|
'logged_out' => false
|
|
]
|
|
],
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], true, false, false
|
|
],
|
|
'When the language in header feature is disabled, do not show alert' => [
|
|
[
|
|
'VectorLanguageInHeader' => [
|
|
'logged_in' => false,
|
|
'logged_out' => false
|
|
],
|
|
'VectorLanguageAlertInSidebar' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
]
|
|
],
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], true, false, false
|
|
],
|
|
'When it is a main page, feature is enabled, and there are no languages, do not show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitleMainPage,
|
|
[], true, true, false
|
|
],
|
|
'When it is a non-main page, feature is enabled, and there are no languages, do not show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitle,
|
|
[], true, true, false
|
|
],
|
|
'When it is a main page, header feature is disabled, and there are languages, do not show alert' => [
|
|
[
|
|
'VectorLanguageInHeader' => [
|
|
'logged_in' => false,
|
|
'logged_out' => false
|
|
],
|
|
'VectorLanguageAlertInSidebar' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
]
|
|
],
|
|
$testTitleMainPage,
|
|
[ 'fr', 'en', 'ko' ], true, true, false
|
|
],
|
|
'When it is a non-main page, alert feature is disabled, there are languages, do not show alert' => [
|
|
[
|
|
'VectorLanguageInHeader' => [
|
|
'logged_in' => true,
|
|
'logged_out' => true
|
|
],
|
|
'VectorLanguageAlertInSidebar' => [
|
|
'logged_in' => false,
|
|
'logged_out' => false
|
|
]
|
|
],
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], true, true, false
|
|
],
|
|
'When most requirements are present but languages are not at the top, do not show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], false, false, false
|
|
],
|
|
'When most requirements are present but languages should be hidden, do not show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], true, true, false
|
|
],
|
|
'When it is a main page, features are enabled, and there are languages, show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitleMainPage,
|
|
[ 'fr', 'en', 'ko' ], true, false, true
|
|
],
|
|
'When all the requirements are present on a non-main page, show alert' => [
|
|
$this->enableLanguageAlertFeatureConfig(),
|
|
$testTitle,
|
|
[ 'fr', 'en', 'ko' ], true, false, true
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider providerLanguageAlertRequirements
|
|
* @covers \MediaWiki\Skins\Vector\SkinVector::shouldLanguageAlertBeInSidebar
|
|
* @param array $requirements
|
|
* @param Title $title
|
|
* @param array $getLanguagesCached
|
|
* @param bool $isLanguagesInContentAt
|
|
* @param bool $shouldHideLanguages
|
|
* @param bool $expected
|
|
* @throws Exception
|
|
*/
|
|
public function testShouldLanguageAlertBeInSidebar(
|
|
array $requirements,
|
|
Title $title,
|
|
array $getLanguagesCached,
|
|
bool $isLanguagesInContentAt,
|
|
bool $shouldHideLanguages,
|
|
bool $expected
|
|
) {
|
|
$config = new HashConfig( array_merge( $requirements, [
|
|
'DefaultSkin' => 'vector-2022',
|
|
'VectorDefaultSkinVersion' => '2',
|
|
'VectorSkinMigrationMode' => true,
|
|
] ) );
|
|
$this->installMockMwServices( $config );
|
|
|
|
$mockSkinVector = $this->getMockBuilder( SkinVector22::class )
|
|
->disableOriginalConstructor()
|
|
->onlyMethods( [ 'getTitle', 'getLanguagesCached','isLanguagesInContentAt', 'shouldHideLanguages' ] )
|
|
->getMock();
|
|
$mockSkinVector->method( 'getTitle' )
|
|
->willReturn( $title );
|
|
$mockSkinVector->method( 'getLanguagesCached' )
|
|
->willReturn( $getLanguagesCached );
|
|
$mockSkinVector->method( 'isLanguagesInContentAt' )->with( 'top' )
|
|
->willReturn( $isLanguagesInContentAt );
|
|
$mockSkinVector->method( 'shouldHideLanguages' )
|
|
->willReturn( $shouldHideLanguages );
|
|
|
|
$shouldLanguageAlertBeInSidebarMethod = new ReflectionMethod(
|
|
SkinVector22::class,
|
|
'shouldLanguageAlertBeInSidebar'
|
|
);
|
|
$shouldLanguageAlertBeInSidebarMethod->setAccessible( true );
|
|
|
|
$this->assertSame(
|
|
$expected,
|
|
$shouldLanguageAlertBeInSidebarMethod->invoke( $mockSkinVector )
|
|
);
|
|
}
|
|
|
|
}
|