* Add a void return hint to methods that are not meant to return
anything. This helps catch accidental return statements in the
future, lets Phan better understand how methods are meant to be
used, and might also allow PHP to better optimise the compiled
code form (speculation).
I did not, however, add it to publicly extended methods as that
might mess with strict mode.
* Remove the internal getResourceLoader() method from MessageBlobStore.
This has been redundant since 3edaa0b37c.
The method was protected, and not considered stable to subclass
for extensions. Hence not a breaking change.
* Add Throwable typehint to formatException() and friends.
This is the narrowest one I could add given that the methods
called from here already enforce the same typehint.
Update the doc to match for now. There isn't a reason right now
to limit this only to Exception, and given this is the method
and not the catch statement itself, does not change behaviour.
* Remove unused ResourceLoader->getHookContainer().
Added within 1.35 cycle, safe to remove.
* Remove unexpected `@since` for `@internal` getHookRunner().
* Remove redundant `@internal` from ResourceLoaderMwUrlModule
methods, which itself is already `@internal`.
Change-Id: I68d33ff6feca7ef95282a7ff03eb9332adfde31c
313 lines
7.4 KiB
PHP
313 lines
7.4 KiB
PHP
<?php
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group ResourceLoader
|
|
*/
|
|
class ResourceLoaderImageModuleTest extends ResourceLoaderTestCase {
|
|
|
|
public static $commonImageData = [
|
|
'abc' => 'abc.gif',
|
|
'def' => [
|
|
'file' => 'def.svg',
|
|
'variants' => [ 'destructive' ],
|
|
],
|
|
'ghi' => [
|
|
'file' => [
|
|
'ltr' => 'ghi.svg',
|
|
'rtl' => 'jkl.svg'
|
|
],
|
|
],
|
|
'mno' => [
|
|
'file' => [
|
|
'ltr' => 'mno-ltr.svg',
|
|
'rtl' => 'mno-rtl.svg',
|
|
'lang' => [
|
|
'he' => 'mno-ltr.svg',
|
|
]
|
|
],
|
|
],
|
|
'pqr' => [
|
|
'file' => [
|
|
'default' => 'pqr-a.svg',
|
|
'lang' => [
|
|
'en' => 'pqr-b.svg',
|
|
'ar,de' => 'pqr-f.svg',
|
|
]
|
|
],
|
|
]
|
|
];
|
|
|
|
public static $commonImageVariants = [
|
|
'invert' => [
|
|
'color' => '#FFFFFF',
|
|
'global' => true,
|
|
],
|
|
'primary' => [
|
|
'color' => '#598AD1',
|
|
],
|
|
'constructive' => [
|
|
'color' => '#00C697',
|
|
],
|
|
'destructive' => [
|
|
'color' => '#E81915',
|
|
],
|
|
];
|
|
|
|
public static function providerGetModules() {
|
|
return [
|
|
[
|
|
[
|
|
'class' => ResourceLoaderImageModule::class,
|
|
'prefix' => 'oo-ui-icon',
|
|
'variants' => self::$commonImageVariants,
|
|
'images' => self::$commonImageData,
|
|
],
|
|
'.oo-ui-icon-abc {
|
|
...
|
|
}
|
|
.oo-ui-icon-abc-invert {
|
|
...
|
|
}
|
|
.oo-ui-icon-def {
|
|
...
|
|
}
|
|
.oo-ui-icon-def-invert {
|
|
...
|
|
}
|
|
.oo-ui-icon-def-destructive {
|
|
...
|
|
}
|
|
.oo-ui-icon-ghi {
|
|
...
|
|
}
|
|
.oo-ui-icon-ghi-invert {
|
|
...
|
|
}
|
|
.oo-ui-icon-mno {
|
|
...
|
|
}
|
|
.oo-ui-icon-mno-invert {
|
|
...
|
|
}
|
|
.oo-ui-icon-pqr {
|
|
...
|
|
}
|
|
.oo-ui-icon-pqr-invert {
|
|
...
|
|
}',
|
|
],
|
|
[
|
|
[
|
|
'class' => ResourceLoaderImageModule::class,
|
|
'selectorWithoutVariant' => '.mw-ui-icon-{name}:after, .mw-ui-icon-{name}:before',
|
|
'selectorWithVariant' =>
|
|
'.mw-ui-icon-{name}-{variant}:after, .mw-ui-icon-{name}-{variant}:before',
|
|
'variants' => self::$commonImageVariants,
|
|
'images' => self::$commonImageData,
|
|
],
|
|
'.mw-ui-icon-abc:after, .mw-ui-icon-abc:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-abc-invert:after, .mw-ui-icon-abc-invert:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-def:after, .mw-ui-icon-def:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-def-invert:after, .mw-ui-icon-def-invert:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-def-destructive:after, .mw-ui-icon-def-destructive:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-ghi:after, .mw-ui-icon-ghi:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-ghi-invert:after, .mw-ui-icon-ghi-invert:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-mno:after, .mw-ui-icon-mno:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-mno-invert:after, .mw-ui-icon-mno-invert:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-pqr:after, .mw-ui-icon-pqr:before {
|
|
...
|
|
}
|
|
.mw-ui-icon-pqr-invert:after, .mw-ui-icon-pqr-invert:before {
|
|
...
|
|
}',
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test reading files from elsewhere than localBasePath using ResourceLoaderFilePath.
|
|
*
|
|
* This mimics modules modified by skins using 'ResourceModuleSkinStyles' and 'OOUIThemePaths'
|
|
* skin attributes.
|
|
*
|
|
* @covers ResourceLoaderFilePath::getLocalBasePath
|
|
* @covers ResourceLoaderFilePath::getRemoteBasePath
|
|
*/
|
|
public function testResourceLoaderFilePath() {
|
|
$basePath = __DIR__ . '/../../data/blahblah';
|
|
$filePath = __DIR__ . '/../../data/rlfilepath';
|
|
$testModule = new ResourceLoaderImageModule( [
|
|
'localBasePath' => $basePath,
|
|
'remoteBasePath' => 'blahblah',
|
|
'prefix' => 'foo',
|
|
'images' => [
|
|
'eye' => new ResourceLoaderFilePath( 'eye.svg', $filePath, 'rlfilepath' ),
|
|
'flag' => [
|
|
'file' => [
|
|
'ltr' => new ResourceLoaderFilePath( 'flag-ltr.svg', $filePath, 'rlfilepath' ),
|
|
'rtl' => new ResourceLoaderFilePath( 'flag-rtl.svg', $filePath, 'rlfilepath' ),
|
|
],
|
|
],
|
|
],
|
|
] );
|
|
$expectedModule = new ResourceLoaderImageModule( [
|
|
'localBasePath' => $filePath,
|
|
'remoteBasePath' => 'rlfilepath',
|
|
'prefix' => 'foo',
|
|
'images' => [
|
|
'eye' => 'eye.svg',
|
|
'flag' => [
|
|
'file' => [
|
|
'ltr' => 'flag-ltr.svg',
|
|
'rtl' => 'flag-rtl.svg',
|
|
],
|
|
],
|
|
],
|
|
] );
|
|
|
|
$context = $this->getResourceLoaderContext();
|
|
$this->assertEquals(
|
|
$expectedModule->getModuleContent( $context ),
|
|
$testModule->getModuleContent( $context ),
|
|
"Using ResourceLoaderFilePath works correctly"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider providerGetModules
|
|
* @covers ResourceLoaderImageModule::getStyles
|
|
*/
|
|
public function testGetStyles( $module, $expected ) {
|
|
$module = new ResourceLoaderImageModuleTestable(
|
|
$module,
|
|
__DIR__ . '/../../data/resourceloader'
|
|
);
|
|
$styles = $module->getStyles( $this->getResourceLoaderContext() );
|
|
$this->assertEquals( $expected, $styles['all'] );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoaderContext::getImageObj
|
|
*/
|
|
public function testContext() {
|
|
$context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
|
|
$this->assertFalse( $context->getImageObj(), 'Missing image parameter' );
|
|
|
|
$context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
|
|
'image' => 'example',
|
|
] ) );
|
|
$this->assertFalse( $context->getImageObj(), 'Missing module parameter' );
|
|
|
|
$context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
|
|
'modules' => 'unknown',
|
|
'image' => 'example',
|
|
] ) );
|
|
$this->assertFalse( $context->getImageObj(), 'Not an image module' );
|
|
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', [
|
|
'class' => ResourceLoaderImageModule::class,
|
|
'prefix' => 'test',
|
|
'images' => [ 'example' => 'example.png' ],
|
|
] );
|
|
$context = new ResourceLoaderContext( $rl, new FauxRequest( [
|
|
'modules' => 'test',
|
|
'image' => 'unknown',
|
|
] ) );
|
|
$this->assertFalse( $context->getImageObj(), 'Unknown image' );
|
|
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', [
|
|
'class' => ResourceLoaderImageModule::class,
|
|
'prefix' => 'test',
|
|
'images' => [ 'example' => 'example.png' ],
|
|
] );
|
|
$context = new ResourceLoaderContext( $rl, new FauxRequest( [
|
|
'modules' => 'test',
|
|
'image' => 'example',
|
|
] ) );
|
|
$this->assertInstanceOf( ResourceLoaderImage::class, $context->getImageObj() );
|
|
}
|
|
|
|
public static function providerGetStyleDeclarations() {
|
|
return [
|
|
[
|
|
false,
|
|
<<<TEXT
|
|
background-image: url(rasterized.png);
|
|
background-image: linear-gradient(transparent, transparent), url(original.svg);
|
|
TEXT
|
|
],
|
|
[
|
|
'data:image/svg+xml',
|
|
<<<TEXT
|
|
background-image: url(rasterized.png);
|
|
background-image: linear-gradient(transparent, transparent), url(data:image/svg+xml);
|
|
TEXT
|
|
],
|
|
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider providerGetStyleDeclarations
|
|
* @covers ResourceLoaderImageModule::getStyleDeclarations
|
|
*/
|
|
public function testGetStyleDeclarations( $dataUriReturnValue, $expected ) {
|
|
$module = TestingAccessWrapper::newFromObject( new ResourceLoaderImageModule() );
|
|
$context = $this->getResourceLoaderContext();
|
|
$image = $this->getImageMock( $context, $dataUriReturnValue );
|
|
|
|
$styles = $module->getStyleDeclarations(
|
|
$context,
|
|
$image,
|
|
'load.php'
|
|
);
|
|
|
|
$this->assertEquals( $expected, $styles );
|
|
}
|
|
|
|
private function getImageMock( ResourceLoaderContext $context, $dataUriReturnValue ) {
|
|
$image = $this->getMockBuilder( ResourceLoaderImage::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$image->method( 'getDataUri' )
|
|
->willReturn( $dataUriReturnValue );
|
|
$image->method( 'getUrl' )
|
|
->will( $this->returnValueMap( [
|
|
[ $context, 'load.php', null, 'original', 'original.svg' ],
|
|
[ $context, 'load.php', null, 'rasterized', 'rasterized.png' ],
|
|
] ) );
|
|
|
|
return $image;
|
|
}
|
|
}
|
|
|
|
class ResourceLoaderImageModuleTestable extends ResourceLoaderImageModule {
|
|
/**
|
|
* Replace with a stub to make test cases easier to write.
|
|
*/
|
|
protected function getCssDeclarations( $primary, $fallback ) : array {
|
|
return [ '...' ];
|
|
}
|
|
}
|