From bf6d5fdd55cba5ab00146cb7efa78e76a87aa6b3 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:00:52 +0100 Subject: [PATCH 01/15] [!!!][TASK] Remove legacy TypoScript files --- Configuration/TypoScript/Base/constants.ts | 1 - Configuration/TypoScript/Base/setup.ts | 1 - 2 files changed, 2 deletions(-) delete mode 100644 Configuration/TypoScript/Base/constants.ts delete mode 100644 Configuration/TypoScript/Base/setup.ts diff --git a/Configuration/TypoScript/Base/constants.ts b/Configuration/TypoScript/Base/constants.ts deleted file mode 100644 index b634033..0000000 --- a/Configuration/TypoScript/Base/constants.ts +++ /dev/null @@ -1 +0,0 @@ -@import 'EXT:sms_responsive_images/Configuration/TypoScript/Base/constants.typoscript' diff --git a/Configuration/TypoScript/Base/setup.ts b/Configuration/TypoScript/Base/setup.ts deleted file mode 100644 index 1337be2..0000000 --- a/Configuration/TypoScript/Base/setup.ts +++ /dev/null @@ -1 +0,0 @@ -@import 'EXT:sms_responsive_images/Configuration/TypoScript/Base/setup.typoscript' From 7cee50e4e9ef046cab48616db7102f53d35322f7 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:01:26 +0100 Subject: [PATCH 02/15] [!!!][FEATURE] Drop support for TYPO3 v9, add support for TYPO3 v12.2 --- .github/workflows/tests.yml | 11 +- Classes/Utility/ResponsiveImagesUtility.php | 2 + Classes/ViewHelpers/ImageViewHelper.php | 110 ++++++++----- Classes/ViewHelpers/MediaViewHelper.php | 167 +++++++++++++++----- Documentation/Index.rst | 2 +- Documentation/Links.rst | 3 - Documentation/Settings.cfg | 4 +- README.md | 8 +- composer.json | 13 +- ext_emconf.php | 6 +- 10 files changed, 222 insertions(+), 104 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 14f9fd0..fa9a86a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,14 +40,13 @@ jobs: strategy: max-parallel: 2 matrix: - php-versions: [7.4, 7.3] - typo3-versions: [11, 10, 9] - exclude: - - php-versions: 7.3 - typo3-versions: 11 + php-versions: [8.2, 8.1, 8.0] + typo3-versions: [12, 11] include: - - php-versions: 8 + - php-versions: 7.4 typo3-versions: 11 + - php-versions: 7.4 + typo3-versions: 10 name: PHP ${{ matrix.php-versions }} with TYPO3 ${{ matrix.typo3-versions }} steps: diff --git a/Classes/Utility/ResponsiveImagesUtility.php b/Classes/Utility/ResponsiveImagesUtility.php index 8a19fd7..df77c8d 100644 --- a/Classes/Utility/ResponsiveImagesUtility.php +++ b/Classes/Utility/ResponsiveImagesUtility.php @@ -1,5 +1,7 @@ responsiveImagesUtility = $responsiveImagesUtility; + parent::__construct(); + $this->imageService = GeneralUtility::makeInstance(ImageService::class); + $this->responsiveImagesUtility = GeneralUtility::makeInstance(ResponsiveImagesUtility::class); } - /** - * Initialize arguments. - */ - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); + $this->registerUniversalTagAttributes(); + $this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false); + $this->registerTagAttribute('ismap', 'string', 'Specifies an image as a server-side image-map. Rarely used. Look at usemap instead', false); + $this->registerTagAttribute('longdesc', 'string', 'Specifies the URL to a document that contains a long description of an image', false); + $this->registerTagAttribute('usemap', 'string', 'Specifies an image as a client-side image-map', false); + $this->registerTagAttribute('loading', 'string', 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto"', false); + $this->registerTagAttribute('decoding', 'string', 'Provides an image decoding hint to the browser. Can be "sync", "async" or "auto"', false); + + $this->registerArgument('src', 'string', 'a path to a file, a combined FAL identifier or an uid (int). If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. If you already got a FAL object, consider using the $image parameter instead', false, ''); + $this->registerArgument('treatIdAsReference', 'bool', 'given src argument is a sys_file_reference record', false, false); + $this->registerArgument('image', 'object', 'a FAL object (\\TYPO3\\CMS\\Core\\Resource\\File or \\TYPO3\\CMS\\Core\\Resource\\FileReference)'); + $this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)'); + $this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default'); + $this->registerArgument('fileExtension', 'string', 'Custom file extension to use'); + + $this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.'); + $this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.'); + $this->registerArgument('minWidth', 'int', 'minimum width of the image'); + $this->registerArgument('minHeight', 'int', 'minimum height of the image'); + $this->registerArgument('maxWidth', 'int', 'maximum width of the image'); + $this->registerArgument('maxHeight', 'int', 'maximum height of the image'); + $this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false); + $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( 'sizes', @@ -60,20 +82,6 @@ public function initializeArguments() false, 'svg, gif' ); - - if (version_compare(TYPO3_version, '10.3', '<')) { - $this->registerArgument( - 'fileExtension', - 'string', - 'Custom file extension to use for images' - ); - - $this->registerArgument( - 'loading', - 'string', - 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto". Used on image files only.' - ); - } } /** @@ -84,7 +92,7 @@ public function initializeArguments() * @throws \TYPO3Fluid\Fluid\Core\Exception * @return string Rendered tag */ - public function render() + public function render(): string { $src = (string)$this->arguments['src']; if (($src === '' && is_null($this->arguments['image'])) @@ -104,12 +112,6 @@ public function render() ), 1631539412); // Original code: 1618989190 } - // Fall back to TYPO3 default if no responsive image feature was selected - // This also covers external image urls - if (!$this->arguments['breakpoints'] && !$this->arguments['srcset']) { - return parent::render(); - } - // Add loading attribute to tag if (in_array($this->arguments['loading'] ?? '', ['lazy', 'eager', 'auto'], true)) { $this->tag->addAttribute('loading', $this->arguments['loading']); @@ -120,7 +122,7 @@ public function render() $image = $this->imageService->getImage( $src, $this->arguments['image'], - $this->arguments['treatIdAsReference'] + (bool) $this->arguments['treatIdAsReference'] ); // Determine cropping settings @@ -132,7 +134,11 @@ public function render() $cropVariant = $this->arguments['cropVariant'] ?: 'default'; $cropArea = $cropVariantCollection->getCropArea($cropVariant); - $focusArea = $cropVariantCollection->getFocusArea($cropVariant); + + $focusArea = null; + if (!$this->tag->hasAttribute('data-focus-area')) { + $focusArea = $cropVariantCollection->getFocusArea($cropVariant); + } // Generate fallback image $processingInstructions = [ @@ -149,10 +155,10 @@ public function render() if (!is_null($this->arguments['maxWidth'])) { $processingInstructions['maxWidth'] = $this->arguments['maxWidth']; } - $fallbackImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); if ($this->arguments['breakpoints']) { // Generate picture tag + $fallbackImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); $this->tag = $this->responsiveImagesUtility->createPictureTag( $image, $fallbackImage, @@ -168,8 +174,9 @@ public function render() $this->arguments['placeholderInline'], $this->arguments['fileExtension'] ); - } else { + } elseif ($this->arguments['srcset']) { // Generate img tag with srcset + $fallbackImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); $this->tag = $this->responsiveImagesUtility->createImageTagWithSrcset( $image, $fallbackImage, @@ -185,15 +192,40 @@ public function render() $this->arguments['placeholderInline'], $this->arguments['fileExtension'] ); + } else { + // For simple images, height calculation is not a problem and is done the same way + // the core does it + $processingInstructions = array_merge($processingInstructions, [ + 'height' => $this->arguments['height'], + 'minHeight' => $this->arguments['minHeight'], + 'maxHeight' => $this->arguments['maxHeight'] + ]); + $fallbackImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); + + $this->tag = $this->responsiveImagesUtility->createSimpleImageTag( + $fallbackImage, + null, + $this->tag, + $focusArea, + $this->arguments['absolute'], + $this->arguments['lazyload'], + $this->arguments['placeholderSize'], + $this->arguments['placeholderInline'], + $this->arguments['fileExtension'] + ); } } catch (ResourceDoesNotExistException $e) { // thrown if file does not exist + throw new Exception($e->getMessage(), 1678270145, $e); // Original code: 1509741911 } catch (\UnexpectedValueException $e) { // thrown if a file has been replaced with a folder + throw new Exception($e->getMessage(), 1678270146, $e); // Original code: 1509741912 } catch (\RuntimeException $e) { // RuntimeException thrown if a file is outside of a storage + throw new Exception($e->getMessage(), 1678270147, $e); // Original code: 1509741913 } catch (\InvalidArgumentException $e) { // thrown if file storage does not exist + throw new Exception($e->getMessage(), 1678270148, $e); // Original code: 1509741914 } return $this->tag->render(); diff --git a/Classes/ViewHelpers/MediaViewHelper.php b/Classes/ViewHelpers/MediaViewHelper.php index 98e4b61..9325560 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -1,33 +1,46 @@ responsiveImagesUtility = $responsiveImagesUtility; + parent::__construct(); + $this->imageService = GeneralUtility::makeInstance(ImageService::class); + $this->responsiveImagesUtility = GeneralUtility::makeInstance(ResponsiveImagesUtility::class); } - /** - * Initialize arguments. - */ - public function initializeArguments() + + public function initializeArguments(): void { parent::initializeArguments(); + $this->registerUniversalTagAttributes(); + $this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false); + $this->registerArgument('file', 'object', 'File', true); + $this->registerArgument('additionalConfig', 'array', 'This array can hold additional configuration that is passed though to the Renderer object', false, []); + $this->registerArgument('width', 'string', 'This can be a numeric value representing the fixed width of in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.'); + $this->registerArgument('height', 'string', 'This can be a numeric value representing the fixed height in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.'); + $this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default'); + $this->registerArgument('fileExtension', 'string', 'Custom file extension to use for images'); + $this->registerArgument('loading', 'string', 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto". Used on image files only.'); + $this->registerArgument('decoding', 'string', 'Provides an image decoding hint to the browser. Can be "sync", "async" or "auto"', false); + $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( 'sizes', @@ -59,40 +72,110 @@ public function initializeArguments() false, 'svg, gif' ); + } + + /** + * Render a given media file. + * + * @throws \UnexpectedValueException + * @throws Exception + */ + public function render(): string + { + $file = $this->arguments['file']; + $additionalConfig = (array)$this->arguments['additionalConfig']; + $width = $this->arguments['width']; + $height = $this->arguments['height']; + + // get Resource Object (non ExtBase version) + if (is_callable([$file, 'getOriginalResource'])) { + // We have a domain model, so we need to fetch the FAL resource object from there + $file = $file->getOriginalResource(); + } - if (version_compare(TYPO3_version, '10.3', '<')) { - $this->registerArgument( - 'fileExtension', - 'string', - 'Custom file extension to use for images' + if (!$file instanceof FileInterface) { + throw new \UnexpectedValueException( + 'Supplied file object type ' . get_class($file) . ' must be FileInterface.', + 1678270961 // Original code: 1454252193 ); + } - $this->registerArgument( - 'loading', - 'string', - 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto". Used on image files only.' + if ((string)$this->arguments['fileExtension'] && !GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], (string)$this->arguments['fileExtension'])) { + throw new Exception( + 'The extension ' . $this->arguments['fileExtension'] . ' is not specified in $GLOBALS[\'TYPO3_CONF_VARS\'][\'GFX\'][\'imagefile_ext\']' + . ' as a valid image file extension and can not be processed.', + 1678270962 // Original code: 1619030957 ); } + + $fileRenderer = GeneralUtility::makeInstance(RendererRegistry::class)->getRenderer($file); + + // Fallback to image when no renderer is found + if ($fileRenderer === null) { + if ($this->arguments['breakpoints']) { + return $this->renderPicture($file, $width, $height, $this->arguments['fileExtension'] ?? null); + } elseif ($this->arguments['srcset']) { + return $this->renderImageSrcset($file, $width, $height, $this->arguments['fileExtension'] ?? null); + } else { + return $this->renderSimpleImage($file, $width, $height, $this->arguments['fileExtension'] ?? null); + } + } + $additionalConfig = array_merge_recursive($this->arguments, $additionalConfig); + return $fileRenderer->render($file, $width, $height, $additionalConfig); } /** - * Render img tag + * Render simple img tag * - * @param FileInterface $image - * @param string $width - * @param string $height - * @param string|null $fileExtension - * @return string Rendered img tag + * @param string $width + * @param string $height + * @return string Rendered img tag */ - protected function renderImage(FileInterface $image, $width, $height, ?string $fileExtension = null) + protected function renderSimpleImage(FileInterface $image, $width, $height, ?string $fileExtension): string { - if ($this->arguments['breakpoints']) { - return $this->renderPicture($image, $width, $height, $fileExtension); - } elseif ($this->arguments['srcset']) { - return $this->renderImageSrcset($image, $width, $height, $fileExtension); - } else { - return parent::renderImage($image, $width, $height, $fileExtension); + $cropVariant = $this->arguments['cropVariant'] ?: 'default'; + $cropString = $image instanceof FileReference ? $image->getProperty('crop') : ''; + $cropVariantCollection = CropVariantCollection::create((string)$cropString); + $cropArea = $cropVariantCollection->getCropArea($cropVariant); + $processingInstructions = [ + 'width' => $width, + 'height' => $height, + 'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image), + ]; + if (!empty($fileExtension)) { + $processingInstructions['fileExtension'] = $fileExtension; + } + $processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); + $imageUri = $this->imageService->getImageUri($processedImage); + + if (!$this->tag->hasAttribute('data-focus-area')) { + $focusArea = $cropVariantCollection->getFocusArea($cropVariant); + if (!$focusArea->isEmpty()) { + $this->tag->addAttribute('data-focus-area', $focusArea->makeAbsoluteBasedOnFile($image)); + } + } + $this->tag->addAttribute('src', $imageUri); + $this->tag->addAttribute('width', $processedImage->getProperty('width')); + $this->tag->addAttribute('height', $processedImage->getProperty('height')); + if (in_array($this->arguments['loading'] ?? '', ['lazy', 'eager', 'auto'], true)) { + $this->tag->addAttribute('loading', $this->arguments['loading']); + } + if (in_array($this->arguments['decoding'] ?? '', ['sync', 'async', 'auto'], true)) { + $this->tag->addAttribute('decoding', $this->arguments['decoding']); + } + + $alt = $image->getProperty('alternative'); + $title = $image->getProperty('title'); + + // The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty + if (empty($this->arguments['alt'])) { + $this->tag->addAttribute('alt', $alt); + } + if (empty($this->arguments['title']) && $title) { + $this->tag->addAttribute('title', $title); } + + return $this->tag->render(); } /** @@ -171,6 +254,9 @@ protected function renderImageSrcset(FileInterface $image, $width, $height, ?str if (in_array($this->arguments['loading'] ?? '', ['lazy', 'eager', 'auto'], true)) { $this->tag->addAttribute('loading', $this->arguments['loading']); } + if (in_array($this->arguments['decoding'] ?? '', ['sync', 'async', 'auto'], true)) { + $this->tag->addAttribute('decoding', $this->arguments['decoding']); + } // Generate image tag $this->tag = $this->responsiveImagesUtility->createImageTagWithSrcset( @@ -214,8 +300,7 @@ protected function generateFallbackImage( if (!empty($fileExtension)) { $processingInstructions['fileExtension'] = $fileExtension; } - $imageService = $this->getImageService(); - $fallbackImage = $imageService->applyProcessingInstructions($image, $processingInstructions); + $fallbackImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); return $fallbackImage; } diff --git a/Documentation/Index.rst b/Documentation/Index.rst index d432015..897be99 100644 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -30,7 +30,7 @@ SMS Responsive Images TYPO3 CMS,responsive images,srcset,sizes,picture,highdpi,retina,image,aspect ratio,cropping,lazyload,placeholder :Copyright: - 2017 + 2023 :Author: Simon Praetorius diff --git a/Documentation/Links.rst b/Documentation/Links.rst index 9710653..3402d53 100644 --- a/Documentation/Links.rst +++ b/Documentation/Links.rst @@ -19,6 +19,3 @@ Links :Git Repository: https://github.com/sitegeist/sms-responsive-images - -:Contact: - `@s2bproject `__, `@sitegeist_de `__ diff --git a/Documentation/Settings.cfg b/Documentation/Settings.cfg index fca34a5..6fb2be8 100644 --- a/Documentation/Settings.cfg +++ b/Documentation/Settings.cfg @@ -25,13 +25,13 @@ project = SMS Responsive Images # ... (recommended) version, displayed next to title (desktop) and in =0.0.1" - }, "autoload": { "psr-4": { "Sitegeist\\ResponsiveImages\\": "Classes/" @@ -34,7 +31,11 @@ }, "config": { "vendor-dir": ".Build/vendor", - "bin-dir": ".Build/bin" + "bin-dir": ".Build/bin", + "allow-plugins": { + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true + } }, "extra": { "typo3/cms": { diff --git a/ext_emconf.php b/ext_emconf.php index ddfbd81..9b5a30f 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -9,14 +9,12 @@ 'state' => 'stable', 'uploadfolder' => false, 'clearCacheOnLoad' => false, - 'version' => '2.1.3', + 'version' => '3.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '9.5.0-11.9.99', - 'php' => '7.2.0-7.9.99' + 'typo3' => '10.4.0-12.9.99', ], 'conflicts' => [ - 'fluid_styled_responsive_images' => '' ], 'suggests' => [ 'fluid_styled_content' => '' From ebb676eeba1becce4ec20235838a60ab55a58f20 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:17:03 +0100 Subject: [PATCH 03/15] [BUGFIX] Fix strict types --- Classes/Utility/ResponsiveImagesUtility.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/Utility/ResponsiveImagesUtility.php b/Classes/Utility/ResponsiveImagesUtility.php index df77c8d..b04f181 100644 --- a/Classes/Utility/ResponsiveImagesUtility.php +++ b/Classes/Utility/ResponsiveImagesUtility.php @@ -464,6 +464,7 @@ public function generateSrcsetImages( $images = []; foreach ($srcset as $widthDescriptor) { + $widthDescriptor = (string) $widthDescriptor; // Determine image width $srcsetMode = substr($widthDescriptor, -1); switch ($srcsetMode) { From 7a7c5e137250eb6fc31f10997aa7308de4198b3c Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:23:07 +0100 Subject: [PATCH 04/15] [TASK] Update testing setup --- Build/Testing/UnitTests.xml | 30 +++++++++++ Build/Testing/UnitTestsBootstrap.php | 75 ++++++++++++++++++++++++++++ composer.json | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 Build/Testing/UnitTests.xml create mode 100644 Build/Testing/UnitTestsBootstrap.php diff --git a/Build/Testing/UnitTests.xml b/Build/Testing/UnitTests.xml new file mode 100644 index 0000000..d71bb3f --- /dev/null +++ b/Build/Testing/UnitTests.xml @@ -0,0 +1,30 @@ + + + + ../../Tests/Unit/ + + + + + + + + diff --git a/Build/Testing/UnitTestsBootstrap.php b/Build/Testing/UnitTestsBootstrap.php new file mode 100644 index 0000000..b3d535e --- /dev/null +++ b/Build/Testing/UnitTestsBootstrap.php @@ -0,0 +1,75 @@ +getWebRoot(), '/')); + } + if (!getenv('TYPO3_PATH_WEB')) { + putenv('TYPO3_PATH_WEB=' . rtrim($testbase->getWebRoot(), '/')); + } + + $testbase->defineSitePath(); + + $requestType = \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE | \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI; + \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(0, $requestType); + + $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3conf/ext'); + $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/assets'); + $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/tests'); + $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/transient'); + + // Retrieve an instance of class loader and inject to core bootstrap + $classLoader = require $testbase->getPackagesPath() . '/autoload.php'; + \TYPO3\CMS\Core\Core\Bootstrap::initializeClassLoader($classLoader); + + // Initialize default TYPO3_CONF_VARS + $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager(); + $GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration(); + + $cache = new \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend( + 'core', + new \TYPO3\CMS\Core\Cache\Backend\NullBackend('production', []) + ); + + // Set all packages to active + if (interface_exists(\TYPO3\CMS\Core\Package\Cache\PackageCacheInterface::class)) { + $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::createPackageManager( + \TYPO3\CMS\Core\Package\UnitTestPackageManager::class, + \TYPO3\CMS\Core\Core\Bootstrap::createPackageCache($cache) + ); + } else { + // v10 compatibility layer + $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::createPackageManager( + \TYPO3\CMS\Core\Package\UnitTestPackageManager::class, + $cache + ); + } + + \TYPO3\CMS\Core\Utility\GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager); + \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::setPackageManager($packageManager); + + $testbase->dumpClassLoadingInformation(); + + \TYPO3\CMS\Core\Utility\GeneralUtility::purgeInstances(); +}); diff --git a/composer.json b/composer.json index 939ccd9..9c5e236 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "lint:php": "phpcs --standard=PSR2 --extensions=php --ignore=.Build,Tests,ext_emconf.php .", "lint:editorconfig": "ec .", - "test": "phpunit -c .Build/vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml Tests/", + "test": "phpunit -c Build/Testing/UnitTests.xml", "prepare-release": [ "rm -r .github .ecrc .editorconfig .gitattributes Tests" ], From 4dbc5e585c01b595f653025ae3635af4bc023f7f Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:26:50 +0100 Subject: [PATCH 05/15] [BUGFIX] Fix strict types --- Classes/Utility/ResponsiveImagesUtility.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Utility/ResponsiveImagesUtility.php b/Classes/Utility/ResponsiveImagesUtility.php index b04f181..d0757a2 100644 --- a/Classes/Utility/ResponsiveImagesUtility.php +++ b/Classes/Utility/ResponsiveImagesUtility.php @@ -130,7 +130,7 @@ public function createImageTagWithSrcset( $absoluteUri, $fileExtension ); - $srcsetMode = substr(key($srcsetImages), -1); // x or w + $srcsetMode = substr(key($srcsetImages) ?? 'w', -1); // x or w // Add fallback image to source options $fallbackWidthDescriptor = ($srcsetMode == 'x') ? '1x' : $referenceWidth . 'w'; @@ -307,7 +307,7 @@ public function createPictureSourceTag( $absoluteUri, $fileExtension ); - $srcsetMode = substr(key($srcsetImages), -1); // x or w + $srcsetMode = substr(key($srcsetImages) ?? 'w', -1); // x or w // Create source tag for this breakpoint $sourceTag = GeneralUtility::makeInstance(TagBuilder::class, 'source'); From dba9171b122f05f124c7f761a424866dc5d469b8 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 15:29:22 +0100 Subject: [PATCH 06/15] [TASK] Modernize test suite --- .github/workflows/tests.yml | 3 + Build/Testing/UnitTests.xml | 52 +++-- Classes/ViewHelpers/ImageViewHelper.php | 2 + Classes/ViewHelpers/MediaViewHelper.php | 25 ++- ...stractResponsiveImagesUtilityTestCase.php} | 34 +-- .../ResponsiveImagesUtility/HelpersTest.php | 76 +++---- .../ResponsiveImagesUtility/ImageTagTest.php | 197 ++++++++++-------- .../PictureSourceTagTest.php | 18 +- .../PictureTagTest.php | 95 +++++---- composer.json | 7 +- 10 files changed, 285 insertions(+), 224 deletions(-) rename Tests/Unit/Utility/ResponsiveImagesUtility/{AbstractResponsiveImagesUtilityTest.php => AbstractResponsiveImagesUtilityTestCase.php} (75%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa9a86a..20ad2cc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -42,6 +42,9 @@ jobs: matrix: php-versions: [8.2, 8.1, 8.0] typo3-versions: [12, 11] + exclude: + - php-versions: 8.0 + typo3-versions: 12 include: - php-versions: 7.4 typo3-versions: 11 diff --git a/Build/Testing/UnitTests.xml b/Build/Testing/UnitTests.xml index d71bb3f..18f450f 100644 --- a/Build/Testing/UnitTests.xml +++ b/Build/Testing/UnitTests.xml @@ -1,30 +1,28 @@ + - - - ../../Tests/Unit/ - - - - - - - + + + ../../Tests/Unit/ + + + + + + + diff --git a/Classes/ViewHelpers/ImageViewHelper.php b/Classes/ViewHelpers/ImageViewHelper.php index 1fb3840..01cf4fd 100644 --- a/Classes/ViewHelpers/ImageViewHelper.php +++ b/Classes/ViewHelpers/ImageViewHelper.php @@ -29,6 +29,7 @@ public function initializeArguments(): void { parent::initializeArguments(); $this->registerUniversalTagAttributes(); + // phpcs:disable Generic.Files.LineLength $this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false); $this->registerTagAttribute('ismap', 'string', 'Specifies an image as a server-side image-map. Rarely used. Look at usemap instead', false); $this->registerTagAttribute('longdesc', 'string', 'Specifies the URL to a document that contains a long description of an image', false); @@ -50,6 +51,7 @@ public function initializeArguments(): void $this->registerArgument('maxWidth', 'int', 'maximum width of the image'); $this->registerArgument('maxHeight', 'int', 'maximum height of the image'); $this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false); + // phpcs:enable $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( diff --git a/Classes/ViewHelpers/MediaViewHelper.php b/Classes/ViewHelpers/MediaViewHelper.php index 9325560..12d2f0e 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -31,6 +31,7 @@ public function initializeArguments(): void { parent::initializeArguments(); $this->registerUniversalTagAttributes(); + // phpcs:disable Generic.Files.LineLength $this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false); $this->registerArgument('file', 'object', 'File', true); $this->registerArgument('additionalConfig', 'array', 'This array can hold additional configuration that is passed though to the Renderer object', false, []); @@ -40,6 +41,7 @@ public function initializeArguments(): void $this->registerArgument('fileExtension', 'string', 'Custom file extension to use for images'); $this->registerArgument('loading', 'string', 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto". Used on image files only.'); $this->registerArgument('decoding', 'string', 'Provides an image decoding hint to the browser. Can be "sync", "async" or "auto"', false); + // phpcs:enable $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( @@ -100,12 +102,12 @@ public function render(): string ); } - if ((string)$this->arguments['fileExtension'] && !GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], (string)$this->arguments['fileExtension'])) { - throw new Exception( - 'The extension ' . $this->arguments['fileExtension'] . ' is not specified in $GLOBALS[\'TYPO3_CONF_VARS\'][\'GFX\'][\'imagefile_ext\']' - . ' as a valid image file extension and can not be processed.', - 1678270962 // Original code: 1619030957 - ); + if (!$this->isKnownFileExtension($this->arguments['fileExtension'])) { + throw new Exception(sprintf( + 'The extension %s is not specified in %s as a valid image file extension and can not be processed.', + $this->arguments['fileExtension'], + '$GLOBALS[\'TYPO3_CONF_VARS\'][\'GFX\'][\'imagefile_ext\']' + ), 1631539412); // Original code: 1619030957 } $fileRenderer = GeneralUtility::makeInstance(RendererRegistry::class)->getRenderer($file); @@ -304,4 +306,15 @@ protected function generateFallbackImage( return $fallbackImage; } + + protected function isKnownFileExtension($fileExtension): bool + { + $fileExtension = (string) $fileExtension; + // Skip if no file extension was specified + if ($fileExtension === '') { + return true; + } + // Check against list of supported extensions + return GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension); + } } diff --git a/Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTest.php b/Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTestCase.php similarity index 75% rename from Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTest.php rename to Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTestCase.php index 463e638..a74e510 100644 --- a/Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTest.php +++ b/Tests/Unit/Utility/ResponsiveImagesUtility/AbstractResponsiveImagesUtilityTestCase.php @@ -5,16 +5,16 @@ use Sitegeist\ResponsiveImages\Utility\ResponsiveImagesUtility; use TYPO3\CMS\Core\Resource\FileReference; use TYPO3\CMS\Core\Resource\ProcessedFile; -use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Extbase\Service\ImageService; -abstract class AbstractResponsiveImagesUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase +abstract class AbstractResponsiveImagesUtilityTestCase extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { - protected $resetSingletonInstances = true; + protected ResponsiveImagesUtility $utility; protected function setUp(): void { parent::setUp(); + $this->resetSingletonInstances = true; $this->utility = new ResponsiveImagesUtility( $this->mockImageService() @@ -27,7 +27,7 @@ protected function mockImageService() $imageServiceMock = $this->getMockBuilder(ImageService::class) ->disableOriginalConstructor() - ->setMethods(['applyProcessingInstructions', 'getImageUri']) + ->onlyMethods(['applyProcessingInstructions', 'getImageUri']) ->getMock(); $imageServiceMock @@ -70,10 +70,23 @@ protected function mockFileObject($properties, $processed = false) ]; $properties = array_replace($defaultProperties, $properties); - $fileMock = $this->getMockBuilder($processed ? ProcessedFile::class : FileReference::class) - ->disableOriginalConstructor() - ->setMethods(['getProperty', 'getMimeType', 'getContents', 'usesOriginalFile']) - ->getMock(); + if ($processed) { + $fileMock = $this->getMockBuilder(ProcessedFile::class) + ->disableOriginalConstructor() + ->onlyMethods(['getProperty', 'getMimeType', 'getContents', 'usesOriginalFile']) + ->getMock(); + + $fileMock + ->method('usesOriginalFile') + ->will($this->returnCallback(function () use ($properties) { + return false; + })); + } else { + $fileMock = $this->getMockBuilder(FileReference::class) + ->disableOriginalConstructor() + ->onlyMethods(['getProperty', 'getMimeType', 'getContents']) + ->getMock(); + } $fileMock ->method('getProperty') @@ -90,11 +103,6 @@ protected function mockFileObject($properties, $processed = false) ->will($this->returnCallback(function () use ($properties) { return 'das-ist-der-dateiinhalt'; })); - $fileMock - ->method('usesOriginalFile') - ->will($this->returnCallback(function () use ($properties) { - return false; - })); return $fileMock; } diff --git a/Tests/Unit/Utility/ResponsiveImagesUtility/HelpersTest.php b/Tests/Unit/Utility/ResponsiveImagesUtility/HelpersTest.php index 223dcf0..f46a2d1 100644 --- a/Tests/Unit/Utility/ResponsiveImagesUtility/HelpersTest.php +++ b/Tests/Unit/Utility/ResponsiveImagesUtility/HelpersTest.php @@ -5,9 +5,9 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -class HelpersTest extends AbstractResponsiveImagesUtilityTest +class HelpersTest extends AbstractResponsiveImagesUtilityTestCase { - public function addMetadataToImageTagWithFocusAreaProvider() + public static function addMetadataToImageTagWithFocusAreaProvider() { $imageTagWithAttribute = new TagBuilder('img'); $imageTagWithAttribute->addAttribute('data-focus-area', 'fixed'); @@ -16,32 +16,32 @@ public function addMetadataToImageTagWithFocusAreaProvider() // Test focus area attribute 'usingFocusArea' => [ new TagBuilder('img'), - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new Area(0.4, 0.4, 0.6, 0.6), htmlspecialchars(json_encode(['x' => 400, 'y' => 400, 'width' => 600, 'height' => 600])) ], // Test fallback to fixed value 'usingFixedFocusAreaValue' => [ $imageTagWithAttribute, - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new Area(0.4, 0.4, 0.6, 0.6), 'fixed' ], // Test omitted parameter 'withoutFocusArea' => [ new TagBuilder('img'), - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null ], // Test empty focus area 'withEmptyFocusArea' => [ new TagBuilder('img'), - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new Area(0, 0, 1, 1), null ] @@ -54,6 +54,9 @@ public function addMetadataToImageTagWithFocusAreaProvider() */ public function addMetadataToImageTagWithFocusArea($tag, $originalImage, $fallbackImage, $focusArea, $dataAttribute) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $this->utility->addMetadataToImageTag($tag, $originalImage, $fallbackImage, $focusArea); $this->assertEquals( $dataAttribute, @@ -61,7 +64,7 @@ public function addMetadataToImageTagWithFocusArea($tag, $originalImage, $fallba ); } - public function addMetadataToImageTagWithAltAndTitleProvider() + public static function addMetadataToImageTagWithAltAndTitleProvider() { $imageTagWithAttributes = new TagBuilder('img'); $imageTagWithAttributes->addAttribute('alt', 'fixed alt'); @@ -71,28 +74,24 @@ public function addMetadataToImageTagWithAltAndTitleProvider() // Test alt and title attributes 'usingAltAndTitle' => [ new TagBuilder('img'), - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], 'image alt', 'image title' ], // Test fixed alt/title attributes 'usingFixedAltAndTitleValues' => [ $imageTagWithAttributes, - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], 'fixed alt', 'fixed title' ], // Test default alt/title attributes 'withoutAltAndTitle' => [ new TagBuilder('img'), - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], '', null ] @@ -110,17 +109,20 @@ public function addMetadataToImageTagWithAltAndTitle( $altAttribute, $titleAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $this->utility->addMetadataToImageTag($tag, $originalImage, $fallbackImage); $this->assertEquals($altAttribute, $tag->getAttribute('alt')); $this->assertEquals($titleAttribute, $tag->getAttribute('title')); } - public function generatesSrcsetImagesProvider() + public static function generatesSrcsetImagesProvider() { return [ // Test high dpi image srcset 'usingHighDpi' => [ - $this->mockFileObject(['width' => 1000, 'extension' => 'jpg']), + ['width' => 1000, 'extension' => 'jpg'], 400, ['1x', '2x'], null, @@ -129,7 +131,7 @@ public function generatesSrcsetImagesProvider() ], // Test responsive image srcset (widths in integers) 'usingResponsiveWidths' => [ - $this->mockFileObject(['width' => 1000, 'extension' => 'jpg']), + ['width' => 1000, 'extension' => 'jpg'], 400, [200, 400, 600], null, @@ -138,7 +140,7 @@ public function generatesSrcsetImagesProvider() ], // Test responsive image srcset (widths as strings) 'usingResponsiveWidthsAsStrings' => [ - $this->mockFileObject(['width' => 1000, 'extension' => 'jpg']), + ['width' => 1000, 'extension' => 'jpg'], 400, ['200w', '400w', '600w'], null, @@ -147,7 +149,7 @@ public function generatesSrcsetImagesProvider() ], // Test absolute urls 'requestingAbsoluteUrls' => [ - $this->mockFileObject(['width' => 1000, 'extension' => 'jpg']), + ['width' => 1000, 'extension' => 'jpg'], 400, ['200w', '400w', '600w'], null, @@ -160,7 +162,7 @@ public function generatesSrcsetImagesProvider() ], // Test srcset input as string 'usingSrcsetString' => [ - $this->mockFileObject(['width' => 1000, 'extension' => 'jpg']), + ['width' => 1000, 'extension' => 'jpg'], 400, '200, 400, 600', null, @@ -172,7 +174,7 @@ public function generatesSrcsetImagesProvider() ] ], 'usingTooSmallImage' => [ - $this->mockFileObject(['width' => 400, 'extension' => 'jpg']), + ['width' => 400, 'extension' => 'jpg'], 400, '200, 300, 500', null, @@ -185,7 +187,7 @@ public function generatesSrcsetImagesProvider() ], // Test if special characters are kept in file name 'usingSpecialCharactersInFileName' => [ - $this->mockFileObject(['name' => 'this/is a/filename@with-/special!charac,ters', 'width' => 400, 'extension' => 'jpg']), + ['name' => 'this/is a/filename@with-/special!charac,ters', 'width' => 400, 'extension' => 'jpg'], 400, [200], null, @@ -203,6 +205,8 @@ public function generatesSrcsetImagesProvider() */ public function generatesSrcsetImages($originalImage, $width, $srcsetConfig, $cropArea, $absoluteUri, $output) { + $originalImage = $this->mockFileObject($originalImage); + $this->assertEquals( $output, $this->utility->generateSrcsetImages( @@ -215,11 +219,11 @@ public function generatesSrcsetImages($originalImage, $width, $srcsetConfig, $cr ); } - public function generatePlaceholderImageProvider() + public static function generatePlaceholderImageProvider() { return [ 'usingFile' => [ - $this->mockFileObject(['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg']), + ['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg'], 20, null, false, @@ -227,7 +231,7 @@ public function generatePlaceholderImageProvider() '/image-20.jpg' ], 'usingFileWithAbsolute' => [ - $this->mockFileObject(['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg']), + ['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg'], 20, null, false, @@ -235,7 +239,7 @@ public function generatePlaceholderImageProvider() 'http://domain.tld/image-20.jpg' ], 'usingInline' => [ - $this->mockFileObject(['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg']), + ['width' => 1000, 'mimeType' => 'image/jpeg', 'extension' => 'jpg'], 20, null, true, @@ -251,6 +255,8 @@ public function generatePlaceholderImageProvider() */ public function generatePlaceholderImage($originalImage, $width, $cropArea, $inline, $absoluteUri, $output) { + $originalImage = $this->mockFileObject($originalImage); + $this->assertEquals( $output, $this->utility->generatePlaceholderImage( @@ -263,7 +269,7 @@ public function generatePlaceholderImage($originalImage, $width, $cropArea, $inl ); } - public function generateSrcsetAttributeProvider() + public static function generateSrcsetAttributeProvider() { return [ // Test empty srcset @@ -301,7 +307,7 @@ public function generateSrcsetAttribute($input, $output) $this->assertEquals($output, $this->utility->generateSrcsetAttribute($input)); } - public function normalizeImageBreakpointsProvider() + public static function normalizeImageBreakpointsProvider() { return [ // Test without any breakpoints diff --git a/Tests/Unit/Utility/ResponsiveImagesUtility/ImageTagTest.php b/Tests/Unit/Utility/ResponsiveImagesUtility/ImageTagTest.php index 6d6dcb9..02a7e9e 100644 --- a/Tests/Unit/Utility/ResponsiveImagesUtility/ImageTagTest.php +++ b/Tests/Unit/Utility/ResponsiveImagesUtility/ImageTagTest.php @@ -5,17 +5,17 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -class ImageTagTest extends AbstractResponsiveImagesUtilityTest +class ImageTagTest extends AbstractResponsiveImagesUtilityTestCase { - public function createSimpleImageTagProvider() + public static function createSimpleImageTagProvider() { $predefinedTag = new TagBuilder('img-test'); $predefinedTag->addAttribute('class', 'myClass'); return [ 'simple' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -32,10 +32,8 @@ public function createSimpleImageTagProvider() null ], 'usingMetadata' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -52,8 +50,8 @@ public function createSimpleImageTagProvider() null ], 'usingPredefinedTag' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], clone $predefinedTag, null, false, @@ -70,8 +68,8 @@ public function createSimpleImageTagProvider() 'myClass' ], 'usingFocusArea' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, new Area(0.4, 0.4, 0.6, 0.6), false, @@ -88,8 +86,8 @@ public function createSimpleImageTagProvider() null ], 'usingAbsoluteUri' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, true, @@ -106,8 +104,8 @@ public function createSimpleImageTagProvider() null ], 'usingLazyload' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -124,8 +122,8 @@ public function createSimpleImageTagProvider() 'lazyload' ], 'usingLazyloadWithPredefinedTag' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], clone $predefinedTag, null, false, @@ -142,8 +140,8 @@ public function createSimpleImageTagProvider() 'myClass lazyload' ], 'usingLazyloadWithPlaceholder' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -160,8 +158,8 @@ public function createSimpleImageTagProvider() 'lazyload' ], 'usingLazyloadWithPlaceholderInline' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -178,8 +176,8 @@ public function createSimpleImageTagProvider() 'lazyload' ], 'usingCustomFileExtension' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], null, null, false, @@ -220,6 +218,9 @@ public function createSimpleImageTag( $titleAttribute, $classAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createSimpleImageTag( $originalImage, $fallbackImage, @@ -240,13 +241,13 @@ public function createSimpleImageTag( $this->assertEquals($classAttribute, $tag->getAttribute('class')); } - public function createImageTagWithSrcsetUsingEmptySrcsetProvider() + public static function createImageTagWithSrcsetUsingEmptySrcsetProvider() { return [ // Test plain tag 'usingEmptySrcset' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [], 'img', 1000, @@ -271,6 +272,9 @@ public function createImageTagWithSrcsetUsingEmptySrcset( $srcAttribute, $srcsetAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset($originalImage, $fallbackImage, $srcsetConfig); $this->assertEquals($tagName, $tag->getTagName()); $this->assertEquals($widthAttribute, $tag->getAttribute('width')); @@ -279,45 +283,45 @@ public function createImageTagWithSrcsetUsingEmptySrcset( $this->assertEquals($srcsetAttribute, $tag->getAttribute('srcset')); } - public function createImageTagWithSrcsetProvider() + public static function createImageTagWithSrcsetProvider() { return [ // Test standard output 'usingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], '/image-1000.jpg', '/image-400.jpg 400w, /image-1000.jpg 1000w' ], // Test srcset with 3 widths, one having same width as fallback 'usingThreeWidthsWithFallbackDuplicate' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400, 800, 1000], '/image-1000.jpg', '/image-400.jpg 400w, /image-800.jpg 800w, /image-1000.jpg 1000w' ], // Test srcset with 2 widths + fallback image 'usingTwoWidthsWithoutFallbackDuplicate' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400, 800], '/image-1000.jpg', '/image-400.jpg 400w, /image-800.jpg 800w, /image-1000.jpg 1000w' ], // Test high dpi 'usingHighDpi' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], ['1x', '2x'], '/image-1000.jpg', '/image-1000.jpg 1x, /image-2000.jpg 2x' ], // Test svg image 'usingSvg' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'svg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], [100, 200, 300], '/image-2000.svg', null @@ -336,6 +340,9 @@ public function createImageTagWithSrcset( $srcAttribute, $srcsetAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset( $originalImage, $fallbackImage, @@ -345,12 +352,12 @@ public function createImageTagWithSrcset( $this->assertEquals($srcsetAttribute, $tag->getAttribute('srcset')); } - public function createImageTagWithSrcsetAndFocusAreaProvider() + public static function createImageTagWithSrcsetAndFocusAreaProvider() { return [ 'usingFocusArea' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new Area(0.4, 0.4, 0.6, 0.6), htmlspecialchars(json_encode(['x' => 400, 'y' => 400, 'width' => 600, 'height' => 600])) ] @@ -367,34 +374,37 @@ public function createImageTagWithSrcsetAndFocusArea( $focusArea, $focusAreaAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + // Test focus area attribute $tag = $this->utility->createImageTagWithSrcset($originalImage, $fallbackImage, [], null, $focusArea); $this->assertEquals($focusAreaAttribute, $tag->getAttribute('data-focus-area')); } - public function createImageTagWithSrcsetAndSizesProvider() + public static function createImageTagWithSrcsetAndSizesProvider() { return [ // Test sizes attribute 'usingStaticQuery' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], 'sizes query', 'sizes query' ], // Test sizes attribute with dynamic width 'usingDynamicQuery' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], '%1$d', 1000 ], // Test sizes attribute for high dpi setup 'usingHighDpi' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], ['1x', '2x'], '%1$d', null @@ -413,6 +423,9 @@ public function createImageTagWithSrcsetAndSizes( $sizesConfig, $sizesAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset( $originalImage, $fallbackImage, @@ -424,24 +437,20 @@ public function createImageTagWithSrcsetAndSizes( $this->assertEquals($sizesAttribute, $tag->getAttribute('sizes')); } - public function createImageTagWithSrcsetAndMetadataProvider() + public static function createImageTagWithSrcsetAndMetadataProvider() { return [ // Test image metadata attributes 'usingMetadata' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], 'image alt', 'image title' ], // Test svg image metadata attributes 'usingMetadataWithSvg' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'svg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], 'image alt', 'image title' ] @@ -454,13 +463,16 @@ public function createImageTagWithSrcsetAndMetadataProvider() */ public function createImageTagWithSrcsetAndMetadata($originalImage, $fallbackImage, $altAttribute, $titleAttribute) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + // Test image metadata attributes $tag = $this->utility->createImageTagWithSrcset($originalImage, $fallbackImage, []); $this->assertEquals($altAttribute, $tag->getAttribute('alt')); $this->assertEquals($titleAttribute, $tag->getAttribute('title')); } - public function createImageTagWithSrcsetBasedOnCustomTagProvider() + public static function createImageTagWithSrcsetBasedOnCustomTagProvider() { // Test if original tag attributes persist $customTag = new TagBuilder('img'); @@ -469,19 +481,15 @@ public function createImageTagWithSrcsetBasedOnCustomTagProvider() return [ 'usingCustomTag' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alt' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alt' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], $customTag, 'fixed alt', 'long description' ], 'usingCustomTagWithSvg' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alt' => 'image alt', 'title' => 'image title', 'extension' => 'svg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'alt' => 'image alt', 'title' => 'image title', 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], $customTag, 'fixed alt', 'long description' @@ -500,6 +508,9 @@ public function createImageTagWithSrcsetBasedOnCustomTag( $altAttribute, $longdescAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset( $originalImage, $fallbackImage, @@ -513,13 +524,13 @@ public function createImageTagWithSrcsetBasedOnCustomTag( $this->assertEquals($longdescAttribute, $tag->getAttribute('longdesc')); } - public function createImageTagWithSrcsetAndLazyloadProvider() + public static function createImageTagWithSrcsetAndLazyloadProvider() { return [ // Test standard output 'usingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], null, null, @@ -530,8 +541,8 @@ public function createImageTagWithSrcsetAndLazyloadProvider() ], // Test srcset with 2 widths + fallback image 'usingTwoWidthsWithoutFallbackDuplicate' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400, 800], null, null, @@ -542,8 +553,8 @@ public function createImageTagWithSrcsetAndLazyloadProvider() ], // Test svg image 'withSvgImage' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'svg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], [400, 800], null, null, @@ -554,8 +565,8 @@ public function createImageTagWithSrcsetAndLazyloadProvider() ], // Test placeholder image 'usingPlaceholderImage' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], '/image-20.jpg', null, @@ -566,8 +577,8 @@ public function createImageTagWithSrcsetAndLazyloadProvider() ], // Test placeholder image inline 'usingPlaceholderImageInline' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], [400], 'data:image/jpeg;base64,ZGFzLWlzdC1kZXItZGF0ZWlpbmhhbHQ=', null, @@ -594,6 +605,9 @@ public function createImageTagWithSrcsetAndLazyload( $placeholderSize, $placeholderInline ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset( $originalImage, $fallbackImage, @@ -615,13 +629,13 @@ public function createImageTagWithSrcsetAndLazyload( $this->assertEquals('lazyload', $tag->getAttribute('class')); } - public function createImageTagWithSrcsetAndCustomFileExtensionProvider() + public static function createImageTagWithSrcsetAndCustomFileExtensionProvider() { return [ // Test standard output 'usingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], false, 'svg', @@ -634,8 +648,8 @@ public function createImageTagWithSrcsetAndCustomFileExtensionProvider() ], // Test standard output 'usingLazyload' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], true, 'svg', @@ -648,8 +662,8 @@ public function createImageTagWithSrcsetAndCustomFileExtensionProvider() ], // Test svg image 'withSvgImage' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'svg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], [400, 800], true, 'svg', @@ -662,8 +676,8 @@ public function createImageTagWithSrcsetAndCustomFileExtensionProvider() ], // Test with ignored extension 'withIgnoredExtension' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'svg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], [400, 800], false, 'svg, webp', @@ -676,8 +690,8 @@ public function createImageTagWithSrcsetAndCustomFileExtensionProvider() ], // Test placeholder image 'usingPlaceholderImage' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [400], true, 'svg', @@ -690,8 +704,8 @@ public function createImageTagWithSrcsetAndCustomFileExtensionProvider() ], // Test placeholder image inline 'usingPlaceholderImageInline' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], [400], true, 'svg', @@ -722,6 +736,9 @@ public function createImageTagWithSrcsetAndCustomFileExtension( $placeholderSize, $placeholderInline ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createImageTagWithSrcset( $originalImage, $fallbackImage, diff --git a/Tests/Unit/Utility/ResponsiveImagesUtility/PictureSourceTagTest.php b/Tests/Unit/Utility/ResponsiveImagesUtility/PictureSourceTagTest.php index a23de86..30955c5 100644 --- a/Tests/Unit/Utility/ResponsiveImagesUtility/PictureSourceTagTest.php +++ b/Tests/Unit/Utility/ResponsiveImagesUtility/PictureSourceTagTest.php @@ -2,14 +2,14 @@ namespace Sitegeist\ResponsiveImages\Tests\Unit\Utility\ResponsiveImagesUtility; -class PictureSourceTagTest extends AbstractResponsiveImagesUtilityTest +class PictureSourceTagTest extends AbstractResponsiveImagesUtilityTestCase { - public function createPictureSourceTagProvider() + public static function createPictureSourceTagProvider() { return [ // Test empty srcset 'usingEmptySrcset' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, [], '', @@ -21,7 +21,7 @@ public function createPictureSourceTagProvider() ], // Test high dpi srcset 'usingHighDpi' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, ['1x', '2x'], '', @@ -33,7 +33,7 @@ public function createPictureSourceTagProvider() ], // Test responsive images srcset 'usingResponsiveWidths' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, [400, 800], '', @@ -45,7 +45,7 @@ public function createPictureSourceTagProvider() ], // Test dynamic sizes query 'usingDynamicSizesQuery' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, [400], 'media query', @@ -57,7 +57,7 @@ public function createPictureSourceTagProvider() ], // Test custom file extension 'usingCustomFileExtension' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, [400], 'media query', @@ -69,7 +69,7 @@ public function createPictureSourceTagProvider() ], // Test absolute urls 'requestingAbsoluteUrls' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], 1000, ['1x', '2x'], '', @@ -97,6 +97,8 @@ public function createPictureSourceTag( $srcsetAttribute, $sizesAttribute ) { + $image = $this->mockFileObject($image); + $tag = $this->utility->createPictureSourceTag( $image, $defaultWidth, diff --git a/Tests/Unit/Utility/ResponsiveImagesUtility/PictureTagTest.php b/Tests/Unit/Utility/ResponsiveImagesUtility/PictureTagTest.php index e0fa26f..5db390c 100644 --- a/Tests/Unit/Utility/ResponsiveImagesUtility/PictureTagTest.php +++ b/Tests/Unit/Utility/ResponsiveImagesUtility/PictureTagTest.php @@ -7,9 +7,9 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariant; -class PictureTagTest extends AbstractResponsiveImagesUtilityTest +class PictureTagTest extends AbstractResponsiveImagesUtilityTestCase { - public function createPictureTagProvider() + public static function createPictureTagProvider() { $cropVariantCollection = new CropVariantCollection([ new CropVariant('desktop', 'Desktop', Area::createEmpty()), @@ -19,8 +19,8 @@ public function createPictureTagProvider() return [ // Test two breakpoints with media queries with standard output 'usingTwoBreakpointsWithMediaRequestingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -49,8 +49,8 @@ public function createPictureTagProvider() ], // Test two breakpoints, last one without media query, with standard output 'usingTwoBreakpointsLastWithoutMediaRequestingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -74,8 +74,8 @@ public function createPictureTagProvider() ], // Test focus area 'usingFocusArea' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [], $cropVariantCollection, new Area(0.4, 0.4, 0.6, 0.6), @@ -91,10 +91,8 @@ public function createPictureTagProvider() ], // Test image metadata attributes 'usingMetadata' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [], $cropVariantCollection, null, @@ -108,8 +106,8 @@ public function createPictureTagProvider() ], // Test lazyload markup with standard output 'usingLazyloadRequestingStandardOutput' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -133,8 +131,8 @@ public function createPictureTagProvider() ], // Test lazyload markup with placeholder 'usingLazyloadWithPlaceholder' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -158,8 +156,8 @@ public function createPictureTagProvider() ], // Test lazyload markup with inline placeholder 'usingLazyloadWithInlinePlaceholder' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -200,6 +198,9 @@ public function createPictureTag( $tagName, $tagContent ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createPictureTag( $originalImage, $fallbackImage, @@ -218,7 +219,7 @@ public function createPictureTag( $this->assertEquals(implode('', $tagContent), $tag->getContent()); } - public function createPictureTagWithCustomTagProvider() + public static function createPictureTagWithCustomTagProvider() { $pictureTag = new TagBuilder('picture-custom'); $pictureTag->addAttribute('test', 'test attribute'); @@ -226,8 +227,8 @@ public function createPictureTagWithCustomTagProvider() return [ // Test if tag attributes persist 'usingCustomTag' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new CropVariantCollection([]), $pictureTag, 'picture-custom', @@ -248,6 +249,9 @@ public function createPictureTagWithCustomTag( $tagName, $testAttribute ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createPictureTag( $originalImage, $fallbackImage, @@ -260,7 +264,7 @@ public function createPictureTagWithCustomTag( $this->assertEquals($testAttribute, $tag->getAttribute('test')); } - public function createPictureTagWithCustomFallbackTagProvider() + public static function createPictureTagWithCustomFallbackTagProvider() { $fallbackTag = new TagBuilder('img'); $fallbackTag->addAttribute('alt', 'fixed alt'); @@ -271,10 +275,8 @@ public function createPictureTagWithCustomFallbackTagProvider() return [ // Test if fallback tag attributes persist 'usingCustomFallbackTag' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new CropVariantCollection([]), clone $fallbackTag, false, @@ -282,10 +284,8 @@ public function createPictureTagWithCustomFallbackTagProvider() ], // Test if fallback tag works with lazyloading 'usingCustomFallbackTagWithLazyload' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], new CropVariantCollection([]), clone $fallbackTag, true, @@ -306,6 +306,9 @@ public function createPictureTagWithCustomFallbackTag( $lazyload, $tagContent ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createPictureTag( $originalImage, $fallbackImage, @@ -320,15 +323,13 @@ public function createPictureTagWithCustomFallbackTag( $this->assertEquals(implode('', $tagContent), $tag->getContent()); } - public function createPictureTagFromSvgProvider() + public static function createPictureTagFromSvgProvider() { return [ // Test if fallback tag attributes persist 'withSvgImage' => [ - $this->mockFileObject( - ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'svg'] - ), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'svg']), + ['width' => 2000, 'height' => 2000, 'alternative' => 'image alt', 'title' => 'image title', 'extension' => 'svg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'svg'], 'img', '/image-2000.svg', null, @@ -344,6 +345,9 @@ public function createPictureTagFromSvgProvider() */ public function createPictureTagFromSvg($originalImage, $fallbackImage, $tagName, $srcAttribute, $srcsetAttribute, $heightAttribute, $widthAttribute) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createPictureTag( $originalImage, $fallbackImage, @@ -357,7 +361,7 @@ public function createPictureTagFromSvg($originalImage, $fallbackImage, $tagName $this->assertEquals($heightAttribute, $tag->getAttribute('height')); } - public function createPictureTagWithCustomFileExtensionProvider() + public static function createPictureTagWithCustomFileExtensionProvider() { $cropVariantCollection = new CropVariantCollection([ new CropVariant('desktop', 'Desktop', Area::createEmpty()), @@ -367,8 +371,8 @@ public function createPictureTagWithCustomFileExtensionProvider() return [ // Test two breakpoints with media queries with standard output 'usingTwoBreakpoints' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -397,8 +401,8 @@ public function createPictureTagWithCustomFileExtensionProvider() ], // Test lazyload markup with standard output 'usingLazyload' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -422,8 +426,8 @@ public function createPictureTagWithCustomFileExtensionProvider() ], // Test lazyload markup with placeholder 'usingLazyloadWithPlaceholder' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -447,8 +451,8 @@ public function createPictureTagWithCustomFileExtensionProvider() ], // Test lazyload markup with inline placeholder 'usingLazyloadWithInlinePlaceholder' => [ - $this->mockFileObject(['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg']), - $this->mockFileObject(['width' => 1000, 'height' => 1000, 'extension' => 'jpg']), + ['width' => 2000, 'height' => 2000, 'extension' => 'jpg', 'mimeType' => 'image/jpeg'], + ['width' => 1000, 'height' => 1000, 'extension' => 'jpg'], [ [ 'cropVariant' => 'desktop', @@ -489,6 +493,9 @@ public function createPictureTagWithCustomFileExtension( $tagName, $tagContent ) { + $originalImage = $this->mockFileObject($originalImage); + $fallbackImage = $this->mockFileObject($fallbackImage); + $tag = $this->utility->createPictureTag( $originalImage, $fallbackImage, diff --git a/composer.json b/composer.json index 9c5e236..e4a8d5d 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "typo3/cms-core": "^12.2 || ^11.5 || ^10.4" }, "require-dev": { - "typo3/testing-framework": "^6.0 || ^4.0 || dev-main", + "typo3/testing-framework": "^7.0 || ^6.0 || dev-main", "squizlabs/php_codesniffer": "^3.0", "editorconfig-checker/editorconfig-checker": "^10.0" }, @@ -29,6 +29,11 @@ "Sitegeist\\ResponsiveImages\\": "Classes/" } }, + "autoload-dev": { + "psr-4": { + "Sitegeist\\ResponsiveImages\\Tests\\": "Tests/" + } + }, "config": { "vendor-dir": ".Build/vendor", "bin-dir": ".Build/bin", From 7bbe9a64bc00d6f23b7a387d22f79f900830cd94 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 16:52:33 +0100 Subject: [PATCH 07/15] =?UTF-8?q?[TASK]=C2=A0Add=20decoding=20attribute=20?= =?UTF-8?q?to=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fluid_styled_content/Partials/Media/Rendering/Image.html | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Private/Extensions/fluid_styled_content/Partials/Media/Rendering/Image.html b/Resources/Private/Extensions/fluid_styled_content/Partials/Media/Rendering/Image.html index 33db3bd..495d6d7 100644 --- a/Resources/Private/Extensions/fluid_styled_content/Partials/Media/Rendering/Image.html +++ b/Resources/Private/Extensions/fluid_styled_content/Partials/Media/Rendering/Image.html @@ -15,5 +15,6 @@ ignoreFileExtensions="{settings.tx_smsresponsiveimages.ignoreFileExtensions}" fileExtension="{settings.tx_smsresponsiveimages.fileExtension}" loading="{settings.media.lazyLoading}" + decoding="{settings.media.imageDecoding}" /> From e51d60c0d2099ad12f027fb3f588ee01275e466c Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 18:33:40 +0100 Subject: [PATCH 08/15] [TASK] Remove obsolete constant check --- ext_localconf.php | 1 - 1 file changed, 1 deletion(-) diff --git a/ext_localconf.php b/ext_localconf.php index d7c64f4..d775dda 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,5 +1,4 @@ Date: Wed, 8 Mar 2023 18:49:18 +0100 Subject: [PATCH 09/15] [BUGFIX] Fix strict types --- Classes/ViewHelpers/ImageViewHelper.php | 2 +- Classes/ViewHelpers/MediaViewHelper.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/ViewHelpers/ImageViewHelper.php b/Classes/ViewHelpers/ImageViewHelper.php index 01cf4fd..b74e9a2 100644 --- a/Classes/ViewHelpers/ImageViewHelper.php +++ b/Classes/ViewHelpers/ImageViewHelper.php @@ -14,7 +14,7 @@ final class ImageViewHelper extends AbstractTagBasedViewHelper { - protected string $tagName = 'img'; + protected $tagName = 'img'; protected ImageService $imageService; protected ResponsiveImagesUtility $responsiveImagesUtility; diff --git a/Classes/ViewHelpers/MediaViewHelper.php b/Classes/ViewHelpers/MediaViewHelper.php index 12d2f0e..f5ebce0 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -16,7 +16,7 @@ final class MediaViewHelper extends AbstractTagBasedViewHelper { - protected string $tagName = 'img'; + protected $tagName = 'img'; protected ImageService $imageService; protected ResponsiveImagesUtility $responsiveImagesUtility; From d943a25031c2da44af0cdc0e5a1e80d617836e05 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 18:49:27 +0100 Subject: [PATCH 10/15] [TASK] Setup modern DI --- Configuration/Services.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Configuration/Services.yaml diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml new file mode 100644 index 0000000..d9c37c6 --- /dev/null +++ b/Configuration/Services.yaml @@ -0,0 +1,8 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Sitegeist\ResponsiveImages\: + resource: '../Classes/*' From f13d0cbd3080d65eebf8250de9b0d6d8473fbecf Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Wed, 8 Mar 2023 18:54:34 +0100 Subject: [PATCH 11/15] [BUGFIX] Fix strict types --- Classes/ViewHelpers/ImageViewHelper.php | 6 +++--- Classes/ViewHelpers/MediaViewHelper.php | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Classes/ViewHelpers/ImageViewHelper.php b/Classes/ViewHelpers/ImageViewHelper.php index b74e9a2..5f853e4 100644 --- a/Classes/ViewHelpers/ImageViewHelper.php +++ b/Classes/ViewHelpers/ImageViewHelper.php @@ -172,7 +172,7 @@ public function render(): string $this->arguments['absolute'], $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $this->arguments['placeholderInline'], $this->arguments['fileExtension'] ); @@ -190,7 +190,7 @@ public function render(): string $this->arguments['absolute'], $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $this->arguments['placeholderInline'], $this->arguments['fileExtension'] ); @@ -211,7 +211,7 @@ public function render(): string $focusArea, $this->arguments['absolute'], $this->arguments['lazyload'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $this->arguments['placeholderInline'], $this->arguments['fileExtension'] ); diff --git a/Classes/ViewHelpers/MediaViewHelper.php b/Classes/ViewHelpers/MediaViewHelper.php index f5ebce0..df26ffd 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -9,6 +9,7 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Resource\FileReference; +use TYPO3\CMS\Core\Resource\Rendering\RendererRegistry; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper; @@ -222,7 +223,7 @@ protected function renderPicture(FileInterface $image, $width, $height, ?string false, $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $this->arguments['placeholderInline'], $fileExtension ); @@ -272,7 +273,7 @@ protected function renderImageSrcset(FileInterface $image, $width, $height, ?str false, $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $this->arguments['placeholderInline'], $fileExtension ); From 26dd8b567dbed047df55a6e204c53b50360aacee Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Fri, 10 Mar 2023 18:55:36 +0100 Subject: [PATCH 12/15] [TASK] Use DI in ViewHelpers --- Classes/ViewHelpers/ImageViewHelper.php | 11 +++++++---- Classes/ViewHelpers/MediaViewHelper.php | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Classes/ViewHelpers/ImageViewHelper.php b/Classes/ViewHelpers/ImageViewHelper.php index 5f853e4..1a16823 100644 --- a/Classes/ViewHelpers/ImageViewHelper.php +++ b/Classes/ViewHelpers/ImageViewHelper.php @@ -18,11 +18,14 @@ final class ImageViewHelper extends AbstractTagBasedViewHelper protected ImageService $imageService; protected ResponsiveImagesUtility $responsiveImagesUtility; - public function __construct() + public function injectImageService(ImageService $imageService): void { - parent::__construct(); - $this->imageService = GeneralUtility::makeInstance(ImageService::class); - $this->responsiveImagesUtility = GeneralUtility::makeInstance(ResponsiveImagesUtility::class); + $this->imageService = $imageService; + } + + public function injectResponsiveImagesUtility(ResponsiveImagesUtility $responsiveImagesUtility): void + { + $this->responsiveImagesUtility = $responsiveImagesUtility; } public function initializeArguments(): void diff --git a/Classes/ViewHelpers/MediaViewHelper.php b/Classes/ViewHelpers/MediaViewHelper.php index df26ffd..e19a527 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -21,11 +21,14 @@ final class MediaViewHelper extends AbstractTagBasedViewHelper protected ImageService $imageService; protected ResponsiveImagesUtility $responsiveImagesUtility; - public function __construct() + public function injectImageService(ImageService $imageService): void { - parent::__construct(); - $this->imageService = GeneralUtility::makeInstance(ImageService::class); - $this->responsiveImagesUtility = GeneralUtility::makeInstance(ResponsiveImagesUtility::class); + $this->imageService = $imageService; + } + + public function injectResponsiveImagesUtility(ResponsiveImagesUtility $responsiveImagesUtility): void + { + $this->responsiveImagesUtility = $responsiveImagesUtility; } public function initializeArguments(): void From 1132d0d53067c1dcd07d0b4c3a5c16a5a56a0c8c Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Fri, 10 Mar 2023 18:56:00 +0100 Subject: [PATCH 13/15] [TASK] Add functional tests for ImageViewHelper --- .github/workflows/tests.yml | 49 ++- Build/Testing/FunctionalTests.xml | 27 ++ Build/Testing/FunctionalTestsBootstrap.php | 20 + .../Fixtures/ImageViewHelperTest.png | Bin 0 -> 469 bytes Tests/Functional/ImageViewHelperTest.php | 381 ++++++++++++++++++ Tests/Functional/MediaViewHelperTest.php | 248 ++++++++++++ Tests/Functional/ViewHelperTestCase.php | 44 ++ composer.json | 19 +- 8 files changed, 780 insertions(+), 8 deletions(-) create mode 100644 Build/Testing/FunctionalTests.xml create mode 100644 Build/Testing/FunctionalTestsBootstrap.php create mode 100644 Tests/Functional/Fixtures/ImageViewHelperTest.png create mode 100644 Tests/Functional/ImageViewHelperTest.php create mode 100644 Tests/Functional/MediaViewHelperTest.php create mode 100644 Tests/Functional/ViewHelperTestCase.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20ad2cc..ced5450 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: run: composer lint:editorconfig - test: + test-unit: runs-on: ubuntu-latest strategy: @@ -51,7 +51,7 @@ jobs: - php-versions: 7.4 typo3-versions: 10 - name: PHP ${{ matrix.php-versions }} with TYPO3 ${{ matrix.typo3-versions }} + name: Unit Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - uses: actions/checkout@v2 @@ -75,5 +75,46 @@ jobs: run: composer require typo3/minimal "^${{ matrix.typo3-versions }}" --prefer-dist --no-progress --no-suggest - - name: Automated Testing - run: composer test + name: Unit Testing + run: composer test:unit + + + test-functional: + runs-on: ubuntu-latest + + strategy: + max-parallel: 1 + matrix: + php-versions: [8.2, 8.1] + typo3-versions: [12] + + name: Functional Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) + steps: + - + uses: actions/checkout@v2 + + - + name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: intl, mbstring, pdo_sqlite + + - + name: Setup GraphicsMagick + run: sudo apt-get install -y graphicsmagick + + - + name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} + + - + name: Install composer dependencies + run: composer require typo3/minimal "^${{ matrix.typo3-versions }}" --prefer-dist --no-progress --no-suggest + + - + name: Functional Testing + run: typo3DatabaseDriver=pdo_sqlite composer test:functional diff --git a/Build/Testing/FunctionalTests.xml b/Build/Testing/FunctionalTests.xml new file mode 100644 index 0000000..b6871ca --- /dev/null +++ b/Build/Testing/FunctionalTests.xml @@ -0,0 +1,27 @@ + + + + + ../../Tests/Functional/ + + + + + + + + diff --git a/Build/Testing/FunctionalTestsBootstrap.php b/Build/Testing/FunctionalTestsBootstrap.php new file mode 100644 index 0000000..443197d --- /dev/null +++ b/Build/Testing/FunctionalTestsBootstrap.php @@ -0,0 +1,20 @@ +defineOriginalRootPath(); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/transient'); +}); diff --git a/Tests/Functional/Fixtures/ImageViewHelperTest.png b/Tests/Functional/Fixtures/ImageViewHelperTest.png new file mode 100644 index 0000000000000000000000000000000000000000..78ebd23bd241a58cec465ebb3b0064ae1fa8e0a9 GIT binary patch literal 469 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKNIxNgU5&4-vJ_0HA0G|+7AWaOoEA(?KP#sT6 zkY6wZ@26X;AMaey(^pdV(7IFvG&j)G#WAEJ?(O-FyiSTDZGl|U$xpddauOf%m?v_( zZzvEcH@x_aS5;MQ+nr}~*E4<+e=;Svc73*IhP?pY!G!O|%t6<`SI)T}F7(gh-t7l2 zR|>zFox61`*Jkm5zFfH^xg@Dy_ZO;^?8<$8x '', 'image' => null], 1517766588], + [['src' => null, 'image' => null], 1517766588], + [['src' => '', 'image' => null], 1517766588], + [['src' => 'something', 'image' => 'something'], 1517766588], + [['src' => 'something', 'image' => null, 'fileExtension' => 'dummy'], 1631539412], + ]; + } + + /** + * @test + * @dataProvider invalidArgumentsDataProvider + */ + public function renderThrowsExceptionOnInvalidArguments(array $arguments, int $expectedExceptionCode): void + { + $this->expectException(Exception::class); + $this->expectExceptionCode($expectedExceptionCode); + + $viewHelper = new ImageViewHelper(); + $viewHelper->setArguments($arguments); + $viewHelper->render(); + } + + public static function basicScalingCroppingDataProvider(): \Generator + { + yield 'original size' => [ + '', + '@^$@', + 400, + 300 + ]; + yield 'half width' => [ + '', + '@^$@', + 200, + 150 + ]; + yield 'stretched' => [ + '', + '@^$@', + 200, + 200 + ]; + yield 'cropped' => [ + '', + '@^$@', + 200, + 200 + ]; + yield 'masked width' => [ + '', + '@^$@', + 300, + 225 + ]; + yield 'masked height' => [ + '', + '@^$@', + 200, + 150 + ]; + // would be 200x150, but image will be stretched (why!?) up to have a width of 250 + yield 'min width' => [ + '', + '@^$@', + 250, + 150 + ]; + // would be 200x150, but image will be scaled down to have a width of 100 + yield 'max width' => [ + '', + '@^$@', + 100, + 75 + ]; + // would be 200x150, but image will be stretched (why!?) up to have a height of 200 + yield 'min height' => [ + '', + '@^$@', + 200, + 200 + ]; + // would be 200x150, but image will be scaled down to have a height of 75 + yield 'max height' => [ + '', + '@^$@', + 100, + 75 + ]; + } + + /** + * @test + * @dataProvider basicScalingCroppingDataProvider + */ + public function basicScalingCropping(string $template, string $expected, int $expectedWidth, int $expectedHeight): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + + $coreTemplate = str_replace('setTemplateSource('' . $coreTemplate); + self::assertMatchesRegularExpression($expected, $coreView->render(), 'result of sms:image viewhelper does not match result of core viewhelper'); + + $matches = []; + preg_match($expected, $result, $matches); + list($width, $height) = getimagesize($this->instancePath . '/' . $matches[1]); + self::assertEquals($expectedWidth, $width, 'width of generated image does not match expected width'); + self::assertEquals($expectedHeight, $height, 'height of generated image does not match expected height'); + } + + public static function cropVariantCollectionDataProvider(): \Generator + { + yield 'default crop' => [ + '', + '@^$@', + 200, + 225 + ]; + yield 'square crop' => [ + '', + '@^$@', + 300, + 300 + ]; + yield 'wide crop' => [ + '', + '@^$@', + 400, + 200 + ]; + } + + /** + * @test + * @dataProvider cropVariantCollectionDataProvider + */ + public function cropVariantCollection(string $template, string $expected, int $expectedWidth, int $expectedHeight): void + { + // Based on 400x300 dimensions + $cropVariantCollection = new CropVariantCollection([ + new CropVariant('default', 'Default', new Area(0.25, 0.25, 0.5, 0.75)), + new CropVariant('square', 'Square', new Area(0.125, 0, 0.75, 1)), + new CropVariant('wide', 'Wide', new Area(0, 1 / 6, 1, 2 / 3)), + ]); + + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + $view->assign('crop', (string) $cropVariantCollection); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + + $matches = []; + preg_match($expected, $result, $matches); + list($width, $height) = getimagesize($this->instancePath . '/' . $matches[1]); + self::assertEquals($expectedWidth, $width, 'width of generated image does not match expected width'); + self::assertEquals($expectedHeight, $height, 'height of generated image does not match expected height'); + } + + public static function tagAttributesDataProvider(): \Generator + { + yield 'css' => [ + '', + '' + ]; + yield 'loading' => [ + '', + '' + ]; + yield 'decoding' => [ + '', + '' + ]; + yield 'alt' => [ + '', + 'alternative text' + ]; + yield 'longdesc' => [ + '', + '' + ]; + yield 'usemap' => [ + '', + '' + ]; + yield 'default sizes' => [ + '', + '', + ]; + yield 'sizes' => [ + '', + '', + ]; + } + + /** + * @test + * @dataProvider tagAttributesDataProvider + */ + public function tagAttributes(string $template, string $expected): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + self::assertEquals($expected, $view->render()); + } + + public static function imageWithSrcsetDataProvider(): \Generator + { + yield 'hdpi variants' => [ + '', + '@^$@', + [ + 1 => [100, 75], + 2 => [100, 75], + 3 => [200, 150] + ] + ]; + yield 'width variants' => [ + '', + '@^$@', + [ + 1 => [100, 75], + 2 => [200, 150] + ] + ]; + } + + /** + * @test + * @dataProvider imageWithSrcsetDataProvider + */ + public function imageWithSrcset(string $template, string $expected, array $expectedDimensions): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + + $matches = []; + preg_match($expected, $result, $matches); + + foreach ($expectedDimensions as $match => $imageDimension) { + list($width, $height) = getimagesize($this->instancePath . '/' . $matches[$match]); + self::assertEquals($imageDimension[0], $width, sprintf('width of image %d does not match expected width', $match)); + self::assertEquals($imageDimension[1], $height, sprintf('height of image %d does not match expected height', $match)); + } + } + + public static function pictureTagDataProvider(): \Generator + { + yield [ + [ + [ + 'srcset' => '400', + 'cropVariant' => 'wide', + 'media' => '(min-width: 1000px)', + 'sizes' => '100vw' + ], + [ + 'srcset' => '300', + 'cropVariant' => 'square', + 'media' => '(min-width: 700px)', + 'sizes' => '50vw' + ], + ], + '@^$@', + ]; + } + + /** + * @test + * @dataProvider pictureTagDataProvider + */ + public function pictureTag(array $breakpoints, string $expected): void + { + // Based on 400x300 dimensions + $cropVariantCollection = new CropVariantCollection([ + new CropVariant('default', 'Default', Area::createEmpty()), + new CropVariant('square', 'Square', new Area(0.125, 0, 0.75, 1)), + new CropVariant('wide', 'Wide', new Area(0, 1 / 6, 1, 2 / 3)), + ]); + + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource(''); + $view->assignMultiple([ + 'crop' => (string) $cropVariantCollection, + 'breakpoints' => $breakpoints + ]); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + } + + public static function fileObjectsDataProvider(): \Generator + { + yield 'file in image argument' => [ + '', + '@^$@' + ]; + yield 'file reference in image argument' => [ + '', + '@^$@' + ]; + yield 'file id in src argument' => [ + '', + '@^$@' + ]; + /* + // Currently not testible with simple TYPO3 API calls + yield 'file reference id in src argument' => [ + '', + '@^$@' + ]; + */ + yield 'crop from file reference' => [ + '', + '@^$@' + ]; + } + + /** + * @test + * @dataProvider fileObjectsDataProvider + */ + public function fileObjects(string $template, string $expected) + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + $view->assignMultiple($this->createFileObjects()); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + } +} diff --git a/Tests/Functional/MediaViewHelperTest.php b/Tests/Functional/MediaViewHelperTest.php new file mode 100644 index 0000000..c4cc41e --- /dev/null +++ b/Tests/Functional/MediaViewHelperTest.php @@ -0,0 +1,248 @@ +expectException(\UnexpectedValueException::class); + $this->expectExceptionCode(1678270961); + + $viewHelper = new MediaViewHelper(); + $viewHelper->setArguments(['file' => new \stdClass]); + $viewHelper->render(); + } + + /** + * @test + */ + public function renderThrowsExceptionOnInvalidFileExtension(): void + { + $this->expectException(Exception::class); + $this->expectExceptionCode(1631539412); + + $fileObjects = $this->createFileObjects(); + + $viewHelper = new MediaViewHelper(); + $viewHelper->setArguments(['file' => $fileObjects['file'], 'image' => null, 'fileExtension' => 'dummy']); + $viewHelper->render(); + } + + public static function basicScalingCroppingDataProvider(): \Generator + { + yield 'original size' => [ + '', + '@^$@', + 400, + 300 + ]; + yield 'half width' => [ + '', + '@^$@', + 200, + 150 + ]; + yield 'stretched' => [ + '', + '@^$@', + 200, + 200 + ]; + yield 'cropped' => [ + '', + '@^$@', + 200, + 200 + ]; + yield 'masked width' => [ + '', + '@^$@', + 300, + 225 + ]; + yield 'masked height' => [ + '', + '@^$@', + 200, + 150 + ]; + } + + /** + * @test + * @dataProvider basicScalingCroppingDataProvider + */ + public function basicScalingCropping(string $template, string $expected, int $expectedWidth, int $expectedHeight): void + { + $fileObjects = $this->createFileObjects(); + + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + $view->assignMultiple($fileObjects); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + + $coreTemplate = str_replace('assignMultiple($fileObjects); + $coreView->setTemplateSource('' . $coreTemplate); + self::assertMatchesRegularExpression($expected, $coreView->render(), 'result of sms:image viewhelper does not match result of core viewhelper'); + + $matches = []; + preg_match($expected, $result, $matches); + list($width, $height) = getimagesize($this->instancePath . '/' . $matches[1]); + self::assertEquals($expectedWidth, $width, 'width of generated image does not match expected width'); + self::assertEquals($expectedHeight, $height, 'height of generated image does not match expected height'); + } + + public static function tagAttributesDataProvider(): \Generator + { + yield 'css' => [ + '', + '' + ]; + yield 'loading' => [ + '', + '' + ]; + yield 'decoding' => [ + '', + '' + ]; + yield 'alt' => [ + '', + 'alternative text' + ]; + yield 'default sizes' => [ + '', + '', + ]; + yield 'sizes' => [ + '', + '', + ]; + } + + /** + * @test + * @dataProvider tagAttributesDataProvider + */ + public function tagAttributes(string $template, string $expected): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + $view->assignMultiple($this->createFileObjects()); + self::assertEquals($expected, $view->render()); + } + + public static function imageWithSrcsetDataProvider(): \Generator + { + yield 'hdpi variants' => [ + '', + '@^$@', + [ + 1 => [100, 75], + 2 => [100, 75], + 3 => [200, 150] + ] + ]; + yield 'width variants' => [ + '', + '@^$@', + [ + 1 => [100, 75], + 2 => [200, 150] + ] + ]; + } + + /** + * @test + * @dataProvider imageWithSrcsetDataProvider + */ + public function imageWithSrcset(string $template, string $expected, array $expectedDimensions): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource('' . $template); + + $view->assignMultiple($this->createFileObjects()); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + + $matches = []; + preg_match($expected, $result, $matches); + + foreach ($expectedDimensions as $match => $imageDimension) { + list($width, $height) = getimagesize($this->instancePath . '/' . $matches[$match]); + self::assertEquals($imageDimension[0], $width, sprintf('width of image %d does not match expected width', $match)); + self::assertEquals($imageDimension[1], $height, sprintf('height of image %d does not match expected height', $match)); + } + } + + public static function pictureTagDataProvider(): \Generator + { + yield [ + [ + [ + 'srcset' => '400', + 'cropVariant' => 'wide', + 'media' => '(min-width: 1000px)', + 'sizes' => '100vw' + ], + [ + 'srcset' => '300', + 'cropVariant' => 'square', + 'media' => '(min-width: 700px)', + 'sizes' => '50vw' + ], + ], + '@^$@', + ]; + } + + /** + * @test + * @dataProvider pictureTagDataProvider + */ + public function pictureTag(array $breakpoints, string $expected): void + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplateSource(''); + $view->assignMultiple([ + ...$this->createFileObjects(), + 'breakpoints' => $breakpoints + ]); + $result = $view->render(); + self::assertMatchesRegularExpression($expected, $result); + } +} diff --git a/Tests/Functional/ViewHelperTestCase.php b/Tests/Functional/ViewHelperTestCase.php new file mode 100644 index 0000000..f5446e3 --- /dev/null +++ b/Tests/Functional/ViewHelperTestCase.php @@ -0,0 +1,44 @@ +get(ResourceFactory::class); + + // Create file record from existing test file + $variables['file'] = $resourceFactory->retrieveFileOrFolderObject( + 'EXT:sms_responsive_images/Tests/Functional/Fixtures/ImageViewHelperTest.png' + ); + + // Create file reference with cropping information + // Based on 400x300 dimensions + $cropVariantCollection = new CropVariantCollection([ + new CropVariant('default', 'Default', Area::createEmpty()), + new CropVariant('square', 'Square', new Area(0.125, 0, 0.75, 1)), + new CropVariant('wide', 'Wide', new Area(0, 1 / 6, 1, 2 / 3)), + ]); + $variables['fileReference'] = $resourceFactory->createFileReferenceObject([ + 'crop' => (string) $cropVariantCollection, + 'uid_local' => $variables['file']->getUid(), + 'alternative' => '', + 'title' => '' + ]); + + return $variables; + } +} diff --git a/composer.json b/composer.json index e4a8d5d..f3fb07b 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "require-dev": { "typo3/testing-framework": "^7.0 || ^6.0 || dev-main", "squizlabs/php_codesniffer": "^3.0", - "editorconfig-checker/editorconfig-checker": "^10.0" + "editorconfig-checker/editorconfig-checker": "^10.0", + "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": "^0.1.2" }, "autoload": { "psr-4": { @@ -39,7 +40,8 @@ "bin-dir": ".Build/bin", "allow-plugins": { "typo3/class-alias-loader": true, - "typo3/cms-composer-installers": true + "typo3/cms-composer-installers": true, + "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": true } }, "extra": { @@ -57,10 +59,19 @@ "lint:php": "phpcs --standard=PSR2 --extensions=php --ignore=.Build,Tests,ext_emconf.php .", "lint:editorconfig": "ec .", - "test": "phpunit -c Build/Testing/UnitTests.xml", + "test": [ + "@test:unit", + "@test:functional" + ], + "test:unit": "phpunit -c Build/Testing/UnitTests.xml", + "test:functional": "phpunit -c Build/Testing/FunctionalTests.xml", "prepare-release": [ "rm -r .github .ecrc .editorconfig .gitattributes Tests" ], - "render-documentation": "docker-compose -f Build/Documentation/docker-compose.yml run --rm t3docmake" + "render-documentation": "docker-compose -f Build/Documentation/docker-compose.yml run --rm t3docmake", + + "post-autoload-dump": [ + "TYPO3\\TestingFramework\\Composer\\ExtensionTestEnvironment::prepare" + ] } } From 81edad84af7f7e72f55e4b08bd086c2a30a02702 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Mon, 20 Mar 2023 17:16:46 +0100 Subject: [PATCH 14/15] [TASK] Update github actions --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb38b8a..3c4310e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: tomasnorre/typo3-upload-ter@v2 with: api-token: ${{ secrets.TYPO3_API_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ced5450..dd2e2c2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Validate composer.json @@ -16,7 +16,7 @@ jobs: - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.composer/cache key: composer @@ -54,7 +54,7 @@ jobs: name: Unit Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP @@ -65,7 +65,7 @@ jobs: - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.composer/cache key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} @@ -91,7 +91,7 @@ jobs: name: Functional Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP @@ -106,7 +106,7 @@ jobs: - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.composer/cache key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} From 9025b7ec1049ff0091dc6f6069967e18f6f80f09 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Mon, 20 Mar 2023 17:26:16 +0100 Subject: [PATCH 15/15] [TASK] Document final classes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 33a4ea7..471355d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ For further instructions, please take a look at the full documentation. * Support for TYPO3 9.5 and PHP < 7.4 is gone. * If you still include `constants.ts` and `setup.ts` manually in your TypoScript configuration, these files have now been renamed to `constants.typoscript` and `setup.typoscript`. +* Since TYPO3 v12 +[has declared all ViewHelper classes as `final`](https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/11.5/Important-95298-FluidViewhelpersWillBeDeclaredFinalInV12.html), +this has been applied to the responsive images ViewHelpers as well. ## Updating from 1.x