From 464ef9cbc08b016d664c396b87973d8a521ae106 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Wed, 28 Aug 2024 14:30:58 +0200 Subject: [PATCH 01/22] [TASK] adjust responsibility --- composer.json | 10 +++++++++- ext_emconf.php | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 8ec5eeb..86c5790 100644 --- a/composer.json +++ b/composer.json @@ -6,9 +6,17 @@ "license": ["GPL-2.0-or-later"], "keywords": ["typo3", "typo3-extension", "fluid", "typo3-fluid", "components", "html", "template", "media", "image", "responsive images", "video", "audio"], "authors": [ + { + "name": "Benjamin Tammling", + "email": "benjamin.tammling@sitegeist.de" + }, + { + "name": "Ulrich Mathes", + "email": "mathes@sitegeist.de" + }, { "name": "Simon Praetorius", - "email": "praetorius@sitegeist.de" + "email": "moin@praetorius.me" } ], "support": { diff --git a/ext_emconf.php b/ext_emconf.php index 585fcb4..1c99a50 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -3,8 +3,8 @@ 'title' => 'Media Components', 'description' => 'Ready-to-use fluid components for embedding different media files', 'category' => 'fe', - 'author' => 'Simon Praetorius', - 'author_email' => 'praetorius@sitegeist.de', + 'author' => 'Benjamin Tammling, Ulrich Mathes, Simon Praetorius', + 'author_email' => 'benjamin.tammling@sitegeist.de, mathes@sitegeist.de, moin@praetorius.me', 'author_company' => 'sitegeist media solutions GmbH', 'state' => 'stable', 'uploadfolder' => false, From 8a87c48d601b03f6217e72f52efcf0e1842e4148 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 08:08:55 +0200 Subject: [PATCH 02/22] [TASK] update testing configuration to PHP 8 and TYPO3 13 --- .gitignore | 1 + Build/Scripts/runTests.sh | 65 ++++++++++++------------- Build/Testing/FunctionalTests.xml | 29 ++++++----- Build/Testing/UnitTests.xml | 41 +++++----------- Build/testing-docker/docker-compose.yml | 40 +++++++-------- composer.json | 13 ++--- 6 files changed, 85 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 68f37cd..f72b2a2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ composer.lock /var /Build/Testing/.phpunit.result.cache +/Build/Testing/.phpunit.cache/ /Build/testing-docker/.env diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 7cdd3ff..84b5491 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -39,7 +39,7 @@ Also used by github actions for test execution. Usage: $0 [options] [file] -No arguments: Run all unit tests with PHP 7.4 +No arguments: Run all unit tests with PHP 8.2 Options: -s <...> @@ -61,15 +61,14 @@ Options: - postgres: use postgres - sqlite: use sqlite - -p <7.4|8.0> + -p <8.3|8.2> Specifies the PHP minor version to be used - - 7.4 (default): use PHP 7.4 - - 8.0: use PHP 8.0 + - 8.3 (default): use PHP 8.3 + - 8.2: use PHP 8.2 - -t <10|11> + -t <13> Specifies the TYPO3 version to be used - - 10 (default): use TYPO3 10 - - 11: use TYPO3 11 + - 13 (default): use TYPO3 13 -e "" Only with -s functional|unit @@ -101,16 +100,16 @@ Options: Show this help. Examples: - # Run unit tests using PHP 7.4 + # Run unit tests using PHP 8.2 ./Build/Scripts/runTests.sh - # Run unit tests using PHP 7.3 - ./Build/Scripts/runTests.sh -p 7.3 + # Run unit tests using PHP 8.3 + ./Build/Scripts/runTests.sh -p 8.3 EOF # Test if docker-compose exists, else exit out with error -if ! type "docker-compose" > /dev/null; then - echo "This script relies on docker and docker-compose. Please install" >&2 +if ! type "docker" > /dev/null; then + echo "This script relies on docker and docker compose. Please install" >&2 exit 1 fi @@ -131,8 +130,8 @@ else fi TEST_SUITE="unit" DBMS="mariadb" -PHP_VERSION="7.4" -TYPO3_VERSION="10" +PHP_VERSION="8.3" +TYPO3_VERSION="13" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 EXTRA_TEST_OPTIONS="" @@ -214,41 +213,41 @@ fi case ${TEST_SUITE} in composerInstall) setUpDockerComposeDotEnv - docker-compose run composer_install + docker compose run composer_install SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; composerInstallMax) setUpDockerComposeDotEnv - docker-compose run composer_install_max + docker compose run composer_install_max SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; composerInstallMin) setUpDockerComposeDotEnv - docker-compose run composer_install_min + docker compose run composer_install_min SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; composerValidate) setUpDockerComposeDotEnv - docker-compose run composer_validate + docker compose run composer_validate SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; functional) setUpDockerComposeDotEnv case ${DBMS} in mariadb) - docker-compose run functional_mariadb10 + docker compose run functional_mariadb10 SUITE_EXIT_CODE=$? ;; mssql) - docker-compose run functional_mssql2019latest + docker compose run functional_mssql2019latest SUITE_EXIT_CODE=$? ;; postgres) - docker-compose run functional_postgres10 + docker compose run functional_postgres10 SUITE_EXIT_CODE=$? ;; sqlite) @@ -257,7 +256,7 @@ case ${TEST_SUITE} in # root if docker creates it. Thank you, docker. We create the path beforehand # to avoid permission issues. mkdir -p ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - docker-compose run functional_sqlite + docker compose run functional_sqlite SUITE_EXIT_CODE=$? ;; *) @@ -266,25 +265,25 @@ case ${TEST_SUITE} in echo "${HELP}" >&2 exit 1 esac - docker-compose down + docker compose down ;; lintPhp) setUpDockerComposeDotEnv - docker-compose run lint_php + docker compose run lint_php SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; lintEditorconfig) setUpDockerComposeDotEnv - docker-compose run lint_editorconfig + docker compose run lint_editorconfig SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; unit) setUpDockerComposeDotEnv - docker-compose run unit + docker compose run unit SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; update) # pull typo3/core-testing-*:latest versions of those ones that exist locally diff --git a/Build/Testing/FunctionalTests.xml b/Build/Testing/FunctionalTests.xml index ded3940..912c536 100644 --- a/Build/Testing/FunctionalTests.xml +++ b/Build/Testing/FunctionalTests.xml @@ -1,28 +1,27 @@ + - - - ../../Tests/Functional/ - - - - - - - + + + ../../Tests/Functional/ + + + + + + + diff --git a/Build/Testing/UnitTests.xml b/Build/Testing/UnitTests.xml index d71bb3f..5e69fad 100644 --- a/Build/Testing/UnitTests.xml +++ b/Build/Testing/UnitTests.xml @@ -1,30 +1,13 @@ - - - - ../../Tests/Unit/ - - - - - - - + + + + + ../../Tests/Unit/ + + + + + + + diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml index f68c9b2..f5df54d 100644 --- a/Build/testing-docker/docker-compose.yml +++ b/Build/testing-docker/docker-compose.yml @@ -1,4 +1,3 @@ -version: '2.3' services: mariadb10: image: mariadb:10 @@ -38,7 +37,7 @@ services: fi composer config allow-plugins.typo3/class-alias-loader true composer config allow-plugins.typo3/cms-composer-installers true - composer require typo3/minimal "^${TYPO3_VERSION}" --prefer-dist --no-progress --no-suggest + composer require typo3/minimal "^${TYPO3_VERSION}" --prefer-dist --no-progress composer install --no-progress --no-interaction; " composer_install_max: @@ -129,7 +128,7 @@ services: typo3DatabaseUsername: root typo3DatabasePassword: funcp typo3DatabaseHost: mariadb10 - working_dir: ${ROOT_DIR}/.Build + working_dir: ${ROOT_DIR} command: > /bin/sh -c " if [ ${SCRIPT_VERBOSE} -eq 1 ]; then @@ -143,13 +142,13 @@ services: php -v | grep '^PHP'; if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; fi " @@ -171,7 +170,7 @@ services: typo3DatabasePort: 1433 typo3DatabaseCharset: utf-8 typo3DatabaseHost: mssql2019latest - working_dir: ${ROOT_DIR}/.Build + working_dir: ${ROOT_DIR} command: > /bin/sh -c " if [ ${SCRIPT_VERBOSE} -eq 1 ]; then @@ -186,13 +185,13 @@ services: php -v | grep '^PHP'; if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; fi " @@ -212,7 +211,7 @@ services: typo3DatabaseUsername: ${HOST_USER} typo3DatabaseHost: postgres10 typo3DatabasePassword: funcp - working_dir: ${ROOT_DIR}/.Build + working_dir: ${ROOT_DIR} command: > /bin/sh -c " if [ ${SCRIPT_VERBOSE} -eq 1 ]; then @@ -226,13 +225,13 @@ services: php -v | grep '^PHP'; if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; fi " @@ -248,7 +247,7 @@ services: - ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,uid=${HOST_UID} environment: typo3DatabaseDriver: pdo_sqlite - working_dir: ${ROOT_DIR}/.Build + working_dir: ${ROOT_DIR} command: > /bin/sh -c " if [ ${SCRIPT_VERBOSE} -eq 1 ]; then @@ -257,13 +256,13 @@ services: php -v | grep '^PHP'; if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; fi " @@ -281,7 +280,8 @@ services: set -x fi php -v | grep '^PHP'; - .Build/bin/phpcs --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,Tests,ext_emconf.php . + .Build/bin/phpcs --version; + .Build/bin/phpcs -p --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,Tests,ext_emconf.php . " lint_editorconfig: @@ -298,7 +298,9 @@ services: set -x fi php -v | grep '^PHP'; - .Build/bin/ec -exclude .phpunit.result.cache . + echo -n 'ec version: '; + .Build/bin/ec -version; + .Build/bin/ec -exclude .phpunit.* . " unit: @@ -309,7 +311,7 @@ services: - ${HOST_HOME}:${HOST_HOME} - /etc/passwd:/etc/passwd:ro - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR}/.Build + working_dir: ${ROOT_DIR} command: > /bin/sh -c " if [ ${SCRIPT_VERBOSE} -eq 1 ]; then @@ -318,12 +320,12 @@ services: php -v | grep '^PHP' if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + .Build/bin/phpunit -c Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; fi " diff --git a/composer.json b/composer.json index 86c5790..bdc4554 100644 --- a/composer.json +++ b/composer.json @@ -23,14 +23,14 @@ "issues": "https://github.com/sitegeist/media-components/issues" }, "require": { - "php": ">=7.4.0 <=8.2", - "sitegeist/fluid-components": "^3.3.0", - "typo3fluid/fluid": "^2", + "php": "^8.2", + "sitegeist/fluid-components": "^3.7.0", + "typo3fluid/fluid": "<=2.14.1", "sitegeist/fluid-tagbuilder": "^1", - "typo3/cms-core": "^10.4 || ^11.4" + "typo3/cms-core": "13.2.*" }, "require-dev": { - "typo3/testing-framework": "^6.0 || ^7.0", + "typo3/testing-framework": "^8.2", "squizlabs/php_codesniffer": "^3.0", "editorconfig-checker/editorconfig-checker": "^10.0" }, @@ -61,9 +61,6 @@ } }, "scripts": { - "post-autoload-dump": [ - "TYPO3\\TestingFramework\\Composer\\ExtensionTestEnvironment::prepare" - ], "lint": [ "@lint:php", "@lint:editorconfig" From a937963a0f01630a8cb29951f1045b089641050a Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 08:15:14 +0200 Subject: [PATCH 03/22] [TASK] setup testing backend user from database via csv --- ...mponentTest.php => AbstractComponentTestCase.php} | 12 ++++++------ Tests/Functional/Fixtures/Databases/test_files.csv | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) rename Tests/Functional/{AbstractComponentTest.php => AbstractComponentTestCase.php} (76%) diff --git a/Tests/Functional/AbstractComponentTest.php b/Tests/Functional/AbstractComponentTestCase.php similarity index 76% rename from Tests/Functional/AbstractComponentTest.php rename to Tests/Functional/AbstractComponentTestCase.php index 8d9aee8..74d539a 100644 --- a/Tests/Functional/AbstractComponentTest.php +++ b/Tests/Functional/AbstractComponentTestCase.php @@ -6,24 +6,24 @@ use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; -abstract class AbstractComponentTest extends FunctionalTestCase +abstract class AbstractComponentTestCase extends FunctionalTestCase { - protected $initializeDatabase = true; + protected bool $initializeDatabase = true; - protected $testExtensionsToLoad = [ + protected array $testExtensionsToLoad = [ 'typo3conf/ext/fluid_components', 'typo3conf/ext/media_components' ]; - protected $pathsToLinkInTestInstance = [ - 'typo3_src/typo3conf/ext/media_components/Tests/Functional/Fixtures/Files' => 'fileadmin/test_files', + protected array $pathsToLinkInTestInstance = [ + 'typo3conf/ext/media_components/Tests/Functional/Fixtures/Files' => 'fileadmin/test_files', ]; public function setUp(): void { parent::setUp(); - $this->setUpBackendUserFromFixture(1); $this->importCSVDataSet(__DIR__ . '/Fixtures/Databases/test_files.csv'); + $this->setUpBackendUser(1); } protected function cleanUpTestResult($result = '') diff --git a/Tests/Functional/Fixtures/Databases/test_files.csv b/Tests/Functional/Fixtures/Databases/test_files.csv index 1a926ac..c653611 100644 --- a/Tests/Functional/Fixtures/Databases/test_files.csv +++ b/Tests/Functional/Fixtures/Databases/test_files.csv @@ -19,3 +19,7 @@ sys_file_metadata,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, pages,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,uid,pid,tstamp,crdate,cruser_id,deleted,hidden,starttime,endtime,fe_group,sorting,rowDescription,editlock,sys_language_uid,l10n_parent,l10n_source,l10n_state,t3_origuid,l10n_diffsource,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,title,slug,doktype,TSconfig,is_siteroot,php_tree_stop,url,shortcut,shortcut_mode,subtitle,layout,target,media,lastUpdated,keywords,cache_timeout,cache_tags,newUntil,no_search,module,extendToSubpages,nav_title,nav_hide,content_from_pid,mount_pid,mount_pid_ol,l18n_cfg,fe_login_mode,backend_layout,backend_layout_next_level,tsconfig_includes,categories ,1,0,1634541815,1634541815,1,0,0,0,0,0,0,NULL,0,0,0,0,NULL,0,NULL,1,1,31,31,1,Home,/,1,NULL,1,0,,0,0,,0,,0,0,NULL,0,,0,0,,0,,0,0,0,0,0,0,,,NULL,0 + +"be_users",,,,,,, +,"uid","pid","username","usergroup","deleted","admin","options" +,1,0,"admin",,0,1,0 From 56856baad89666ee83f25a02f93814d838abf733 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 08:16:04 +0200 Subject: [PATCH 04/22] [TASK] adopt database csv to TYPO3 v13 database structure --- Tests/Functional/Fixtures/Databases/test_files.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/Fixtures/Databases/test_files.csv b/Tests/Functional/Fixtures/Databases/test_files.csv index c653611..d32fbdb 100644 --- a/Tests/Functional/Fixtures/Databases/test_files.csv +++ b/Tests/Functional/Fixtures/Databases/test_files.csv @@ -17,8 +17,8 @@ sys_file_metadata,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, pages,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -,uid,pid,tstamp,crdate,cruser_id,deleted,hidden,starttime,endtime,fe_group,sorting,rowDescription,editlock,sys_language_uid,l10n_parent,l10n_source,l10n_state,t3_origuid,l10n_diffsource,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,title,slug,doktype,TSconfig,is_siteroot,php_tree_stop,url,shortcut,shortcut_mode,subtitle,layout,target,media,lastUpdated,keywords,cache_timeout,cache_tags,newUntil,no_search,module,extendToSubpages,nav_title,nav_hide,content_from_pid,mount_pid,mount_pid_ol,l18n_cfg,fe_login_mode,backend_layout,backend_layout_next_level,tsconfig_includes,categories -,1,0,1634541815,1634541815,1,0,0,0,0,0,0,NULL,0,0,0,0,NULL,0,NULL,1,1,31,31,1,Home,/,1,NULL,1,0,,0,0,,0,,0,0,NULL,0,,0,0,,0,,0,0,0,0,0,0,,,NULL,0 +,uid,pid,tstamp,crdate,deleted,hidden,starttime,endtime,fe_group,sorting,rowDescription,editlock,sys_language_uid,l10n_parent,l10n_source,l10n_state,l10n_diffsource,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,title,slug,doktype,TSconfig,is_siteroot,php_tree_stop,url,shortcut,shortcut_mode,subtitle,layout,target,media,lastUpdated,keywords,cache_timeout,cache_tags,newUntil,no_search,module,extendToSubpages,nav_title,nav_hide,content_from_pid,mount_pid,mount_pid_ol,l18n_cfg,backend_layout,backend_layout_next_level,tsconfig_includes,categories +,1,0,1634541815,1634541815,0,0,0,0,0,0,NULL,0,0,0,0,NULL,NULL,1,1,31,31,1,Home,/,1,NULL,1,0,,0,0,,0,,0,0,NULL,0,,0,0,,0,,0,0,0,0,0,,,NULL,0 "be_users",,,,,,, ,"uid","pid","username","usergroup","deleted","admin","options" From f72133ec9d6dddc253fa2b130e0daa1bdba5d917 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 08:17:49 +0200 Subject: [PATCH 05/22] [TASK] adopt functional testing to PHP 8 and PHPUnit 11 --- Tests/Functional/AudioComponentTest.php | 13 +++++++------ Tests/Functional/ImageComponentTest.php | 13 +++++++------ Tests/Functional/PictureComponentTest.php | 13 +++++++------ Tests/Functional/VideoComponentTest.php | 13 +++++++------ Tests/Unit/CropAreaTest.php | 17 +++++------------ Tests/Unit/SourceSetTest.php | 13 ++++--------- 6 files changed, 37 insertions(+), 45 deletions(-) diff --git a/Tests/Functional/AudioComponentTest.php b/Tests/Functional/AudioComponentTest.php index e9180b8..adfb294 100644 --- a/Tests/Functional/AudioComponentTest.php +++ b/Tests/Functional/AudioComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class AudioComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class AudioComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function audioComponentTestProvider(): array + public static function audioComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -30,10 +33,8 @@ public function audioComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider audioComponentTestProvider - */ + #[Test] + #[DataProvider('audioComponentTestProvider')] public function audioComponentTest(string $expectedResult, string $input) { $view = $this->getTestView($input); diff --git a/Tests/Functional/ImageComponentTest.php b/Tests/Functional/ImageComponentTest.php index a4858f1..e167473 100644 --- a/Tests/Functional/ImageComponentTest.php +++ b/Tests/Functional/ImageComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class ImageComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class ImageComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function imageComponentTestProvider(): array + public static function imageComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -34,10 +37,8 @@ public function imageComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider imageComponentTestProvider - */ + #[Test] + #[DataProvider('imageComponentTestProvider')] public function imageComponentTest(string $expectedResult, string $input) { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/PictureComponentTest.php b/Tests/Functional/PictureComponentTest.php index ab731fc..1953719 100644 --- a/Tests/Functional/PictureComponentTest.php +++ b/Tests/Functional/PictureComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class PictureComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class PictureComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function pictureComponentTestProvider(): array + public static function pictureComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -31,10 +34,8 @@ public function pictureComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider pictureComponentTestProvider - */ + #[Test] + #[DataProvider('pictureComponentTestProvider')] public function pictureComponentTest(string $expectedResult, string $input) { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/VideoComponentTest.php b/Tests/Functional/VideoComponentTest.php index 813b75f..00e6fdb 100644 --- a/Tests/Functional/VideoComponentTest.php +++ b/Tests/Functional/VideoComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class VideoComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class VideoComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function videoComponentTestProvider(): array + public static function videoComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -35,10 +38,8 @@ public function videoComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider videoComponentTestProvider - */ + #[Test] + #[DataProvider('videoComponentTestProvider')] public function videoComponentTestMinimal(string $expectedResult, string $input) { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Unit/CropAreaTest.php b/Tests/Unit/CropAreaTest.php index c560ec4..d5cf040 100644 --- a/Tests/Unit/CropAreaTest.php +++ b/Tests/Unit/CropAreaTest.php @@ -2,14 +2,13 @@ namespace Sitegeist\MediaComponents\Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use Sitegeist\MediaComponents\Domain\Model\CropArea; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; class CropAreaTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { - /** - * @test - */ + #[Test] public function testFromArray() { $cropArea = CropArea::fromArray(['x' => 0, 'y' => 0, 'width' => 1.0, 'height' => 1.0]); @@ -19,9 +18,7 @@ public function testFromArray() $this->assertEquals(['x' => 0, 'y' => 0, 'width' => 1.0, 'height' => 1.0], $cropArea->getArea()->asArray()); } - /** - * @test - */ + #[Test] public function testFromFloat() { $cropArea = CropArea::fromFloat(2.0); @@ -31,9 +28,7 @@ public function testFromFloat() $this->assertEquals(['x' => 0, 'y' => 0.25, 'width' => 1.0, 'height' => 0.5], $cropArea->getArea()->asArray()); } - /** - * @test - */ + #[Test] public function testFromInteger() { $cropArea = CropArea::fromInteger(4); @@ -43,9 +38,7 @@ public function testFromInteger() $this->assertEquals(['x' => 0, 'y' => 0.375, 'width' => 1.0, 'height' => 0.25], $cropArea->getArea()->asArray()); } - /** - * @test - */ + #[Test] public function testFromString() { $cropArea = CropArea::fromString(''); diff --git a/Tests/Unit/SourceSetTest.php b/Tests/Unit/SourceSetTest.php index dccf63c..ed1e3fc 100644 --- a/Tests/Unit/SourceSetTest.php +++ b/Tests/Unit/SourceSetTest.php @@ -2,13 +2,12 @@ namespace Sitegeist\MediaComponents\Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use Sitegeist\MediaComponents\Domain\Model\SourceSet; class SourceSetTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { - /** - * @test - */ + #[Test] public function testFromArray() { $sourceSet = SourceSet::fromArray(['10w', '20w', '30w']); @@ -17,9 +16,7 @@ public function testFromArray() $this->assertEquals(['10w', '20w', '30w'], $sourceSet->getSrcset()); } - /** - * @test - */ + #[Test] public function testFromInteger() { $sourceSet = SourceSet::fromInteger(200); @@ -28,9 +25,7 @@ public function testFromInteger() $this->assertEquals([200], $sourceSet->getSrcset()); } - /** - * @test - */ + #[Test] public function testFromString() { $sourceSet = SourceSet::fromString('10w,20w,30w'); From 3672149f1ae700b5f8a8db7783cc812424ed771f Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 11:34:59 +0200 Subject: [PATCH 06/22] [TASK] rector and docblock migration --- Build/Testing/FunctionalTestsBootstrap.php | 2 +- Build/Testing/UnitTestsBootstrap.php | 2 +- Classes/Domain/Model/CropArea.php | 16 +++--- Classes/Domain/Model/ImageSource.php | 55 ++++-------------- Classes/Domain/Model/SourceSet.php | 12 ++-- Classes/Domain/Model/Track.php | 56 ++----------------- Classes/Interfaces/ConstructibleFromArea.php | 5 +- Classes/Interfaces/ConstructibleFromFloat.php | 5 +- Classes/Interfaces/ConstructibleFromImage.php | 5 +- .../Image/CropVariantViewHelper.php | 4 +- .../Image/Modify/CropViewHelper.php | 2 +- .../Image/Modify/FormatViewHelper.php | 2 +- .../Image/Modify/ScaleViewHelper.php | 2 +- .../ViewHelpers/Image/SrcsetViewHelper.php | 6 +- .../Functional/AbstractComponentTestCase.php | 2 +- Tests/Functional/AudioComponentTest.php | 2 +- Tests/Functional/ImageComponentTest.php | 2 +- Tests/Functional/PictureComponentTest.php | 2 +- Tests/Functional/VideoComponentTest.php | 2 +- Tests/Unit/CropAreaTest.php | 8 +-- Tests/Unit/SourceSetTest.php | 6 +- 21 files changed, 55 insertions(+), 143 deletions(-) diff --git a/Build/Testing/FunctionalTestsBootstrap.php b/Build/Testing/FunctionalTestsBootstrap.php index 443197d..58b29f5 100644 --- a/Build/Testing/FunctionalTestsBootstrap.php +++ b/Build/Testing/FunctionalTestsBootstrap.php @@ -12,7 +12,7 @@ * The TYPO3 project - inspiring people to share! */ -call_user_func(function () { +call_user_func(function (): void { $testbase = new \TYPO3\TestingFramework\Core\Testbase(); $testbase->defineOriginalRootPath(); $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); diff --git a/Build/Testing/UnitTestsBootstrap.php b/Build/Testing/UnitTestsBootstrap.php index b3d535e..42acad0 100644 --- a/Build/Testing/UnitTestsBootstrap.php +++ b/Build/Testing/UnitTestsBootstrap.php @@ -12,7 +12,7 @@ * The TYPO3 project - inspiring people to share! */ -call_user_func(function () { +call_user_func(function (): void { $testbase = new \TYPO3\TestingFramework\Core\Testbase(); // These if's are for core testing (package typo3/cms) only. cms-composer-installer does diff --git a/Classes/Domain/Model/CropArea.php b/Classes/Domain/Model/CropArea.php index dee2401..518480c 100644 --- a/Classes/Domain/Model/CropArea.php +++ b/Classes/Domain/Model/CropArea.php @@ -14,7 +14,7 @@ class CropArea implements ConstructibleFromArray, ConstructibleFromFloat, ConstructibleFromInteger, ConstructibleFromString, ConstructibleFromArea { - protected $area; + protected ?Area $area = null; public function __construct(Area $area = null) { @@ -26,37 +26,37 @@ public function getArea(): Area return $this->area; } - public static function fromArea(Area $area) + public static function fromArea(Area $area): static { return new static($area); } - public static function fromArray(array $config) + public static function fromArray(array $config): ?object { return new static(Area::createFromConfiguration($config)); } - public static function fromFloat(float $ratio) + public static function fromFloat(float $ratio): static { $ratio = new Ratio('', '', $ratio); return new static(Area::createEmpty()->applyRatioRestriction($ratio)); } - public static function fromInteger(int $ratio) + public static function fromInteger(int $ratio): object { $ratio = new Ratio('', '', $ratio); return new static(Area::createEmpty()->applyRatioRestriction($ratio)); } - public static function fromString(string $ratio) + public static function fromString(string $ratio): static { $area = Area::createEmpty(); if (substr_count($ratio, ':') === 1) { - list($x, $y) = GeneralUtility::trimExplode(':', $ratio); + [$x, $y] = GeneralUtility::trimExplode(':', $ratio); $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); } elseif (substr_count($ratio, '/') === 1) { - list($x, $y) = GeneralUtility::trimExplode('/', $ratio); + [$x, $y] = GeneralUtility::trimExplode('/', $ratio); $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); } diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index 96624d7..469e919 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -27,57 +27,28 @@ class ImageSource implements ConstructibleFromInteger, ConstructibleFromString { - /** - * Original image - * - * @var Image - */ - protected $originalImage; - /** * Image with applied modifications - * - * @var Image */ - protected $image; + protected ?Image $image = null; - /** - * @var float - */ - protected $scale = 1.0; + protected ?ImageService $imageService = null; - /** - * @var CropArea - */ - protected $crop; + protected float $scale = 1.0; - /** - * @var string - */ - protected $format; + protected ?CropArea $crop = null; - /** - * @var string - */ - protected $media; + protected string $format = ''; - /** - * @var SourceSet - */ - protected $srcset; + protected string $media = ''; - /** - * @var string - */ - protected $sizes; + protected ?SourceSet $srcset = null; - /** - * @var ImageService - */ - protected $imageService; + protected string $sizes = ''; - public function __construct(Image $originalImage = null) - { + public function __construct( + protected $originalImage = null + ) { $this->imageService = GeneralUtility::makeInstance(ImageService::class); $this ->setOriginalImage($originalImage) @@ -90,7 +61,7 @@ public static function fromArray(array $value): ImageSource try { $image = $argumentConverter->convertValueToType($value['originalImage'], Image::class); - } catch (\SMS\FluidComponents\Exception\InvalidArgumentException $e) { + } catch (\SMS\FluidComponents\Exception\InvalidArgumentException) { // TODO better error handling here: // Image is not required, but invalid combination of parameters should // be catched @@ -259,8 +230,6 @@ public function setSrcset(?SourceSet $srcset): self /** * Use public url of image as string representation of image objects - * - * @return string */ public function __toString(): string { diff --git a/Classes/Domain/Model/SourceSet.php b/Classes/Domain/Model/SourceSet.php index 9f9a60d..d277fda 100644 --- a/Classes/Domain/Model/SourceSet.php +++ b/Classes/Domain/Model/SourceSet.php @@ -9,9 +9,7 @@ class SourceSet implements ConstructibleFromArray, ConstructibleFromInteger, ConstructibleFromString { - protected $srcset = []; - - public function __construct(array $srcset = []) + public function __construct(protected array $srcset = []) { $this->setSrcset($srcset); } @@ -25,7 +23,7 @@ public function getSrcsetAndWidths(int $baseWidth): array { $useAbsoluteWidth = false; foreach ($this->srcset as $widthDescriptor) { - if (substr($widthDescriptor, -1) !== 'x') { + if (!str_ends_with((string) $widthDescriptor, 'x')) { $useAbsoluteWidth = true; break; } @@ -33,11 +31,11 @@ public function getSrcsetAndWidths(int $baseWidth): array $srcset = []; foreach ($this->srcset as $widthDescriptor) { - $srcsetMode = substr($widthDescriptor, -1); + $srcsetMode = substr((string) $widthDescriptor, -1); switch ($srcsetMode) { // Relative dimensions case 'x': - $candidateWidth = (int) ($baseWidth * (float) substr($widthDescriptor, 0, -1)); + $candidateWidth = (int) ($baseWidth * (float) substr((string) $widthDescriptor, 0, -1)); if ($useAbsoluteWidth === true) { $widthDescriptor = $candidateWidth . 'w'; } @@ -45,7 +43,7 @@ public function getSrcsetAndWidths(int $baseWidth): array // Absolute dimensions case 'w': - $candidateWidth = (int) substr($widthDescriptor, 0, -1); + $candidateWidth = (int) substr((string) $widthDescriptor, 0, -1); break; default: diff --git a/Classes/Domain/Model/Track.php b/Classes/Domain/Model/Track.php index 2af0130..65f200d 100644 --- a/Classes/Domain/Model/Track.php +++ b/Classes/Domain/Model/Track.php @@ -12,101 +12,55 @@ class Track extends File /** * Type of file to differentiate implementations in Fluid templates - * - * @var string */ - protected $type = 'Track'; + protected string$type = 'Track'; - /** - * Default flag for track - * - * @var int - */ - protected $default = 0; + protected int $default = 0; - /** - * Kind of track - * - * @var string - */ - protected $kind = 'subtitles'; + protected string $kind = 'subtitles'; - /** - * Label for the track - * - * @var string - */ - protected $label = ''; + protected string $label = ''; - /** - * Language of track - * - * @var string - */ - protected $srclang = 'en'; + protected string $srclang = 'en'; - /** - * @return int - */ public function getDefault(): int { return $this->default; } - /** - * @param int $default - */ public function setDefault(int $default): self { $this->default = $default; return $this; } - /** - * @return string - */ public function getKind(): string { return $this->kind; } - /** - * @param string $kind - */ public function setKind(string $kind): self { $this->kind = $kind; return $this; } - /** - * @return string - */ public function getLabel(): string { return $this->label; } - /** - * @param string $label - */ public function setLabel(string $label): self { $this->label = $label; return $this; } - /** - * @return string - */ public function getSrclang(): string { return $this->srclang; } - /** - * @param string $srclang - */ public function setSrclang(string $srclang): self { $this->srclang = $srclang; diff --git a/Classes/Interfaces/ConstructibleFromArea.php b/Classes/Interfaces/ConstructibleFromArea.php index 56d106d..e24ebc7 100644 --- a/Classes/Interfaces/ConstructibleFromArea.php +++ b/Classes/Interfaces/ConstructibleFromArea.php @@ -13,9 +13,6 @@ interface ConstructibleFromArea { /** * Creates an instance of the class based on the provided Area object - * - * @param Area $value - * @return object */ - public static function fromArea(Area $value); + public static function fromArea(Area $value): object; } diff --git a/Classes/Interfaces/ConstructibleFromFloat.php b/Classes/Interfaces/ConstructibleFromFloat.php index d05c71d..d8ee614 100644 --- a/Classes/Interfaces/ConstructibleFromFloat.php +++ b/Classes/Interfaces/ConstructibleFromFloat.php @@ -11,9 +11,6 @@ interface ConstructibleFromFloat { /** * Creates an instance of the class based on the provided float - * - * @param float $value - * @return object */ - public static function fromFloat(float $value); + public static function fromFloat(float $value): object; } diff --git a/Classes/Interfaces/ConstructibleFromImage.php b/Classes/Interfaces/ConstructibleFromImage.php index 357b2e8..5836cf3 100644 --- a/Classes/Interfaces/ConstructibleFromImage.php +++ b/Classes/Interfaces/ConstructibleFromImage.php @@ -9,9 +9,6 @@ interface ConstructibleFromImage { /** * Creates an instance of the class based on the provided Image object - * - * @param Image $value - * @return object */ - public static function fromImage(Image $value); + public static function fromImage(Image $value): object; } diff --git a/Classes/ViewHelpers/Image/CropVariantViewHelper.php b/Classes/ViewHelpers/Image/CropVariantViewHelper.php index 6c43004..2643956 100644 --- a/Classes/ViewHelpers/Image/CropVariantViewHelper.php +++ b/Classes/ViewHelpers/Image/CropVariantViewHelper.php @@ -12,7 +12,7 @@ class CropVariantViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractVi { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('image', FileInterface::class, 'FAL file'); $this->registerArgument('name', 'string', 'name of the crop variant that should be used', false, 'default'); @@ -23,7 +23,7 @@ public static function renderStatic( \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ): Area { - $arguments['image'] = $arguments['image'] ?? $renderChildrenClosure(); + $arguments['image'] ??= $renderChildrenClosure(); $cropVariantCollection = CropVariantCollection::create((string)$arguments['image']->getProperty('crop')); return $cropVariantCollection->getCropArea($arguments['name']); } diff --git a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php index e085f43..a651b4a 100644 --- a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php @@ -12,7 +12,7 @@ class CropViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelpe { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('crop', CropArea::class, 'Crop area'); diff --git a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php index 623ac3e..6f15f75 100644 --- a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php @@ -11,7 +11,7 @@ class FormatViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHel { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('format', 'string', 'File format that should be used'); diff --git a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php index c772277..91cc68d 100644 --- a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php @@ -11,7 +11,7 @@ class ScaleViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelp { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('height', 'integer', 'Desired image height'); diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index db00844..b240d2b 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -14,7 +14,7 @@ class SrcsetViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHel { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('srcset', SourceSet::class, 'srcset definition'); @@ -26,7 +26,7 @@ public static function renderStatic( \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ): string { - $arguments['imageSource'] = $arguments['imageSource'] ?? $renderChildrenClosure(); + $arguments['imageSource'] ??= $renderChildrenClosure(); if ($arguments['srcset'] instanceof SourceSet) { return self::generateSrcsetString($arguments['imageSource'], $arguments['srcset'], $arguments['base']); @@ -38,7 +38,7 @@ public static function renderStatic( public static function generateSrcsetString(ImageSource $imageSource, SourceSet $srcset, ImageSource $base = null): string { $output = []; - $base = $base ?? $imageSource; + $base ??= $imageSource; $widths = $srcset->getSrcsetAndWidths($base->getWidth()); $imageService = GeneralUtility::makeInstance(ImageService::class); $localImageSource = clone $imageSource; diff --git a/Tests/Functional/AbstractComponentTestCase.php b/Tests/Functional/AbstractComponentTestCase.php index 74d539a..bfcd3a2 100644 --- a/Tests/Functional/AbstractComponentTestCase.php +++ b/Tests/Functional/AbstractComponentTestCase.php @@ -28,7 +28,7 @@ public function setUp(): void protected function cleanUpTestResult($result = '') { - return trim(preg_replace('/\\>\\s+\\<', str_replace("\n", '', (string)$result))); + return trim((string) preg_replace('/\\>\\s+\\<', str_replace("\n", '', (string)$result))); } protected function getTestView($html = '') diff --git a/Tests/Functional/AudioComponentTest.php b/Tests/Functional/AudioComponentTest.php index adfb294..284c764 100644 --- a/Tests/Functional/AudioComponentTest.php +++ b/Tests/Functional/AudioComponentTest.php @@ -35,7 +35,7 @@ public static function audioComponentTestProvider(): array #[Test] #[DataProvider('audioComponentTestProvider')] - public function audioComponentTest(string $expectedResult, string $input) + public function audioComponentTest(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/ImageComponentTest.php b/Tests/Functional/ImageComponentTest.php index e167473..b4c1f29 100644 --- a/Tests/Functional/ImageComponentTest.php +++ b/Tests/Functional/ImageComponentTest.php @@ -39,7 +39,7 @@ public static function imageComponentTestProvider(): array #[Test] #[DataProvider('imageComponentTestProvider')] - public function imageComponentTest(string $expectedResult, string $input) { + public function imageComponentTest(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/PictureComponentTest.php b/Tests/Functional/PictureComponentTest.php index 1953719..43b677f 100644 --- a/Tests/Functional/PictureComponentTest.php +++ b/Tests/Functional/PictureComponentTest.php @@ -36,7 +36,7 @@ public static function pictureComponentTestProvider(): array #[Test] #[DataProvider('pictureComponentTestProvider')] - public function pictureComponentTest(string $expectedResult, string $input) { + public function pictureComponentTest(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/VideoComponentTest.php b/Tests/Functional/VideoComponentTest.php index 00e6fdb..80895d4 100644 --- a/Tests/Functional/VideoComponentTest.php +++ b/Tests/Functional/VideoComponentTest.php @@ -40,7 +40,7 @@ public static function videoComponentTestProvider(): array #[Test] #[DataProvider('videoComponentTestProvider')] - public function videoComponentTestMinimal(string $expectedResult, string $input) { + public function videoComponentTestMinimal(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Unit/CropAreaTest.php b/Tests/Unit/CropAreaTest.php index d5cf040..991789e 100644 --- a/Tests/Unit/CropAreaTest.php +++ b/Tests/Unit/CropAreaTest.php @@ -9,7 +9,7 @@ class CropAreaTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { #[Test] - public function testFromArray() + public function testFromArray(): void { $cropArea = CropArea::fromArray(['x' => 0, 'y' => 0, 'width' => 1.0, 'height' => 1.0]); @@ -19,7 +19,7 @@ public function testFromArray() } #[Test] - public function testFromFloat() + public function testFromFloat(): void { $cropArea = CropArea::fromFloat(2.0); @@ -29,7 +29,7 @@ public function testFromFloat() } #[Test] - public function testFromInteger() + public function testFromInteger(): void { $cropArea = CropArea::fromInteger(4); @@ -39,7 +39,7 @@ public function testFromInteger() } #[Test] - public function testFromString() + public function testFromString(): void { $cropArea = CropArea::fromString(''); diff --git a/Tests/Unit/SourceSetTest.php b/Tests/Unit/SourceSetTest.php index ed1e3fc..d1dd7d8 100644 --- a/Tests/Unit/SourceSetTest.php +++ b/Tests/Unit/SourceSetTest.php @@ -8,7 +8,7 @@ class SourceSetTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { #[Test] - public function testFromArray() + public function testFromArray(): void { $sourceSet = SourceSet::fromArray(['10w', '20w', '30w']); @@ -17,7 +17,7 @@ public function testFromArray() } #[Test] - public function testFromInteger() + public function testFromInteger(): void { $sourceSet = SourceSet::fromInteger(200); @@ -26,7 +26,7 @@ public function testFromInteger() } #[Test] - public function testFromString() + public function testFromString(): void { $sourceSet = SourceSet::fromString('10w,20w,30w'); From edc336f20569c9c1eda9b0073e6028d650f600eb Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 08:26:25 +0200 Subject: [PATCH 07/22] [TASK] remove PreloadViewHelper as parent class is final now we have to call CssViewHelper directly --- Classes/ViewHelpers/PreloadViewHelper.php | 12 ------------ Resources/Private/Components/Image/Image.html | 2 +- .../Private/Components/Picture/Source/Source.html | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 Classes/ViewHelpers/PreloadViewHelper.php diff --git a/Classes/ViewHelpers/PreloadViewHelper.php b/Classes/ViewHelpers/PreloadViewHelper.php deleted file mode 100644 index d10e3ac..0000000 --- a/Classes/ViewHelpers/PreloadViewHelper.php +++ /dev/null @@ -1,12 +0,0 @@ - - - Date: Mon, 2 Sep 2024 11:39:06 +0200 Subject: [PATCH 08/22] [TASK] more readable ext_localconf --- ext_localconf.php | 64 +++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/ext_localconf.php b/ext_localconf.php index 2afc2b6..fc548b2 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,30 +1,40 @@ addConversionInterface( - 'float', - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromFloat::class, - 'fromFloat' - ); - $componentArgumentConverter->addConversionInterface( - 'double', - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromFloat::class, - 'fromFloat' - ); - $componentArgumentConverter->addConversionInterface( - \TYPO3\CMS\Core\Imaging\ImageManipulation\Area::class, - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromArea::class, - 'fromArea' - ); - $componentArgumentConverter->addConversionInterface( - \SMS\FluidComponents\Domain\Model\Image::class, - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromImage::class, - 'fromImage' - ); -}); +defined('TYPO3') or die(); + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['namespaces']['Sitegeist\\MediaComponents\\Components'] = + ExtensionManagementUtility::extPath('media_components', 'Resources/Private/Components'); + +$componentArgumentConverter = GeneralUtility::makeInstance( + ComponentArgumentConverter::class +); +$componentArgumentConverter->addConversionInterface( + 'float', + ConstructibleFromFloat::class, + 'fromFloat' +); +$componentArgumentConverter->addConversionInterface( + 'double', + ConstructibleFromFloat::class, + 'fromFloat' +); +$componentArgumentConverter->addConversionInterface( + Area::class, + ConstructibleFromArea::class, + 'fromArea' +); +$componentArgumentConverter->addConversionInterface( + Image::class, + ConstructibleFromImage::class, + 'fromImage' +); From eaf98bb6906d70adf7755f089b71902e19139c6f Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 11:37:41 +0200 Subject: [PATCH 09/22] [TASK] round processing dimensions not to lose precision Implicit conversion from float to int loses precision --- Classes/Domain/Model/ImageSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index 469e919..c5b08d3 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -250,8 +250,8 @@ protected function processImage(): void } $processingInstructions = [ - 'width' => $originalImage->getWidth() * $this->getScale(), - 'height' => $originalImage->getHeight() * $this->getScale(), + 'width' => round( $originalImage->getWidth() * $this->getScale() ), + 'height' => round( $originalImage->getHeight() * $this->getScale() ), 'fileExtension' => $this->getFormat(), 'crop' => $crop ]; From 534e295dac7b26d5463326c5aa2d599120619cf7 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 15:26:57 +0200 Subject: [PATCH 10/22] [BUGFIX] do not return srcset fallback url if no srcset is given results in src=srcset which does not help --- Classes/ViewHelpers/Image/SrcsetViewHelper.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index b240d2b..65dbbf9 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -26,13 +26,12 @@ public static function renderStatic( \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ): string { - $arguments['imageSource'] ??= $renderChildrenClosure(); - - if ($arguments['srcset'] instanceof SourceSet) { - return self::generateSrcsetString($arguments['imageSource'], $arguments['srcset'], $arguments['base']); - } else { - return $arguments['imageSource']->getPublicUrl(); + if (!$arguments['srcset'] instanceof SourceSet) { + return ''; } + + $arguments['imageSource'] ??= $renderChildrenClosure(); + return self::generateSrcsetString($arguments['imageSource'], $arguments['srcset'], $arguments['base']); } public static function generateSrcsetString(ImageSource $imageSource, SourceSet $srcset, ImageSource $base = null): string From ac5729bf823c98f2c94c59b3fd5128bf76da8300 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 15:27:53 +0200 Subject: [PATCH 11/22] [TASK] only set imageSource values from array when given --- Classes/Domain/Model/ImageSource.php | 42 ++++++++++++++++------------ Classes/Domain/Model/Track.php | 2 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index c5b08d3..b83cfb3 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -47,12 +47,10 @@ class ImageSource implements protected string $sizes = ''; public function __construct( - protected $originalImage = null + protected ?Image $originalImage = null ) { $this->imageService = GeneralUtility::makeInstance(ImageService::class); - $this - ->setOriginalImage($originalImage) - ->setCrop(new CropArea); + $this->setCrop(new CropArea); } public static function fromArray(array $value): ImageSource @@ -68,20 +66,28 @@ public static function fromArray(array $value): ImageSource } $imageSource = new static($image); - $imageSource - ->setScale($value['scale'] ?? null) - ->setFormat($value['format'] ?? null) - ->setMedia($value['media'] ?? null) - ->setSizes($value['sizes'] ?? null); - if ($value['crop'] || $value['srcset']) { + if (isset($value['scale'])) { + $imageSource->setScale((float) $value['scale']); + } + if (isset($value['format'])) { + $imageSource->setFormat((string) $value['format']); + } + if (isset($value['media'])) { + $imageSource->setMedia((string) $value['media']); + } + if (isset($value['scale'])) { + $imageSource->setSizes((string) $value['sizes']); + } + + if (isset($value['crop']) || isset($value['srcset'])) { $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); - if ($value['crop']) { + if (isset($value['crop'])) { $imageSource->setCrop($argumentConverter->convertValueToType($value['crop'], CropArea::class)); } - if ($value['srcset']) { + if (isset($value['srcset'])) { $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); } } @@ -119,7 +125,7 @@ public function getOriginalImage(): ?Image return $this->originalImage; } - public function setOriginalImage(?Image $image): self + public function setOriginalImage(Image $image): self { $this->originalImage = $image; return $this; @@ -130,7 +136,7 @@ public function getScale(): ?float return $this->scale; } - public function setScale(?float $scale): self + public function setScale(float $scale): self { $this->scale = $scale; return $this; @@ -141,7 +147,7 @@ public function getFormat(): ?string return $this->format; } - public function setFormat(?string $format): self + public function setFormat(string $format): self { $this->format = $format; return $this; @@ -200,7 +206,7 @@ public function getMedia(): ?string return $this->media; } - public function setMedia(?string $media): self + public function setMedia(string $media): self { $this->media = $media; return $this; @@ -211,7 +217,7 @@ public function getSizes(): ?string return $this->sizes; } - public function setSizes(?string $sizes): self + public function setSizes(string $sizes): self { $this->sizes = $sizes; return $this; @@ -222,7 +228,7 @@ public function getSrcset(): ?SourceSet return $this->srcset; } - public function setSrcset(?SourceSet $srcset): self + public function setSrcset(SourceSet $srcset): self { $this->srcset = $srcset; return $this; diff --git a/Classes/Domain/Model/Track.php b/Classes/Domain/Model/Track.php index 65f200d..79dc6f1 100644 --- a/Classes/Domain/Model/Track.php +++ b/Classes/Domain/Model/Track.php @@ -13,7 +13,7 @@ class Track extends File /** * Type of file to differentiate implementations in Fluid templates */ - protected string$type = 'Track'; + protected string $type = 'Track'; protected int $default = 0; From cdad1c7481cbc9c4fe2a1ef37a0a469d17c64790 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 15:42:11 +0200 Subject: [PATCH 12/22] [TASK] refresh PictureComponentTest processed images hash --- Tests/Functional/PictureComponentTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/PictureComponentTest.php b/Tests/Functional/PictureComponentTest.php index 43b677f..9808f61 100644 --- a/Tests/Functional/PictureComponentTest.php +++ b/Tests/Functional/PictureComponentTest.php @@ -14,11 +14,11 @@ public static function pictureComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ - '', + '', '' ], 'All data provided' => [ - 'Alt text', + 'Alt text', ' Date: Mon, 2 Sep 2024 15:42:36 +0200 Subject: [PATCH 13/22] [TASK] use php8.2 as default for tests --- Build/Scripts/runTests.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 84b5491..6254c2b 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -61,10 +61,11 @@ Options: - postgres: use postgres - sqlite: use sqlite - -p <8.3|8.2> + -p <8.3|8.2|8.1> Specifies the PHP minor version to be used - - 8.3 (default): use PHP 8.3 - - 8.2: use PHP 8.2 + - 8.3: use PHP 8.3 + - 8.2 (default): use PHP 8.2 + - 8.1: use PHP 8.3 -t <13> Specifies the TYPO3 version to be used @@ -103,8 +104,8 @@ Examples: # Run unit tests using PHP 8.2 ./Build/Scripts/runTests.sh - # Run unit tests using PHP 8.3 - ./Build/Scripts/runTests.sh -p 8.3 + # Run unit tests using PHP 8.1 + ./Build/Scripts/runTests.sh -p 8.1 EOF # Test if docker-compose exists, else exit out with error @@ -130,7 +131,7 @@ else fi TEST_SUITE="unit" DBMS="mariadb" -PHP_VERSION="8.3" +PHP_VERSION="8.2" TYPO3_VERSION="13" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 From ac1b3f93a3800e7d8f8b09fc3188a8188d47628a Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 15:41:30 +0200 Subject: [PATCH 14/22] [TASK] linting errors --- Build/Testing/FunctionalTests.xml | 20 +++++++-------- Build/Testing/UnitTests.xml | 37 +++++++++++++++++++--------- Classes/Domain/Model/ImageSource.php | 4 +-- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Build/Testing/FunctionalTests.xml b/Build/Testing/FunctionalTests.xml index 912c536..d3c9085 100644 --- a/Build/Testing/FunctionalTests.xml +++ b/Build/Testing/FunctionalTests.xml @@ -14,14 +14,14 @@ cacheDirectory=".phpunit.cache" requireCoverageMetadata="false" > - - - ../../Tests/Functional/ - - - - - - - + + + ../../Tests/Functional/ + + + + + + + diff --git a/Build/Testing/UnitTests.xml b/Build/Testing/UnitTests.xml index 5e69fad..a1a307a 100644 --- a/Build/Testing/UnitTests.xml +++ b/Build/Testing/UnitTests.xml @@ -1,13 +1,28 @@ - - - - ../../Tests/Unit/ - - - - - - - + + + + ../../Tests/Unit/ + + + + + + + diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index b83cfb3..a3ff756 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -256,8 +256,8 @@ protected function processImage(): void } $processingInstructions = [ - 'width' => round( $originalImage->getWidth() * $this->getScale() ), - 'height' => round( $originalImage->getHeight() * $this->getScale() ), + 'width' => round($originalImage->getWidth() * $this->getScale()), + 'height' => round($originalImage->getHeight() * $this->getScale()), 'fileExtension' => $this->getFormat(), 'crop' => $crop ]; From d66368c9aa16d37698d6faafa48ab8ce98a5a094 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 17:19:24 +0200 Subject: [PATCH 15/22] [TASK] match ext_emconf to composer.json and use tag for ext_emconf version --- composer.json | 6 +++++- ext_emconf.php | 9 +++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index bdc4554..7e94658 100644 --- a/composer.json +++ b/composer.json @@ -72,6 +72,10 @@ "@test:functional" ], "test:unit": "Build/Scripts/runTests.sh", - "test:functional": "Build/Scripts/runTests.sh -s functional" + "test:functional": "Build/Scripts/runTests.sh -s functional", + "prepare-release": [ + "sed -i'' -e \"s/'version' => ''/'version' => '$(echo ${GITHUB_REF#refs/tags/} | sed 's/v//')'/\" ext_emconf.php", + "rm -r .github .ecrc .editorconfig .gitattributes Build Tests" + ] } } diff --git a/ext_emconf.php b/ext_emconf.php index 1c99a50..380e1a6 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -5,15 +5,12 @@ 'category' => 'fe', 'author' => 'Benjamin Tammling, Ulrich Mathes, Simon Praetorius', 'author_email' => 'benjamin.tammling@sitegeist.de, mathes@sitegeist.de, moin@praetorius.me', - 'author_company' => 'sitegeist media solutions GmbH', 'state' => 'stable', - 'uploadfolder' => false, - 'clearCacheOnLoad' => false, - 'version' => '1.0.0-dev', + 'version' => '', 'constraints' => [ 'depends' => [ - 'typo3' => '9.5.0-11.9.99', - 'php' => '7.2.0-7.9.99' + 'typo3' => '13.2.0-13.2.99', + 'php' => '8.2.0-8.3.99' ], 'conflicts' => [ ], From 58a3ce564520755bed973f4c486f26b0c4288378 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 2 Sep 2024 17:21:25 +0200 Subject: [PATCH 16/22] [TASK] update github testing and only use TYPO3 13 and PHP 8.2 --- .github/workflows/tests.yml | 101 ++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d1ebf30..ce486bb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - + name: Set up Docker Compose + run: | + docker compose version || { + echo "Docker Compose not found, installing..." + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker compose version + } - name: Validate composer.json @@ -16,7 +26,7 @@ jobs: - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.composer/cache key: composer @@ -40,17 +50,23 @@ jobs: strategy: max-parallel: 2 matrix: - php-versions: [7.4, '8.0'] - typo3-versions: [10, 11] - exclude: - - - php-versions: '8.0' - typo3-versions: 10 + php-versions: ['8.2'] + typo3-versions: [13] name: Unit (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - + name: Set up Docker Compose + run: | + docker compose version || { + echo "Docker Compose not found, installing..." + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker compose version + } - name: Validate composer.json @@ -58,7 +74,7 @@ jobs: - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.composer/cache key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} @@ -76,35 +92,42 @@ jobs: runs-on: ubuntu-latest strategy: - max-parallel: 2 - matrix: - php-versions: [7.4, '8.0'] - typo3-versions: [10, 11] - exclude: - - - php-versions: '8.0' - typo3-versions: 10 + max-parallel: 2 + matrix: + php-versions: ['8.2'] + typo3-versions: [13] + name: Functional (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - - uses: actions/checkout@v2 - - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate - - - - 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: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall - - - - name: Automated Functional Testing - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s functional + - + uses: actions/checkout@v4 + + - + name: Set up Docker Compose + run: | + docker compose version || { + echo "Docker Compose not found, installing..." + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker compose version + } + + - + name: Validate composer.json + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate + + - + name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} + + - + name: Install composer dependencies + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall + + - + name: Automated Functional Testing + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s functional From cdc54e522d32b60adb094fd9091c92e8d2304756 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Fri, 6 Sep 2024 15:05:03 +0200 Subject: [PATCH 17/22] [TASK] update testing setup based on https://git.typo3.org/qa/example-extension from the best practices team --- .ecrc | 1 + .github/workflows/tests.yml | 146 ++--- .gitignore | 6 +- Build/Scripts/runTests.sh | 613 +++++++++++++----- .../{Testing => phpunit}/FunctionalTests.xml | 0 .../FunctionalTestsBootstrap.php | 0 Build/{Testing => phpunit}/UnitTests.xml | 0 .../UnitTestsBootstrap.php | 0 Build/testing-docker/docker-compose.yml | 331 ---------- composer.json | 11 +- 10 files changed, 523 insertions(+), 585 deletions(-) rename Build/{Testing => phpunit}/FunctionalTests.xml (100%) rename Build/{Testing => phpunit}/FunctionalTestsBootstrap.php (100%) rename Build/{Testing => phpunit}/UnitTests.xml (100%) rename Build/{Testing => phpunit}/UnitTestsBootstrap.php (100%) delete mode 100644 Build/testing-docker/docker-compose.yml diff --git a/.ecrc b/.ecrc index 7d1afee..7ece0b3 100644 --- a/.ecrc +++ b/.ecrc @@ -6,6 +6,7 @@ "NoColor": false, "Exclude": [ ".Build", + ".cache", ".git", ".DS_Store", ".xsd" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ce486bb..95ee46c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,129 +5,91 @@ on: [push, pull_request] jobs: lint: name: Source code linting - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 + strategy: + max-parallel: 2 + matrix: + php-versions: ['8.2'] + typo3-versions: ['13.2'] steps: - - + - name: "Checkout" uses: actions/checkout@v4 - - - name: Set up Docker Compose - run: | - docker compose version || { - echo "Docker Compose not found, installing..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker compose version - } - - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -s composerValidate - - - - name: Cache composer dependencies + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate + + - name: Cache dependencies installed with composer uses: actions/cache@v4 with: - path: ~/.composer/cache - key: composer + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -s composerInstall + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -s composer install - - - name: PHP Linting + - name: PHP Linting run: Build/Scripts/runTests.sh -s lintPhp - - - name: Editorconfig Linting + - name: Editorconfig Linting run: Build/Scripts/runTests.sh -s lintEditorconfig unittest: - runs-on: ubuntu-latest - + runs-on: ubuntu-24.04 strategy: max-parallel: 2 matrix: php-versions: ['8.2'] - typo3-versions: [13] + typo3-versions: ['13.2'] - name: Unit (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) + name: Unit Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - + - name: "Checkout" uses: actions/checkout@v4 - - - name: Set up Docker Compose - run: | - docker compose version || { - echo "Docker Compose not found, installing..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker compose version - } - - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate - - - - name: Cache composer dependencies + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate + + - name: Cache dependencies installed with composer uses: actions/cache@v4 with: - path: ~/.composer/cache - key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} - - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" - - - name: Automated Unit Testing - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s unit + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerUpdateMin + - name: Automated Unit Testing + run: Build/Scripts/runTests.sh -s unit - functionaltest: - runs-on: ubuntu-latest - - strategy: - max-parallel: 2 - matrix: - php-versions: ['8.2'] - typo3-versions: [13] + functional: + runs-on: ubuntu-24.04 + strategy: + max-parallel: 2 + matrix: + php-versions: ['8.2'] + typo3-versions: ['13.2'] - name: Functional (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) - steps: - - + name: Functional Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) + steps: + - name: "Checkout" uses: actions/checkout@v4 - - - name: Set up Docker Compose - run: | - docker compose version || { - echo "Docker Compose not found, installing..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker compose version - } - - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate - - - - name: Cache composer dependencies + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate + + - name: Cache dependencies installed with composer uses: actions/cache@v4 with: - path: ~/.composer/cache - key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerUpdateMin - - - name: Automated Functional Testing - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s functional + - name: Automated Unit Testing + run: Build/Scripts/runTests.sh -s functional diff --git a/.gitignore b/.gitignore index f72b2a2..22c4fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .Build -composer.lock /var -/Build/Testing/.phpunit.result.cache -/Build/Testing/.phpunit.cache/ -/Build/testing-docker/.env +.cache +/Build/phpunit/.phpunit.cache/ diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 6254c2b..ba74f23 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -1,85 +1,223 @@ #!/usr/bin/env bash -# -# TYPO3 core test runner based on docker and docker-compose. -# - -# Function to write a .env file in Build/testing-docker/local -# This is read by docker-compose and vars defined here are -# used in Build/testing-docker/local/docker-compose.yml -setUpDockerComposeDotEnv() { - # Delete possibly existing local .env file if exists - [ -e .env ] && rm .env - # Set up a new .env file for docker-compose - echo "COMPOSE_PROJECT_NAME=local" >> .env - # To prevent access rights of files created by the testing, the docker image later - # runs with the same user that is currently executing the script. docker-compose can't - # use $UID directly itself since it is a shell variable and not an env variable, so - # we have to set it explicitly here. - echo "HOST_UID=`id -u`" >> .env - # Your local home directory for composer and npm caching - echo "HOST_HOME=${HOME}" >> .env - # Your local user - echo "ROOT_DIR"=${ROOT_DIR} >> .env - echo "HOST_USER=${USER}" >> .env - echo "TEST_FILE=${TEST_FILE}" >> .env - echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" >> .env - echo "PHP_XDEBUG_PORT=${PHP_XDEBUG_PORT}" >> .env - echo "PHP_VERSION=${PHP_VERSION}" >> .env - echo "TYPO3_VERSION=${TYPO3_VERSION}" >> .env - echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" >> .env - echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" >> .env - echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" >> .env +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 20 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + # shellcheck disable=SC2181 # Disabled because we don‘t want to move the long line between the brackets + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi } -# Load help text into $HELP -read -r -d '' HELP </dev/null + done + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null +} -Usage: $0 [options] [file] +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. + case ${DBMS} in + mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.3" + if ! [[ ${DBMS_VERSION} =~ ^(10.3|10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(5.5|5.6|5.7|8.0)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; + esac +} -No arguments: Run all unit tests with PHP 8.2 +cleanCacheFiles() { + echo -n "Clean caches ... " + rm -rf \ + .Build/.cache \ + .php-cs-fixer.cache + echo "done" +} + +cleanTestFiles() { + # test related + echo -n "Clean test related files ... " + rm -rf \ + .Build/public/typo3temp/var/tests/ + echo "done" +} + +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < Specifies which test suite to run - - composerInstall: "composer install" - - composerInstallMax: "composer update", with no platform.php config. - - composerInstallMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0. - - composerValidate: "composer validate" + - clean: clean up build, cache and testing related files and folders + - cleanCache: clean up cache related files and folders + - cleanTests: clean up test related files and folders + - composer: "composer" with all remaining arguments dispatched. + - composerNormalize: Normalizes the composer.json. + - composerUnused: Finds unused Composer packages. + - composerUpdateMax: "composer update", with no platform.php config. + - composerUpdateMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0. + - functional: PHP functional tests - lintPhp: PHP linting - - lintEditorconfig: Editorconfig linting + - lintEditorconfig: Editor config linting - unit (default): PHP unit tests - - functional: functional tests + - unitRandom: PHP unit tests in random order, add -o to use specific seed + - update: Updates existing typo3/core-testing-*:latest container images and removes dangling local volumes. + + -a + Only with -s functional|functionalDeprecated + Specifies to use another driver, following combinations are available: + - mysql + - mysqli (default) + - pdo_mysql + - mariadb + - mysqli (default) + - pdo_mysql + + -b + Container environment: + - docker (default) + - podman - -d - Only with -s functional + -d + Only with -s functional|functionalDeprecated Specifies on which DBMS tests are performed - - mariadb (default): use mariadb - - mssql: use mssql microsoft sql server + - mariadb: use mariadb + - mysql: use MySQL - postgres: use postgres - - sqlite: use sqlite + - sqlite: (default): use sqlite - -p <8.3|8.2|8.1> - Specifies the PHP minor version to be used - - 8.3: use PHP 8.3 - - 8.2 (default): use PHP 8.2 - - 8.1: use PHP 8.3 + -i version + Specify a specific database version + With "-d mariadb": + - 10.3 short-term, maintained until 2023-05-25 (default) + - 10.4 short-term, maintained until 2024-06-18 + - 10.5 short-term, maintained until 2025-06-24 + - 10.6 long-term, maintained until 2026-06 + - 10.7 short-term, no longer maintained + - 10.8 short-term, maintained until 2023-05 + - 10.9 short-term, maintained until 2023-08 + - 10.10 short-term, maintained until 2023-11 + - 10.11 long-term, maintained until 2028-02 + - 11.0 development series + - 11.1 short-term development series + With "-d mysql": + - 5.5 unmaintained since 2018-12 + - 5.6 unmaintained since 2021-02 + - 5.7 maintained until 2023-10 + - 8.0 maintained until 2026-04 (default) + With "-d postgres": + - 10 unmaintained since 2022-11-10 (default) + - 11 unmaintained since 2023-11-09 + - 12 maintained until 2024-11-14 + - 13 maintained until 2025-11-13 + - 14 maintained until 2026-11-12 + - 15 maintained until 2027-11-11 + - 16 maintained until 2028-11-09 + + -t <13.2> + Only with -s composerUpdateMin|composerUpdateMax + Specifies the TYPO3 CORE Version to be used + - 13.2: (default) use TYPO3 v13 with typo3/cms-composer-installers ^5 - -t <13> - Specifies the TYPO3 version to be used - - 13 (default): use TYPO3 13 + -p <8.2|8.3|8.4> + Specifies the PHP minor version to be used + - 8.2: use PHP 8.2 + - 8.3: (default) use PHP 8.3 + - 8.4: use PHP 8.4 -e "" - Only with -s functional|unit - Additional options to send to phpunit tests. - For phpunit, options starting with "--" must be added after options starting with "-". + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom + Additional options to send to phpunit (unit & functional tests). For phpunit, + options starting with "--" must be added after options starting with "-". Example -e "-v --filter canRetrieveValueWithGP" to enable verbose output AND filter tests named "canRetrieveValueWithGP" -x - Only with -s unit + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom Send information to host instance for test or system under test break points. This is especially useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port can be selected with -y @@ -88,100 +226,150 @@ Options: Send xdebug information to a different port than default 9003 if an IDE like PhpStorm is not listening on default port. - -u - Update existing typo3gmbh/phpXY:latest docker images. Maintenance call to docker pull latest - versions of the main php images. The images are updated once in a while and only the youngest - ones are supported by core testing. Use this if weird test errors occur. Also removes obsolete - image versions of typo3gmbh/phpXY. + -o + Only with -s unitRandom + Set specific random seed to replay a random run in this order again. The phpunit randomizer + outputs the used seed at the end (in gitlab core testing logs, too). Use that number to + replay the unit tests in that order. - -v - Enable verbose script output. Shows variables and docker commands. + -u + Update existing typo3/core-testing-*:latest container images and remove dangling local volumes. + New images are published once in a while and only the latest ones are supported by core testing. + Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*. -h Show this help. Examples: - # Run unit tests using PHP 8.2 + # Run all core unit tests using PHP 8.3 ./Build/Scripts/runTests.sh + ./Build/Scripts/runTests.sh -s unit + + # Run all core units tests and enable xdebug (have a PhpStorm listening on port 9003!) + ./Build/Scripts/runTests.sh -x + + # Run unit tests in phpunit verbose mode with xdebug on PHP 8.1 and filter for test canRetrieveValueWithGP + ./Build/Scripts/runTests.sh -x -p 8.1 -e "-v --filter canRetrieveValueWithGP" + + # Run functional tests in phpunit with a filtered test method name in a specified file + # example will currently execute two tests, both of which start with the search term + ./Build/Scripts/runTests.sh -s functional -e "--filter deleteContent" typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php + + # Run functional tests on postgres with xdebug, php 8.1 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.1 -s functional -d postgres typo3/sysext/core/Tests/Functional/Authentication - # Run unit tests using PHP 8.1 - ./Build/Scripts/runTests.sh -p 8.1 + # Run functional tests on postgres 11 + ./Build/Scripts/runTests.sh -s functional -d postgres -i 11 EOF +} -# Test if docker-compose exists, else exit out with error -if ! type "docker" > /dev/null; then - echo "This script relies on docker and docker compose. Please install" >&2 +# Test if at least one of the supported container binaries exists, else exit out with error +if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then + echo "This script relies on docker or podman. Please install at least one of them" >&2 exit 1 fi # Go to the directory this script is located, so everything else is relative -# to this dir, no matter from where this script is called. -THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" cd "$THIS_SCRIPT_DIR" || exit 1 - -# Go to directory that contains the local docker-compose.yml file -cd ../testing-docker || exit 1 +cd ../../ || exit 1 +ROOT_DIR="${PWD}" # Option defaults -if ! command -v realpath &> /dev/null; then - echo "Consider installing realpath for properly resolving symlinks" >&2 - ROOT_DIR="${PWD}/../../" -else - ROOT_DIR=`realpath ${PWD}/../../` -fi TEST_SUITE="unit" -DBMS="mariadb" -PHP_VERSION="8.2" -TYPO3_VERSION="13" +CORE_VERSION="13.2" +DBMS="sqlite" +DBMS_VERSION="" +PHP_VERSION="8.3" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 EXTRA_TEST_OPTIONS="" -SCRIPT_VERBOSE=0 +PHPUNIT_RANDOM="" +DATABASE_DRIVER="" +CONTAINER_BIN="" +COMPOSER_ROOT_VERSION="3.0.x-dev" +NODE_VERSION=22 +HELP_TEXT_NPM_CI="Now running \'npm ci --silent\'." +HELP_TEXT_NPM_FAILURE="npm clean-install has failed. Please run \'${0} -s npm ci\' to explore." +CONTAINER_INTERACTIVE="-it --init" +HOST_UID=$(id -u) +HOST_PID=$(id -g) +USERSET="" +SUFFIX="$RANDOM" +NETWORK="friendsoftypo3-tea-${SUFFIX}" +CI_PARAMS="${CI_PARAMS:-}" +CONTAINER_HOST="host.docker.internal" +# shellcheck disable=SC2034 # This variable will be needed when we try to clean up the root folder +PHPSTAN_CONFIG_FILE="phpstan.neon" +IS_CORE_CI=0 -# Option parsing +# Option parsing updates above default vars # Reset in case getopts has been used previously in the shell OPTIND=1 # Array for invalid options -INVALID_OPTIONS=(); +INVALID_OPTIONS=() # Simple option parsing based on getopts (! not getopt) -while getopts ":s:d:p:e:t:xy:huv" OPT; do +while getopts "a:b:s:d:i:p:e:t:xy:o:nhu" OPT; do case ${OPT} in s) TEST_SUITE=${OPTARG} ;; + a) + DATABASE_DRIVER=${OPTARG} + ;; + b) + if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then + INVALID_OPTIONS+=("-b ${OPTARG}") + fi + CONTAINER_BIN=${OPTARG} + ;; d) DBMS=${OPTARG} ;; + i) + DBMS_VERSION=${OPTARG} + ;; p) PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2|8.3|8.4)$ ]]; then + INVALID_OPTIONS+=("-p ${OPTARG}") + fi ;; e) EXTRA_TEST_OPTIONS=${OPTARG} ;; + t) + CORE_VERSION=${OPTARG} + if ! [[ ${CORE_VERSION} =~ ^(13.2)$ ]]; then + INVALID_OPTIONS+=("-t ${OPTARG}") + fi + ;; x) PHP_XDEBUG_ON=1 ;; y) PHP_XDEBUG_PORT=${OPTARG} ;; + o) + PHPUNIT_RANDOM="--random-order-seed=${OPTARG}" + ;; + n) + CGLCHECK_DRY_RUN=1 + ;; h) + loadHelp echo "${HELP}" exit 0 ;; u) TEST_SUITE=update ;; - v) - SCRIPT_VERBOSE=1 - ;; - t) - TYPO3_VERSION=${OPTARG} - ;; \?) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("-${OPTARG}") ;; :) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("-${OPTARG}") ;; esac done @@ -190,113 +378,234 @@ done if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then echo "Invalid option(s):" >&2 for I in "${INVALID_OPTIONS[@]}"; do - echo "-"${I} >&2 + echo ${I} >&2 done echo >&2 - echo "${HELP}" >&2 + echo "call \".Build/Scripts/runTests.sh -h\" to display help and valid options" exit 1 fi -# Move "7.4" to "php74", the latter is the docker container name -DOCKER_PHP_IMAGE=`echo "php${PHP_VERSION}" | sed -e 's/\.//'` + + +# ENV var "CI" is set by gitlab-ci. Use it to force some CI details. +if [ "${CI}" == "true" ]; then + IS_CORE_CI=1 + CONTAINER_INTERACTIVE="" +fi + +# determine default container binary to use: 1. podman 2. docker +if [[ -z "${CONTAINER_BIN}" ]]; then + if type "podman" >/dev/null 2>&1; then + CONTAINER_BIN="podman" + elif type "docker" >/dev/null 2>&1; then + CONTAINER_BIN="docker" + fi +fi + +if [ "$(uname)" != "Darwin" ] && [ "${CONTAINER_BIN}" == "docker" ]; then + # Run docker jobs as current user to prevent permission issues. Not needed with podman. + USERSET="--user $HOST_UID" +fi + +if ! type ${CONTAINER_BIN} >/dev/null 2>&1; then + echo "Selected container environment \"${CONTAINER_BIN}\" not found. Please install \"${CONTAINER_BIN}\" or use -b option to select one." >&2 + exit 1 +fi + +# Create .cache dir: composer need this. +mkdir -p .cache +mkdir -p .Build/public/typo3temp/var/tests + +IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" +IMAGE_NODE="docker.io/node:${NODE_VERSION}-alpine" +IMAGE_ALPINE="docker.io/alpine:3.8" +IMAGE_SHELLCHECK="docker.io/koalaman/shellcheck:v0.10.0" +IMAGE_DOCS="ghcr.io/typo3-documentation/render-guides:latest" +IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" # Set $1 to first mass argument, this is the optional test file or test directory to execute shift $((OPTIND - 1)) -if [ -n "${1}" ]; then - TEST_FILE="Web/typo3conf/ext/media_components/${1}" +TEST_FILE=${1} + +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +if [ "${CONTAINER_BIN}" == "docker" ]; then + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" +else + # podman + CONTAINER_HOST="host.containers.internal" + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" fi -if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=host.docker.internal" fi # Suite execution case ${TEST_SUITE} in - composerInstall) - setUpDockerComposeDotEnv - docker compose run composer_install + clean) + cleanCacheFiles + cleanRenderedDocumentationFiles + cleanTestFiles + ;; + cleanCache) + cleanCacheFiles + ;; + cleanTests) + cleanTestFiles + ;; + composer) + COMMAND_PARTS=(composer "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND_PARTS[@]}" + SUITE_EXIT_CODE=$? + ;; + composerUpdateMax) + COMMAND="composer config --unset platform.php; composer require --no-ansi --no-interaction --no-progress --no-install typo3/cms-core:"^${CORE_VERSION}"; composer update --no-progress --no-interaction; composer dumpautoload; composer show" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-max-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker compose down ;; - composerInstallMax) - setUpDockerComposeDotEnv - docker compose run composer_install_max + composerUpdateMin) + COMMAND="composer config platform.php ${PHP_VERSION}.0; composer require --no-ansi --no-interaction --no-progress --no-install typo3/cms-core:"^${CORE_VERSION}"; composer update --prefer-lowest --no-progress --no-interaction; composer dumpautoload; composer show" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-min-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker compose down ;; - composerInstallMin) - setUpDockerComposeDotEnv - docker compose run composer_install_min + composerNormalize) + COMMAND="composer ci:composer:normalize" + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND="composer ci:composer:normalize" + else + COMMAND="composer fix:composer:normalize" + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-normalize-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_DOCS} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker compose down ;; - composerValidate) - setUpDockerComposeDotEnv - docker compose run composer_validate + composerUnused) + COMMAND="composer ci:composer:unused" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-unused-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker compose down ;; functional) - setUpDockerComposeDotEnv + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Functional" + COMMAND=".Build/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} ${TEST_FILE}" case ${DBMS} in mariadb) - docker compose run functional_mariadb10 + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; - mssql) - docker compose run functional_mssql2019latest + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; postgres) - docker compose run functional_postgres10 + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; sqlite) - # sqlite has a tmpfs as .Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - # Since docker is executed as root (yay!), the path to this dir is owned by - # root if docker creates it. Thank you, docker. We create the path beforehand - # to avoid permission issues. - mkdir -p ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - docker compose run functional_sqlite + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; - *) - echo "Invalid -d option argument ${DBMS}" >&2 - echo >&2 - echo "${HELP}" >&2 - exit 1 esac - docker compose down ;; lintPhp) - setUpDockerComposeDotEnv - docker compose run lint_php + COMMAND="composer lint:php" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker compose down ;; lintEditorconfig) - setUpDockerComposeDotEnv - docker compose run lint_editorconfig - SUITE_EXIT_CODE=$? - docker compose down - ;; + COMMAND="composer lint:editorconfig" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; unit) - setUpDockerComposeDotEnv - docker compose run unit + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Unit" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} .Build/bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE} + SUITE_EXIT_CODE=$? + ;; + unitRandom) + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Unit" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-random-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} .Build/bin/phpunit -c Build/phpunit/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE} SUITE_EXIT_CODE=$? - docker compose down ;; update) # pull typo3/core-testing-*:latest versions of those ones that exist locally - docker images typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} docker pull {} + echo "> pull ghcr.io/typo3/core-testing-*:latest versions of those ones that exist locally" + ${CONTAINER_BIN} images ghcr.io/typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} ${CONTAINER_BIN} pull {} + echo "" # remove "dangling" typo3/core-testing-* images (those tagged as ) - docker images typo3/core-testing-* --filter "dangling=true" --format "{{.ID}}" | xargs -I {} docker rmi {} + echo "> remove \"dangling\" ghcr.io/typo3/core-testing-* images (those tagged as )" + ${CONTAINER_BIN} images --filter "reference=ghcr.io/typo3/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi {} + echo "" ;; *) + loadHelp echo "Invalid -s option argument ${TEST_SUITE}" >&2 echo >&2 echo "${HELP}" >&2 + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null + else + ${CONTAINER_BIN} network rm -f ${NETWORK} >/dev/null + fi exit 1 + ;; esac +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +if [[ ${IS_CORE_CI} -eq 1 ]]; then + echo "Environment: CI" >&2 +else + echo "Environment: local" >&2 +fi +if [[ ${TEST_SUITE} =~ ^(npm|lintCss|lintJs)$ ]]; then + echo "NODE: ${NODE_VERSION}" >&2 +else + echo "PHP: ${PHP_VERSION}" >&2 + echo "TYPO3: ${CORE_VERSION}" >&2 +fi +echo "CONTAINER_BIN: ${CONTAINER_BIN}" +if [[ ${TEST_SUITE} =~ ^functional$ ]]; then + case "${DBMS}" in + mariadb|mysql) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver pdo_pgsql" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS} driver pdo_sqlite" >&2 + ;; + esac +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. exit $SUITE_EXIT_CODE diff --git a/Build/Testing/FunctionalTests.xml b/Build/phpunit/FunctionalTests.xml similarity index 100% rename from Build/Testing/FunctionalTests.xml rename to Build/phpunit/FunctionalTests.xml diff --git a/Build/Testing/FunctionalTestsBootstrap.php b/Build/phpunit/FunctionalTestsBootstrap.php similarity index 100% rename from Build/Testing/FunctionalTestsBootstrap.php rename to Build/phpunit/FunctionalTestsBootstrap.php diff --git a/Build/Testing/UnitTests.xml b/Build/phpunit/UnitTests.xml similarity index 100% rename from Build/Testing/UnitTests.xml rename to Build/phpunit/UnitTests.xml diff --git a/Build/Testing/UnitTestsBootstrap.php b/Build/phpunit/UnitTestsBootstrap.php similarity index 100% rename from Build/Testing/UnitTestsBootstrap.php rename to Build/phpunit/UnitTestsBootstrap.php diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml deleted file mode 100644 index f5df54d..0000000 --- a/Build/testing-docker/docker-compose.yml +++ /dev/null @@ -1,331 +0,0 @@ -services: - mariadb10: - image: mariadb:10 - environment: - MYSQL_ROOT_PASSWORD: funcp - tmpfs: - - /var/lib/mysql/:rw,noexec,nosuid - - mssql2019latest: - image: typo3/core-testing-mssql2019:latest - environment: - ACCEPT_EULA: Y - SA_PASSWORD: "Test1234!" - MSSQL_PID: Developer - - postgres10: - image: postgres:10-alpine - environment: - POSTGRES_PASSWORD: funcp - POSTGRES_USER: ${HOST_USER} - tmpfs: - - /var/lib/postgresql/data:rw,noexec,nosuid - - composer_install: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer require typo3/minimal "^${TYPO3_VERSION}" --prefer-dist --no-progress - composer install --no-progress --no-interaction; - " - composer_install_max: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer req typo3/cms-core:"^${TYPO3_VERSION}" \ - typo3/cms-backend:"^${TYPO3_VERSION}" \ - typo3/cms-frontend:"^${TYPO3_VERSION}" \ - typo3/cms-extbase:"^${TYPO3_VERSION}" \ - typo3/cms-fluid:"^${TYPO3_VERSION}" \ - typo3/cms-recordlist:"^${TYPO3_VERSION}" \ - typo3/cms-install:"^${TYPO3_VERSION}" - composer config --unset platform.php; - composer update --no-progress --no-interaction; - composer dumpautoload; - " - - composer_install_min: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer req typo3/cms-core:"^${TYPO3_VERSION}" \ - typo3/cms-backend:"^${TYPO3_VERSION}" \ - typo3/cms-frontend:"^${TYPO3_VERSION}" \ - typo3/cms-extbase:"^${TYPO3_VERSION}" \ - typo3/cms-fluid:"^${TYPO3_VERSION}" \ - typo3/cms-recordlist:"^${TYPO3_VERSION}" \ - typo3/cms-install:"^${TYPO3_VERSION}" - composer config platform.php ${PHP_VERSION}.0; - composer update --prefer-lowest --no-progress --no-interaction; - composer dumpautoload; - " - - composer_validate: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer validate; - " - - functional_mariadb10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - mariadb10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mariadb10 - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mariadb10 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " - - functional_mssql2019latest: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - mssql2019latest - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseDriver: sqlsrv - typo3DatabaseName: func - typo3DatabasePassword: "Test1234!" - typo3DatabaseUsername: SA - typo3DatabasePort: 1433 - typo3DatabaseCharset: utf-8 - typo3DatabaseHost: mssql2019latest - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mssql2019latest 1433; do - sleep 1; - done; - sleep 5; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; - fi - " - - functional_postgres10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - postgres10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseDriver: pdo_pgsql - typo3DatabaseName: bamboo - typo3DatabaseUsername: ${HOST_USER} - typo3DatabaseHost: postgres10 - typo3DatabasePassword: funcp - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z postgres10 5432; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - fi - " - - functional_sqlite: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - tmpfs: - - ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,uid=${HOST_UID} - environment: - typo3DatabaseDriver: pdo_sqlite - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - .Build/bin/phpunit -c Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - fi - " - - lint_php: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - .Build/bin/phpcs --version; - .Build/bin/phpcs -p --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,Tests,ext_emconf.php . - " - - lint_editorconfig: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - echo -n 'ec version: '; - .Build/bin/ec -version; - .Build/bin/ec -exclude .phpunit.* . - " - - unit: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP' - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - .Build/bin/phpunit -c Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - .Build/bin/phpunit -c Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " diff --git a/composer.json b/composer.json index 7e94658..4f53306 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "config": { "vendor-dir": ".Build/vendor", "bin-dir": ".Build/bin", + "lock": false, "allow-plugins": { "typo3/cms-composer-installers": true, "typo3/class-alias-loader": true @@ -54,10 +55,8 @@ }, "extra": { "typo3/cms": { - "cms-package-dir": "{$vendor-dir}/typo3/cms", - "app-dir": ".Build", - "web-dir": ".Build/Web", - "extension-key": "media_components" + "extension-key": "media_components", + "web-dir": ".Build/public" } }, "scripts": { @@ -65,8 +64,8 @@ "@lint:php", "@lint:editorconfig" ], - "lint:php": "Build/Scripts/runTests.sh -s lintPhp", - "lint:editorconfig": "Build/Scripts/runTests.sh -s lintEditorconfig", + "lint:php": "phpcs --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,.cache,Tests,ext_emconf.php .", + "lint:editorconfig": "ec -exclude .Build .", "test": [ "@test:unit", "@test:functional" From 827837a13fa40f14d36071caac398f63704a9081 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 30 Sep 2024 14:50:28 +0200 Subject: [PATCH 18/22] [TASK] avoid renderStatic in ViewHelpers for Fluid 4 --- .../Image/CropVariantViewHelper.php | 21 +++++-------- .../Image/Modify/CropViewHelper.php | 20 +++++-------- .../Image/Modify/FormatViewHelper.php | 20 +++++-------- .../Image/Modify/ScaleViewHelper.php | 30 ++++++++----------- .../ViewHelpers/Image/SrcsetViewHelper.php | 21 +++++-------- composer.json | 8 ++--- 6 files changed, 46 insertions(+), 74 deletions(-) diff --git a/Classes/ViewHelpers/Image/CropVariantViewHelper.php b/Classes/ViewHelpers/Image/CropVariantViewHelper.php index 2643956..e058579 100644 --- a/Classes/ViewHelpers/Image/CropVariantViewHelper.php +++ b/Classes/ViewHelpers/Image/CropVariantViewHelper.php @@ -4,27 +4,22 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3\CMS\Core\Resource\FileInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class CropVariantViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class CropVariantViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments(): void { $this->registerArgument('image', FileInterface::class, 'FAL file'); $this->registerArgument('name', 'string', 'name of the crop variant that should be used', false, 'default'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): Area { - $arguments['image'] ??= $renderChildrenClosure(); - $cropVariantCollection = CropVariantCollection::create((string)$arguments['image']->getProperty('crop')); - return $cropVariantCollection->getCropArea($arguments['name']); + public function render(): Area + { + $this->arguments['image'] ??= $this->renderChildren(); + $cropVariantCollection = CropVariantCollection::create((string)$this->arguments['image']->getProperty('crop')); + return $cropVariantCollection->getCropArea($this->arguments['name']); } } diff --git a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php index a651b4a..f87e8a1 100644 --- a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php @@ -5,28 +5,22 @@ use Sitegeist\MediaComponents\Domain\Model\CropArea; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class CropViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class CropViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('crop', CropArea::class, 'Crop area'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - if ($arguments['crop']) { + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if ($this->arguments['crop']) { $croppedSource = clone $imageSource; - $croppedSource->setCrop($arguments['crop']); + $croppedSource->setCrop($this->arguments['crop']); } return $croppedSource ?? $imageSource; } diff --git a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php index 6f15f75..e03cda8 100644 --- a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php @@ -4,28 +4,22 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image\Modify; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class FormatViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class FormatViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('format', 'string', 'File format that should be used'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - if ($arguments['format']) { + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if ($this->arguments['format']) { $imageSourceWithFormat = clone $imageSource; - $imageSourceWithFormat->setFormat($arguments['format']); + $imageSourceWithFormat->setFormat($this->arguments['format']); } return $imageSourceWithFormat ?? $imageSource; } diff --git a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php index 91cc68d..d3bd581 100644 --- a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php @@ -4,13 +4,11 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image\Modify; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class ScaleViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class ScaleViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); @@ -19,24 +17,22 @@ public function initializeArguments(): void $this->registerArgument('maxDimensions', 'boolean', 'If true, height and width will be considered maximums', true, false); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - - if ($arguments['height'] || $arguments['width']) { - $heightFactor = $arguments['height'] ? $arguments['height'] / $imageSource->getOriginalImage()->getHeight() : 1; - $widthFactor = $arguments['width'] ? $arguments['width'] / $imageSource->getOriginalImage()->getWidth() : 1; - $scaleFactor = ($arguments['maxDimensions']) + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if (!($imageSource->getOriginalImage() instanceof ImageWithDimensions)) { + return $imageSource; + } + if ($this->arguments['height'] || $this->arguments['width']) { + $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getOriginalImage()->getHeight() : 1; + $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getOriginalImage()->getWidth() : 1; + $scaleFactor = ($this->arguments['maxDimensions']) ? min($heightFactor, $widthFactor) : max($heightFactor, $widthFactor); $scaledSource = clone $imageSource; $scaledSource->setScale($scaleFactor); } - return $scaledSource ?? $imageSource; } } diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index 65dbbf9..e340ec4 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -7,13 +7,10 @@ use Sitegeist\MediaComponents\Domain\Model\SourceSet; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class SrcsetViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class SrcsetViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); @@ -21,17 +18,13 @@ public function initializeArguments(): void $this->registerArgument('base', ImageSource::class, 'Base image for pixel density calculations'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): string { - if (!$arguments['srcset'] instanceof SourceSet) { + public function render(): string + { + if (!$this->arguments['srcset'] instanceof SourceSet) { return ''; } - - $arguments['imageSource'] ??= $renderChildrenClosure(); - return self::generateSrcsetString($arguments['imageSource'], $arguments['srcset'], $arguments['base']); + $this->arguments['imageSource'] ??= $this->renderChildren(); + return self::generateSrcsetString($this->arguments['imageSource'], $this->arguments['srcset'], $this->arguments['base']); } public static function generateSrcsetString(ImageSource $imageSource, SourceSet $srcset, ImageSource $base = null): string diff --git a/composer.json b/composer.json index 4f53306..24f281c 100644 --- a/composer.json +++ b/composer.json @@ -24,10 +24,10 @@ }, "require": { "php": "^8.2", - "sitegeist/fluid-components": "^3.7.0", - "typo3fluid/fluid": "<=2.14.1", - "sitegeist/fluid-tagbuilder": "^1", - "typo3/cms-core": "13.2.*" + "sitegeist/fluid-components": "^3.7", + "typo3fluid/fluid": "^4.0", + "sitegeist/fluid-tagbuilder": "^1 || dev-main", + "typo3/cms-core": "^13.3 || dev-main" }, "require-dev": { "typo3/testing-framework": "^8.2", From 33a03f9baa12173f38f9f09ec406da5cd96d87d3 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Mon, 30 Sep 2024 16:21:57 +0200 Subject: [PATCH 19/22] [FEATURE] Implement image processing with new fluid component interfaces --- Classes/Domain/Model/CropArea.php | 4 +- Classes/Domain/Model/ImageSource.php | 104 +++++++++++------- .../Image/CropVariantViewHelper.php | 22 ++-- .../Image/Modify/ScaleViewHelper.php | 4 +- .../ViewHelpers/Image/SrcsetViewHelper.php | 13 ++- Resources/Private/Components/Image/Image.html | 4 + .../Private/Components/Picture/Picture.html | 2 +- .../Components/Picture/Source/Source.html | 4 + .../Functional/AbstractComponentTestCase.php | 2 +- 9 files changed, 104 insertions(+), 55 deletions(-) diff --git a/Classes/Domain/Model/CropArea.php b/Classes/Domain/Model/CropArea.php index 518480c..7540551 100644 --- a/Classes/Domain/Model/CropArea.php +++ b/Classes/Domain/Model/CropArea.php @@ -54,10 +54,10 @@ public static function fromString(string $ratio): static if (substr_count($ratio, ':') === 1) { [$x, $y] = GeneralUtility::trimExplode(':', $ratio); - $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); + $area = $area->applyRatioRestriction(new Ratio('', '', (float) $x / (float) $y)); } elseif (substr_count($ratio, '/') === 1) { [$x, $y] = GeneralUtility::trimExplode('/', $ratio); - $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); + $area = $area->applyRatioRestriction(new Ratio('', '', (float) $x / (float) $y)); } return new static($area); diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index a3ff756..f1d95d3 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -6,18 +6,20 @@ use Sitegeist\MediaComponents\Domain\Model\CropArea; use Sitegeist\MediaComponents\Domain\Model\SourceSet; use Sitegeist\MediaComponents\Interfaces\ConstructibleFromImage; -use SMS\FluidComponents\Domain\Model\FalImage; +use SMS\FluidComponents\Domain\Model\FalFile; use SMS\FluidComponents\Domain\Model\Image; use SMS\FluidComponents\Interfaces\ConstructibleFromArray; use SMS\FluidComponents\Interfaces\ConstructibleFromExtbaseFile; use SMS\FluidComponents\Interfaces\ConstructibleFromFileInterface; use SMS\FluidComponents\Interfaces\ConstructibleFromInteger; use SMS\FluidComponents\Interfaces\ConstructibleFromString; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Interfaces\ProcessableImage; use SMS\FluidComponents\Utility\ComponentArgumentConverter; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Domain\Model\FileReference; -use TYPO3\CMS\Extbase\Service\ImageService; class ImageSource implements ConstructibleFromArray, @@ -32,8 +34,6 @@ class ImageSource implements */ protected ?Image $image = null; - protected ?ImageService $imageService = null; - protected float $scale = 1.0; protected ?CropArea $crop = null; @@ -49,7 +49,6 @@ class ImageSource implements public function __construct( protected ?Image $originalImage = null ) { - $this->imageService = GeneralUtility::makeInstance(ImageService::class); $this->setCrop(new CropArea); } @@ -58,7 +57,7 @@ public static function fromArray(array $value): ImageSource $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); try { - $image = $argumentConverter->convertValueToType($value['originalImage'], Image::class); + $image = $argumentConverter->convertValueToType($value['originalImage'] ?? $value, Image::class); } catch (\SMS\FluidComponents\Exception\InvalidArgumentException) { // TODO better error handling here: // Image is not required, but invalid combination of parameters should @@ -76,20 +75,23 @@ public static function fromArray(array $value): ImageSource if (isset($value['media'])) { $imageSource->setMedia((string) $value['media']); } - if (isset($value['scale'])) { + if (isset($value['sizes'])) { $imageSource->setSizes((string) $value['sizes']); } - if (isset($value['crop']) || isset($value['srcset'])) { - $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); - - if (isset($value['crop'])) { - $imageSource->setCrop($argumentConverter->convertValueToType($value['crop'], CropArea::class)); - } + if (isset($value['crop'])) { + $imageSource->setCrop( + $argumentConverter->convertValueToType($value['crop'], CropArea::class) + ); + } elseif ($image instanceof ImageWithCropVariants) { + $cropVariant = (isset($value['cropVariant'])) + ? $image->getCropVariant($value['cropVariant']) + : $image->getDefaultCrop(); + $imageSource->setCrop(new CropArea($cropVariant)); + } - if (isset($value['srcset'])) { - $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); - } + if (isset($value['srcset'])) { + $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); } return $imageSource; @@ -164,6 +166,40 @@ public function setCrop(CropArea $crop): self return $this; } + public function getCroppedWidth(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getWidth(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getWidth()); + } + + return (int) round($this->crop->getArea()->getWidth() * $this->originalImage->getWidth()); + } + + public function getCroppedHeight(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getHeight(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getHeight()); + } + + return (int) round($this->crop->getArea()->getHeight() * $this->originalImage->getHeight()); + } + public function getImage(): Image { // TODO throw exception if image is not defined @@ -193,12 +229,14 @@ public function getDescription(): ?string public function getHeight(): ?int { - return $this->getImage()->getHeight(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getHeight() : null; } public function getWidth(): ?int { - return $this->getImage()->getWidth(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getWidth() : null; } public function getMedia(): ?string @@ -244,25 +282,17 @@ public function __toString(): string protected function processImage(): void { - $originalImage = $this->getOriginalImage(); - - if ($originalImage) { - $crop = null; - if ($this->getCrop()) { - $cropArea = $this->getCrop()->getArea(); - if (!$cropArea->isEmpty()) { - $crop = $cropArea->makeAbsoluteBasedOnFile($originalImage->getFile()); - } - } - - $processingInstructions = [ - 'width' => round($originalImage->getWidth() * $this->getScale()), - 'height' => round($originalImage->getHeight() * $this->getScale()), - 'fileExtension' => $this->getFormat(), - 'crop' => $crop - ]; - - $this->image = new FalImage($this->imageService->applyProcessingInstructions($originalImage->getFile(), $processingInstructions)); + // TODO implement runtime cache + $this->image = null; + + $original = $this->getOriginalImage(); + if ($original instanceof ProcessableImage && $original instanceof ImageWithDimensions) { + $this->image = $original->process( + (int) round($this->getCroppedWidth() * $this->getScale()), + (int) round($this->getCroppedHeight() * $this->getScale()), + $this->getFormat(), + $this->getCrop()->getArea(), + ); } } } diff --git a/Classes/ViewHelpers/Image/CropVariantViewHelper.php b/Classes/ViewHelpers/Image/CropVariantViewHelper.php index e058579..583ba50 100644 --- a/Classes/ViewHelpers/Image/CropVariantViewHelper.php +++ b/Classes/ViewHelpers/Image/CropVariantViewHelper.php @@ -3,23 +3,31 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image; -use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; -use TYPO3\CMS\Core\Resource\FileInterface; +use Sitegeist\MediaComponents\Domain\Model\CropArea; +use Sitegeist\MediaComponents\Domain\Model\ImageSource; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class CropVariantViewHelper extends AbstractViewHelper { public function initializeArguments(): void { - $this->registerArgument('image', FileInterface::class, 'FAL file'); + $this->registerArgument('image', ImageSource::class, 'Image object'); $this->registerArgument('name', 'string', 'name of the crop variant that should be used', false, 'default'); } - public function render(): Area + public function render(): CropArea { $this->arguments['image'] ??= $this->renderChildren(); - $cropVariantCollection = CropVariantCollection::create((string)$this->arguments['image']->getProperty('crop')); - return $cropVariantCollection->getCropArea($this->arguments['name']); + if (!($this->arguments['image']->getOriginalImage() instanceof ImageWithDimensions)) { + return $this->arguments['image']; + } + + if ($this->arguments['image']->getOriginalImage() instanceof ImageWithCropVariants) { + return new CropArea($this->arguments['image']->getOriginalImage()->getCropVariant($this->arguments['name'])); + } else { + return new CropArea; + } } } diff --git a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php index d3bd581..7e309c4 100644 --- a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php @@ -24,8 +24,8 @@ public function render(): ImageSource return $imageSource; } if ($this->arguments['height'] || $this->arguments['width']) { - $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getOriginalImage()->getHeight() : 1; - $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getOriginalImage()->getWidth() : 1; + $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getCroppedWidth() : 1; + $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getCroppedHeight() : 1; $scaleFactor = ($this->arguments['maxDimensions']) ? min($heightFactor, $widthFactor) : max($heightFactor, $widthFactor); diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index e340ec4..18d5064 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -5,8 +5,7 @@ use Sitegeist\MediaComponents\Domain\Model\ImageSource; use Sitegeist\MediaComponents\Domain\Model\SourceSet; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Service\ImageService; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class SrcsetViewHelper extends AbstractViewHelper @@ -24,6 +23,11 @@ public function render(): string return ''; } $this->arguments['imageSource'] ??= $this->renderChildren(); + + if (!($this->arguments['imageSource']->getOriginalImage() instanceof ImageWithDimensions)) { + return ''; + } + return self::generateSrcsetString($this->arguments['imageSource'], $this->arguments['srcset'], $this->arguments['base']); } @@ -32,12 +36,11 @@ public static function generateSrcsetString(ImageSource $imageSource, SourceSet $output = []; $base ??= $imageSource; $widths = $srcset->getSrcsetAndWidths($base->getWidth()); - $imageService = GeneralUtility::makeInstance(ImageService::class); $localImageSource = clone $imageSource; foreach ($widths as $widthDescriptor => $width) { - $localImageSource->setScale($width / $imageSource->getOriginalImage()->getWidth()); - $output[] = $imageService->getImageUri($localImageSource->getImage()->getFile()) . ' ' . $widthDescriptor; + $localImageSource->setScale($width / $localImageSource->getCroppedWidth()); + $output[] = (string) $localImageSource . ' ' . $widthDescriptor; } return implode(', ', $output); diff --git a/Resources/Private/Components/Image/Image.html b/Resources/Private/Components/Image/Image.html index 2d9dc95..933713e 100644 --- a/Resources/Private/Components/Image/Image.html +++ b/Resources/Private/Components/Image/Image.html @@ -12,6 +12,7 @@ + @@ -27,6 +28,9 @@ + + + + @@ -22,6 +23,9 @@ + + + \\s+\\<', str_replace("\n", '', (string)$result))); + return trim((string) preg_replace('/\\>\\s+\\<', str_replace("\n", '', (string) $result))); } protected function getTestView($html = '') From 2b4e34ff4690d6b62939c58fd871289246d698bd Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Tue, 8 Oct 2024 15:57:14 +0200 Subject: [PATCH 20/22] [TASK] add media components to global fluid namespace "fc" --- ext_localconf.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext_localconf.php b/ext_localconf.php index fc548b2..1abaec3 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -12,6 +12,9 @@ defined('TYPO3') or die(); +$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['fc'] ??= []; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['fc'][] = 'Sitegeist\\MediaComponents\\Components'; + $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['namespaces']['Sitegeist\\MediaComponents\\Components'] = ExtensionManagementUtility::extPath('media_components', 'Resources/Private/Components'); From ef75e0768066197a6a20dc55d23a636824fa72b7 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Fri, 11 Oct 2024 15:05:39 +0200 Subject: [PATCH 21/22] [TASK] add some examples to README --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6be1651..5892beb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Media Components -**This extension is still in development and is clearly not ready for production usage.** +**This extension is still in development and might not ready for production usage.** This extension provides ready-to-use [Fluid Components](https://github.com/sitegeist/fluid-components) for various media assets: @@ -12,13 +12,74 @@ This extension provides ready-to-use [Fluid Components](https://github.com/siteg * Video files * [HTML5 video tag with support for subtitles](./Resources/Private/Components/Video/Video.html) -## Current development +## Usage -We try to decouple Fluid Components and Media Components while also enhancing the basic data structures in Fluid Components during development of Media Components. This leads to several Pull Requests in Fluid Components, which need to be merged and released before Media Components can be released. +We use the public namespace from fluid-components. -Current Pull Requests: +### Images +```html + -* [Respect class inheritance during component argument conversion](https://github.com/sitegeist/fluid-components/pull/121) -* [Provide image dimensions in data structures](https://github.com/sitegeist/fluid-components/pull/122) + +``` -If you want to participate in the development, feel free to get in contact. You can also create issues, review pull requests or even contribute code. +### Pictures +```html + +``` + +### Video +```html + +``` + +### Audio +```html + +``` From 764f47aa8c14a8f9d42d23a0dfbd9f7ebe71ae36 Mon Sep 17 00:00:00 2001 From: Ulrich Mathes Date: Fri, 11 Oct 2024 15:06:54 +0200 Subject: [PATCH 22/22] [TASK] use required 3.8.1 tag from fluid-components this contains the image dimensions and rendering within fluid-components --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 24f281c..7648720 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ }, "require": { "php": "^8.2", - "sitegeist/fluid-components": "^3.7", + "sitegeist/fluid-components": "^3.8.1", "typo3fluid/fluid": "^4.0", "sitegeist/fluid-tagbuilder": "^1 || dev-main", "typo3/cms-core": "^13.3 || dev-main"