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 14f9fd0..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 @@ -34,25 +34,27 @@ jobs: run: composer lint:editorconfig - test: + test-unit: runs-on: ubuntu-latest strategy: max-parallel: 2 matrix: - php-versions: [7.4, 7.3] - typo3-versions: [11, 10, 9] + php-versions: [8.2, 8.1, 8.0] + typo3-versions: [12, 11] exclude: - - php-versions: 7.3 - typo3-versions: 11 + - php-versions: 8.0 + typo3-versions: 12 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 }} + name: Unit Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP @@ -63,7 +65,48 @@ 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 }} + + - + name: Install composer dependencies + run: composer require typo3/minimal "^${{ matrix.typo3-versions }}" --prefer-dist --no-progress --no-suggest + + - + 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@v3 + + - + 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@v3 with: path: ~/.composer/cache key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} @@ -73,5 +116,5 @@ jobs: run: composer require typo3/minimal "^${{ matrix.typo3-versions }}" --prefer-dist --no-progress --no-suggest - - name: Automated Testing - run: composer test + 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/Build/Testing/UnitTests.xml b/Build/Testing/UnitTests.xml new file mode 100644 index 0000000..18f450f --- /dev/null +++ b/Build/Testing/UnitTests.xml @@ -0,0 +1,28 @@ + + + + + ../../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/Classes/Utility/ResponsiveImagesUtility.php b/Classes/Utility/ResponsiveImagesUtility.php index 8a19fd7..d0757a2 100644 --- a/Classes/Utility/ResponsiveImagesUtility.php +++ b/Classes/Utility/ResponsiveImagesUtility.php @@ -1,5 +1,7 @@ imageService = $imageService; + } + + public function injectResponsiveImagesUtility(ResponsiveImagesUtility $responsiveImagesUtility): void { $this->responsiveImagesUtility = $responsiveImagesUtility; } - /** - * Initialize arguments. - */ - public function initializeArguments() + 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); + $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); + // phpcs:enable + $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( 'sizes', @@ -60,20 +87,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 +97,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 +117,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 +127,7 @@ public function render() $image = $this->imageService->getImage( $src, $this->arguments['image'], - $this->arguments['treatIdAsReference'] + (bool) $this->arguments['treatIdAsReference'] ); // Determine cropping settings @@ -132,7 +139,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 +160,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, @@ -164,12 +175,13 @@ public function render() $this->arguments['absolute'], $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], $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, @@ -181,19 +193,44 @@ public function render() $this->arguments['absolute'], $this->arguments['lazyload'], $this->arguments['ignoreFileExtensions'], - $this->arguments['placeholderSize'], + (int) $this->arguments['placeholderSize'], + $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'], + (int) $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..e19a527 100644 --- a/Classes/ViewHelpers/MediaViewHelper.php +++ b/Classes/ViewHelpers/MediaViewHelper.php @@ -1,33 +1,52 @@ imageService = $imageService; + } + + public function injectResponsiveImagesUtility(ResponsiveImagesUtility $responsiveImagesUtility): void { $this->responsiveImagesUtility = $responsiveImagesUtility; } - /** - * Initialize arguments. - */ - public function initializeArguments() + + 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, []); + $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); + // phpcs:enable + $this->registerArgument('srcset', 'mixed', 'Image sizes that should be rendered.', false); $this->registerArgument( 'sizes', @@ -59,40 +78,110 @@ public function initializeArguments() false, 'svg, gif' ); + } - if (version_compare(TYPO3_version, '10.3', '<')) { - $this->registerArgument( - 'fileExtension', - 'string', - 'Custom file extension to use for images' - ); + /** + * 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(); + } - $this->registerArgument( - 'loading', - 'string', - 'Native lazy-loading for images property. Can be "lazy", "eager" or "auto". Used on image files only.' + if (!$file instanceof FileInterface) { + throw new \UnexpectedValueException( + 'Supplied file object type ' . get_class($file) . ' must be FileInterface.', + 1678270961 // Original code: 1454252193 ); } + + 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); + + // 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(); } /** @@ -137,7 +226,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 ); @@ -171,6 +260,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( @@ -184,7 +276,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 ); @@ -214,9 +306,19 @@ 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; } + + 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/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/*' 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' 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 diff --git a/Tests/Functional/Fixtures/ImageViewHelperTest.png b/Tests/Functional/Fixtures/ImageViewHelperTest.png new file mode 100644 index 0000000..78ebd23 Binary files /dev/null and b/Tests/Functional/Fixtures/ImageViewHelperTest.png differ diff --git a/Tests/Functional/ImageViewHelperTest.php b/Tests/Functional/ImageViewHelperTest.php new file mode 100644 index 0000000..a06d59f --- /dev/null +++ b/Tests/Functional/ImageViewHelperTest.php @@ -0,0 +1,381 @@ + '', '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/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 9309303..f3fb07b 100644 --- a/composer.json +++ b/composer.json @@ -17,24 +17,32 @@ "issues": "https://github.com/sitegeist/sms-responsive-images/issues" }, "require": { - "typo3/cms-core": "^10.1 || ^11.4 || ^9.5" + "typo3/cms-core": "^12.2 || ^11.5 || ^10.4" }, "require-dev": { - "typo3/testing-framework": "^4.0 || ^6.0", + "typo3/testing-framework": "^7.0 || ^6.0 || dev-main", "squizlabs/php_codesniffer": "^3.0", - "editorconfig-checker/editorconfig-checker": "^10.0" - }, - "conflict": { - "schnitzler/fluid-styled-responsive-images": ">=0.0.1" + "editorconfig-checker/editorconfig-checker": "^10.0", + "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": "^0.1.2" }, "autoload": { "psr-4": { "Sitegeist\\ResponsiveImages\\": "Classes/" } }, + "autoload-dev": { + "psr-4": { + "Sitegeist\\ResponsiveImages\\Tests\\": "Tests/" + } + }, "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, + "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": true + } }, "extra": { "typo3/cms": { @@ -51,10 +59,19 @@ "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": [ + "@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" + ] } } 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' => '' 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 @@