diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index af4723c7f5651a4da1368f181dbbe2b6d05b7ab9..9d4f7ed7a42035e8a3d5d12973058c9118dd35e5 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -174,6 +174,7 @@ Options: -s <...> Specifies the test suite to run - acceptance: main application acceptance tests + - acceptanceComposer: main application acceptance tests - acceptanceInstall: installation acceptance tests, only with -d mariadb|postgres|sqlite - buildCss: execute scss to css builder - buildJavascript: execute typescript to javascript builder @@ -236,7 +237,7 @@ Options: - pdo_mysql -d <sqlite|mariadb|mysql|postgres> - Only with -s functional|functionalDeprecated|acceptance|acceptanceInstall + Only with -s functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall Specifies on which DBMS tests are performed - sqlite: (default): use sqlite - mariadb: use mariadb @@ -293,12 +294,12 @@ Options: Build/Scripts/runTests.sh -s unit -- --filter filterByValueRecursiveCorrectlyFiltersArray -g - Only with -s acceptance|acceptanceInstall + Only with -s acceptance|acceptanceComposer|acceptanceInstall Activate selenium grid as local port to watch browser clicking around. Can be surfed using http://localhost:7900/. A browser tab is opened automatically if xdg-open is installed. -x - Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom|acceptance|acceptanceInstall + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom|acceptance|acceptanceComposer|acceptanceInstall 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 @@ -566,9 +567,9 @@ fi # Suite execution case ${TEST_SUITE} in acceptance) - CODECEPION_ENV="--env ci" + CODECEPION_ENV="--env ci,classic" if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then - CODECEPION_ENV="--env ci,headless" + CODECEPION_ENV="--env ci,classic,headless" fi if [ "${CHUNKS}" -gt 0 ]; then ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-splitter-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/splitAcceptanceTests.php -v ${CHUNKS} @@ -630,6 +631,75 @@ case ${TEST_SUITE} in ;; esac ;; + acceptanceComposer) + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance-composer" "${CORE_ROOT}/typo3temp/var/tests/AcceptanceReports" + + PREPAREPARAMS="" + TESTPARAMS="" + case ${DBMS} in + mariadb) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=acp -e MYSQL_DATABASE=ac_test --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-ac-${SUFFIX} 3306 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=mysqli -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=root -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=mariadb-ac-${SUFFIX} -e TYPO3_DB_PORT=3306" + TESTPARAMS="-e typo3DatabaseName=ac_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mariadb-ac-${SUFFIX}" + ;; + mysql) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=acp -e MYSQL_DATABASE=ac_test --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-ac-${SUFFIX} 3306 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=mysqli -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=root -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=mysql-ac-${SUFFIX} -e TYPO3_DB_PORT=3306" + TESTPARAMS="-e typo3DatabaseName=ac_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mysql-ac-${SUFFIX}" + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-ac-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_DB=ac_test -e POSTGRES_PASSWORD=acp -e POSTGRES_USER=ac_test --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-ac-${SUFFIX} 5432 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=postgres -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=ac_test -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=postgres-ac-${SUFFIX} -e TYPO3_DB_PORT=5432" + TESTPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=ac_test -e typo3DatabaseUsername=ac_test -e typo3DatabasePassword=acp -e typo3DatabaseHost=postgres-ac-${SUFFIX}" + ;; + sqlite) + PREPAREPARAMS="-e TYPO3_DB_DRIVER=sqlite" + TESTPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + ;; + esac + + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name acceptance-prepare ${XDEBUG_MODE} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${PREPAREPARAMS} ${IMAGE_PHP} "${CORE_ROOT}/Build/Scripts/setupAcceptanceComposer.sh" "typo3temp/var/tests/acceptance-composer" + SUITE_EXIT_CODE=$? + if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + CODECEPION_ENV="--env ci,composer" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,composer,headless" + fi + if [ "${CHUNKS}" -gt 0 ]; then + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-splitter-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/splitAcceptanceTests.php -v ${CHUNKS} + COMMAND=(bin/codecept run Application -d -g AcceptanceTests-Job-${THISCHUNK} -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + else + COMMAND=(bin/codecept run Application -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + fi + SELENIUM_GRID="" + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + SELENIUM_GRID="-p 7900:7900 -e SE_VNC_NO_PASSWORD=1 -e VNC_NO_PASSWORD=1" + fi + APACHE_OPTIONS="-e APACHE_RUN_USER=#${HOST_UID} -e APACHE_RUN_SERVERNAME=web -e APACHE_RUN_GROUP=#${HOST_PID} -e APACHE_RUN_DOCROOT=${CORE_ROOT}/typo3temp/var/tests/acceptance-composer/public -e PHPFPM_HOST=phpfpm -e PHPFPM_PORT=9000" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d ${SELENIUM_GRID} --name ac-chrome-${SUFFIX} --network ${NETWORK} --network-alias chrome --tmpfs /dev/shm:rw,nosuid,nodev,noexec ${IMAGE_SELENIUM} >/dev/null + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} run --rm -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -e PHPFPM_USER=${HOST_UID} -e PHPFPM_GROUP=${HOST_PID} -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web --add-host "${CONTAINER_HOST}:host-gateway" -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + else + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm ${USERSET} -e PHPFPM_USER=0 -e PHPFPM_GROUP=0 -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm -R ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + fi + waitFor chrome 4444 + waitFor chrome 7900 + waitFor web 80 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "xdg-open" >/dev/null; then + xdg-open http://localhost:7900/?autoconnect=1 >/dev/null + elif [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "open" >/dev/null; then + open http://localhost:7900/?autoconnect=1 >/dev/null + fi + + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-${DBMS}-composer ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${TESTPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + fi + ;; acceptanceInstall) SELENIUM_GRID="" if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then @@ -1009,7 +1079,7 @@ echo "########################################################################## echo "Result of ${TEST_SUITE}" >&2 echo "Container runtime: ${CONTAINER_BIN}" >&2 echo "PHP: ${PHP_VERSION}" >&2 -if [[ ${TEST_SUITE} =~ ^(functional|functionalDeprecated|acceptance|acceptanceInstall)$ ]]; then +if [[ ${TEST_SUITE} =~ ^(functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall)$ ]]; then case "${DBMS}" in mariadb|mysql|postgres) echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 diff --git a/Build/Scripts/setupAcceptanceComposer.sh b/Build/Scripts/setupAcceptanceComposer.sh new file mode 100755 index 0000000000000000000000000000000000000000..5e5f43b9375bdccc9f346b2f6b197642f1ed9b72 --- /dev/null +++ b/Build/Scripts/setupAcceptanceComposer.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd "$(dirname $(realpath $0))/../../" + +PROJECT_PATH=${1:-typo3temp/var/tests/acceptance-composer/} +export TYPO3_DB_DRIVER=${2:-${TYPO3_DB_DRIVER:-sqlite}} +EXTRA_PACKAGES="${3}" + +mkdir -p "${PROJECT_PATH}" +ln -snf $(echo "${PROJECT_PATH}" | sed -e 's/[^\/][^\/]*/../g' -e 's/\/$//')/typo3/sysext "${PROJECT_PATH}/typo3-sysext" +ln -snf $(echo "${PROJECT_PATH}" | sed -e 's/[^\/][^\/]*/../g' -e 's/\/$//')/Build/tests/packages "${PROJECT_PATH}/packages" +sed 's/..\/..\/typo3\/sysext/typo3-sysext/' Build/composer/composer.dist.json > "${PROJECT_PATH}/composer.json" + +cd "${PROJECT_PATH}" +rm -rf composer.lock config/ public/ var/ vendor/ + +mkdir -p "config/system/" +cat > "config/system/additional.php" <<\EOF +<?php +$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] = true; +// "temporary password" +$GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'] = '$argon2i$v=19$m=65536,t=16,p=1$Rk9Edk1UWTd1MUtVY1Nydg$bJJgiAH3NT66LkvcTsnYbQvFS/ePOw/50rYjhxUk8L8'; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['displayErrors'] = true; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*'; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['exceptionalErrors'] = E_ALL; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandlerErrors'] = E_ALL; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = '.*'; +$GLOBALS['TYPO3_CONF_VARS']['GFX']['processor'] = 'GraphicsMagick'; +$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = 'mbox'; +$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_mbox_file'] = \TYPO3\CMS\Core\Core\Environment::getVarPath() . '/log/mail.mbox'; +EOF + +# `composer require` will implicitly perform an initial `composer install` since there is no composer.lock +composer require --no-progress --no-interaction --dev typo3tests/dataset-import:@dev typo3/testing-framework:dev-main ${EXTRA_PACKAGES} + +TYPO3_SERVER_TYPE=apache \ +TYPO3_PROJECT_NAME="New TYPO3 site" \ +vendor/bin/typo3 setup --force --no-interaction + +vendor/bin/typo3 dataset:import vendor/typo3/cms-core/Tests/Acceptance/Fixtures/BackendEnvironment.csv +vendor/bin/typo3 styleguide:generate -c -- all + +# Create favicon.ico to suppress potential javascript errors in console +# which are caused by calling a non html in the browser, e.g. seo sitemap xml +ln -snf ../vendor/typo3/cms-backend/Resources/Public/Icons/favicon.ico public/favicon.ico + +# @todo: needed for ugly InstallTool tests, that should be replace by a CLI command that properly enables install tool, both in composer and classic mode +mkdir -p var/transient/ diff --git a/Build/composer/composer.dist.json b/Build/composer/composer.dist.json index 1b4f727044a498714878e83d6bd40652cb3d1dd7..14960b281b7a0cbc84f811e1e43c8c225ebb2149 100644 --- a/Build/composer/composer.dist.json +++ b/Build/composer/composer.dist.json @@ -49,7 +49,8 @@ "typo3/cms-sys-note": "13.1.x-dev", "typo3/cms-t3editor": "13.1.x-dev", "typo3/cms-tstemplate": "13.1.x-dev", - "typo3/cms-viewpage": "13.1.x-dev" + "typo3/cms-viewpage": "13.1.x-dev", + "typo3/cms-workspaces": "13.1.x-dev" } } }, @@ -92,6 +93,7 @@ "typo3/cms-sys-note": "@dev", "typo3/cms-t3editor": "@dev", "typo3/cms-tstemplate": "@dev", - "typo3/cms-viewpage": "@dev" + "typo3/cms-viewpage": "@dev", + "typo3/cms-workspaces": "@dev" } } diff --git a/Build/gitlab-ci.yml b/Build/gitlab-ci.yml index ec1bdfbc16098900097e1ac3a21d4ea846536b22..cac1d25b70ad81c24dbb8f706a32e085854057c3 100644 --- a/Build/gitlab-ci.yml +++ b/Build/gitlab-ci.yml @@ -41,6 +41,7 @@ include: # turns this into a branch 'change-patchset' which executes the pipeline - local: 'Build/gitlab-ci/pre-merge/acceptance-install.yml' - local: 'Build/gitlab-ci/pre-merge/acceptance-application.yml' + - local: 'Build/gitlab-ci/pre-merge/acceptance-application-composer.yml' - local: 'Build/gitlab-ci/pre-merge/integrity.yml' - local: 'Build/gitlab-ci/pre-merge/functional.yml' - local: 'Build/gitlab-ci/pre-merge/unit.yml' @@ -49,4 +50,5 @@ include: - local: 'Build/gitlab-ci/nightly/unit.yml' - local: 'Build/gitlab-ci/nightly/acceptance-install.yml' - local: 'Build/gitlab-ci/nightly/acceptance-application.yml' + - local: 'Build/gitlab-ci/nightly/acceptance-application-composer.yml' - local: 'Build/gitlab-ci/nightly/functional.yml' diff --git a/Build/gitlab-ci/nightly/acceptance-application-composer.yml b/Build/gitlab-ci/nightly/acceptance-application-composer.yml new file mode 100644 index 0000000000000000000000000000000000000000..164d8df903adc7c4661e7a57a3bcdf2b704d7a24 --- /dev/null +++ b/Build/gitlab-ci/nightly/acceptance-application-composer.yml @@ -0,0 +1,163 @@ +acceptance application composer mariadb 10.4 php 8.2 min: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-min + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMin -p 8.2 + - Build/Scripts/runTests.sh -s acceptanceComposer -d mariadb -i 10.4 -p 8.2 -c $CI_NODE_INDEX/$CI_NODE_TOTAL +acceptance application composer mariadb 10.10 php 8.3 max: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-max + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMax -p 8.3 + - Build/Scripts/runTests.sh -s acceptanceComposer -d mariadb -i 10.10 -p 8.3 -c $CI_NODE_INDEX/$CI_NODE_TOTAL + +acceptance application composer mysql 8.0 php 8.3 max: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-max + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMax -p 8.3 + - Build/Scripts/runTests.sh -s acceptanceComposer -d mysql -i 8.0 -p 8.3 -c $CI_NODE_INDEX/$CI_NODE_TOTAL +acceptance application composer mysql 8.0 php 8.2 min: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-min + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMin -p 8.2 + - Build/Scripts/runTests.sh -s acceptanceComposer -d mysql -i 8.0 -p 8.2 -c $CI_NODE_INDEX/$CI_NODE_TOTAL + +acceptance application composer sqlite php 8.3 max: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-max + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMax -p 8.3 + - Build/Scripts/runTests.sh -s acceptanceComposer -d sqlite -p 8.3 -c $CI_NODE_INDEX/$CI_NODE_TOTAL +acceptance application composer sqlite php 8.2 min: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-min + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMin -p 8.2 + - Build/Scripts/runTests.sh -s acceptanceComposer -d sqlite -p 8.2 -c $CI_NODE_INDEX/$CI_NODE_TOTAL + +acceptance application composer postgres 15 php 8.3 max: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-max + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMax -p 8.3 + - Build/Scripts/runTests.sh -s acceptanceComposer -d postgres -i 15 -p 8.3 -c $CI_NODE_INDEX/$CI_NODE_TOTAL +acceptance application composer postgres 10 php 8.2 min: + stage: acceptance + tags: + - metal2 + needs: [] + only: + - schedules + cache: + key: main-composer-min + paths: + - .cache + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 8 + script: + - Build/Scripts/runTests.sh -s composerInstallMin -p 8.2 + - Build/Scripts/runTests.sh -s acceptanceComposer -d postgres -i 10 -p 8.2 -c $CI_NODE_INDEX/$CI_NODE_TOTAL diff --git a/Build/gitlab-ci/pre-merge/acceptance-application-composer.yml b/Build/gitlab-ci/pre-merge/acceptance-application-composer.yml new file mode 100644 index 0000000000000000000000000000000000000000..f3d68f7de5f7be71b48073cf733b3f7cb25899b0 --- /dev/null +++ b/Build/gitlab-ci/pre-merge/acceptance-application-composer.yml @@ -0,0 +1,17 @@ +acceptance application composer postgres 15 php 8.2 pre-merge: + stage: main + tags: + - metal2 + except: + refs: + - schedules + - main + artifacts: + when: on_failure + paths: + - typo3temp/var/tests/acceptance-composer/var/log + - typo3temp/var/tests/AcceptanceReports + parallel: 13 + script: + - Build/Scripts/runTests.sh -s composerInstall -p 8.2 + - Build/Scripts/runTests.sh -s acceptanceComposer -p 8.2 -d postgres -i 15 -c $CI_NODE_INDEX/$CI_NODE_TOTAL diff --git a/Build/tests/packages/dataset_import/Classes/Command/DatasetImportCommand.php b/Build/tests/packages/dataset_import/Classes/Command/DatasetImportCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..eb51b6457e932073b58ce8d2c6d784ef3ac97272 --- /dev/null +++ b/Build/tests/packages/dataset_import/Classes/Command/DatasetImportCommand.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3Tests\DatasetImport\Command; + +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\DataSet; + +/** + * CLI command for setting up TYPO3 via CLI + */ +#[AsCommand('dataset:import', 'Import Dataset')] +class DatasetImportCommand extends Command +{ + protected function configure(): void + { + $this->addArgument('path', InputArgument::REQUIRED, 'Path to CSV dataset to import'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (!class_exists(DataSet::class)) { + $io = new SymfonyStyle($input, $output); + $io->getErrorStyle()->error('Missing typo3/testing-framework dependency.'); + return Command::FAILURE; + } + DataSet::import($input->getArgument('path')); + return Command::SUCCESS; + } + +} diff --git a/Build/tests/packages/dataset_import/Configuration/Services.yaml b/Build/tests/packages/dataset_import/Configuration/Services.yaml new file mode 100644 index 0000000000000000000000000000000000000000..adae8cd8e361dd523676d7e1ea58009de90bd045 --- /dev/null +++ b/Build/tests/packages/dataset_import/Configuration/Services.yaml @@ -0,0 +1,8 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + TYPO3Tests\DatasetImport\: + resource: '../Classes/*' diff --git a/Build/tests/packages/dataset_import/composer.json b/Build/tests/packages/dataset_import/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..9f1d4b403404d74345a67a44d6b31180779ba80d --- /dev/null +++ b/Build/tests/packages/dataset_import/composer.json @@ -0,0 +1,20 @@ +{ + "name": "typo3tests/dataset-import", + "type": "typo3-cms-extension", + "description": "Support extension providing dataset:import command", + "license": "GPL-2.0-or-later", + "require": { + "typo3/cms-core": "13.1.*@dev", + "typo3/testing-framework": "dev-main" + }, + "extra": { + "typo3/cms": { + "extension-key": "dataset_import" + } + }, + "autoload": { + "psr-4": { + "TYPO3Tests\\DatasetImport\\": "Classes/" + } + } +} diff --git a/typo3/sysext/core/Tests/Acceptance/Application.suite.yml b/typo3/sysext/core/Tests/Acceptance/Application.suite.yml index 4ccfcfbef30eaaf467e3f71f929f7656b58e10e6..d4e2887249347cd953057fe48a93021ba2a900c6 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application.suite.yml +++ b/typo3/sysext/core/Tests/Acceptance/Application.suite.yml @@ -16,10 +16,14 @@ modules: editor: '%typo3TestingAcceptanceEditorPassword%' env: - ci: + classic: extensions: enabled: - TYPO3\CMS\Core\Tests\Acceptance\Support\Extension\ApplicationEnvironment + composer: + extensions: + enabled: + - TYPO3\CMS\Core\Tests\Acceptance\Support\Extension\ApplicationComposerEnvironment groups: AcceptanceTests-Job-*: AcceptanceTests-Job-* diff --git a/typo3/sysext/core/Tests/Acceptance/Application/BackendUser/ListUserCest.php b/typo3/sysext/core/Tests/Acceptance/Application/BackendUser/ListUserCest.php index 910bf40fd0cc138b31ddc9626d66dd21898b3cd4..57b66fc726cb9b04e92086f7cce510d61e5beb5b 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/BackendUser/ListUserCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/BackendUser/ListUserCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\BackendUser; +use Codeception\Scenario; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; /** @@ -34,23 +35,38 @@ final class ListUserCest $I->switchToContentFrame(); } - public function showsHeadingAndListsBackendUsers(ApplicationTester $I): void + public function showsHeadingAndListsBackendUsers(ApplicationTester $I, Scenario $scenario): void { $I->see('Backend users'); $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $this->checkCountOfUsers($I, 4); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + // We expect exactly four Backend Users to have been created by the fixtures + $expectedUsers = 4; + if ($isComposerMode) { + // User _cli_ will additionally be available in composer mode, created + // by execution of `vendor/bin/typo3` CLI in setup script. + $expectedUsers++; + } + $this->checkCountOfUsers($I, $expectedUsers); } - public function filterUsersByUsername(ApplicationTester $I): void + public function filterUsersByUsername(ApplicationTester $I, Scenario $scenario): void { $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', 4); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + $expectedUsers = 4; + if ($isComposerMode) { + $expectedUsers++; + } + $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', $expectedUsers); $I->wantTo('Filter the list of user by valid username admin'); $I->fillField('#tx_Beuser_username', 'admin'); @@ -71,12 +87,18 @@ final class ListUserCest $this->checkCountOfUsers($I, 0); } - public function filterUsersByAdmin(ApplicationTester $I): void + public function filterUsersByAdmin(ApplicationTester $I, Scenario $scenario): void { $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', 4); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + $expectedUsers = 4; + if ($isComposerMode) { + $expectedUsers++; + } + $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', $expectedUsers); $I->wantToTest('Filter BackendUser and see only admins'); $I->selectOption('#tx_Beuser_usertype', 'Admin only'); @@ -84,8 +106,8 @@ final class ListUserCest $I->waitForElementNotVisible('div#nprogess'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact two fitting Backend Users created from the Fixtures - $this->checkCountOfUsers($I, 2); + // We expect exact two (composer-mode: three) fitting Backend Users created from the Fixtures + $this->checkCountOfUsers($I, 2 + ($isComposerMode ? 1 : 0)); $I->wantToTest('Filter BackendUser and see normal users'); $I->selectOption('#tx_Beuser_usertype', 'Normal users only'); @@ -97,12 +119,18 @@ final class ListUserCest $this->checkCountOfUsers($I, 2); } - public function filterUsersByStatus(ApplicationTester $I): void + public function filterUsersByStatus(ApplicationTester $I, Scenario $scenario): void { $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', 4); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + $expectedUsers = 4; + if ($isComposerMode) { + $expectedUsers++; + } + $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', $expectedUsers); $I->wantToTest('Filter BackendUser and see only active users'); $I->selectOption('#tx_Beuser_status', 'Active only'); @@ -110,8 +138,8 @@ final class ListUserCest $I->waitForElementNotVisible('div#nprogess'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact two fitting Backend Users created from the Fixtures - $this->checkCountOfUsers($I, 2); + // We expect exact two (composer-mode three) fitting Backend Users created from the Fixtures + $this->checkCountOfUsers($I, 2 + ($isComposerMode ? 1 : 0)); $I->wantToTest('Filter BackendUser and see only inactive users'); $I->selectOption('#tx_Beuser_status', 'Inactive only'); @@ -123,12 +151,18 @@ final class ListUserCest $this->checkCountOfUsers($I, 2); } - public function filterUsersByLogin(ApplicationTester $I): void + public function filterUsersByLogin(ApplicationTester $I, Scenario $scenario): void { $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', 4); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + $expectedUsers = 4; + if ($isComposerMode) { + $expectedUsers++; + } + $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', $expectedUsers); $I->wantToTest('Filter BackendUser and see only users logged in before'); $I->selectOption('#tx_Beuser_logins', 'Logged in before'); @@ -145,16 +179,22 @@ final class ListUserCest $I->waitForElementNotVisible('div#nprogess'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact two fitting Backend Users created from the Fixtures - $this->checkCountOfUsers($I, 2); + // We expect exact two (composer-mode three) fitting Backend Users created from the Fixtures + $this->checkCountOfUsers($I, 2 + ($isComposerMode ? 1 : 0)); } - public function filterUsersByUserGroup(ApplicationTester $I): void + public function filterUsersByUserGroup(ApplicationTester $I, Scenario $scenario): void { $I->wantTo('See the table of users'); $I->waitForElementVisible('#typo3-backend-user-list'); - // We expect exact four Backend Users created from the Fixtures - $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', 4); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $isComposerMode = str_contains($scenario->current('env'), 'composer'); + $expectedUsers = 4; + if ($isComposerMode) { + $expectedUsers++; + } + $I->canSeeNumberOfElements('#typo3-backend-user-list tbody tr', $expectedUsers); // We expect exact one Backend Users created from the Fixtures has the usergroup named 'editor-group' $I->wantToTest('Filter BackendUser and see only users with given usergroup'); @@ -170,18 +210,22 @@ final class ListUserCest public function canEditUsersFromIndexListView(ApplicationTester $I): void { $I->canSee('Backend users', 'h1'); - $username = $I->grabTextFrom('#typo3-backend-user-list > tbody > tr:nth-child(1) > td.col-title > a:nth-child(1) > b'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $I->click('button[value="reset-filters"]'); + $I->waitForElementVisible('#typo3-backend-user-list'); + $username = 'admin'; + $adminRow = '//*[@id="typo3-backend-user-list"]//tr[contains(td[2]/a[1]/b[1], "' . $username . '")]'; $I->amGoingTo('test the edit button'); - $I->click('#typo3-backend-user-list > tbody > tr:nth-child(1) > td.col-control > div:nth-child(1) > a'); + $I->click($adminRow . '//div[@role="group"]/a[@title="Edit"]'); $this->openAndCloseTheEditForm($I, $username); $I->amGoingTo('test the edit link on username'); - $I->click('#typo3-backend-user-list > tbody > tr:nth-child(1) > td.col-title > a:nth-child(1)'); + $I->click($adminRow . '//td[@class="col-title"]/a[1]'); $this->openAndCloseTheEditForm($I, $username); $I->amGoingTo('test the edit link on real name'); - $I->click('#typo3-backend-user-list > tbody > tr:nth-child(1) > td.col-title > a:nth-child(4)'); + $I->click($adminRow . '//td[@class="col-title"]/a[2]'); $this->openAndCloseTheEditForm($I, $username); } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/GetExtensionsCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/GetExtensionsCest.php index aec2d2505dc5324df086b899422a3a26fc8c9be6..89dbbaa169fbd64bf3411d5fb71478038b0c0fd7 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/GetExtensionsCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/GetExtensionsCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\Extensionmanager; +use Codeception\Attribute\Env; use Facebook\WebDriver\WebDriverKeys; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; @@ -47,18 +48,21 @@ final class GetExtensionsCest $I->seeNumberOfElements('#terTable tbody tr', 2); } + #[Env('classic')] public function checkRetrievedExtensionsFromTerAreDisplayed(ApplicationTester $I): void { $I->see('superext'); $I->see('neededext'); } + #[Env('classic')] public function checkPaginationIsNotDisplayedForTwoRecords(ApplicationTester $I): void { $I->dontSeeElement('.pagination-wrap'); $I->dontSee('Extensions 1 - 2'); } + #[Env('classic')] public function checkSearchFilterListFindsExtensionKey(ApplicationTester $I): void { $I->fillField('input[name="search"]', 'superext'); @@ -81,6 +85,7 @@ final class GetExtensionsCest $I->see('Needed Extension'); } + #[Env('classic')] public function checkSearchFilterListFindsPartOfExtensionKey(ApplicationTester $I): void { $I->fillField('input[name="search"]', 'ext'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/InstalledExtensionsCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/InstalledExtensionsCest.php index c3aee7e301464b41dd2d4cf80d9cc601115b67df..915b3d47632d8ea05b1c74411096e720881c34ee 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/InstalledExtensionsCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Extensionmanager/InstalledExtensionsCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\Extensionmanager; +use Codeception\Attribute\Env; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; /** @@ -61,6 +62,7 @@ final class InstalledExtensionsCest $I->seeNumberOfElements('#typo3-extension-list tbody tr[role="row"]', [10, 100]); } + #[Env('classic')] public function checkIfUploadFormAppears(ApplicationTester $I): void { $I->cantSeeElement('.module-body .extension-upload-form'); @@ -68,6 +70,7 @@ final class InstalledExtensionsCest $I->seeElement('.module-body .extension-upload-form'); } + #[Env('classic')] public function checkUninstallingAndInstallingAnExtension(ApplicationTester $I): void { $I->wantTo('Check if uninstalling and installing an extension with backend module removes and adds the module from the module menu.'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/FalMetadataCest.php b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/FalMetadataCest.php index f541dcdf772e1ac6ac19130e756b12504fe6ccaf..a6b31ccb64cc9c6a6dd25e3e693a33151c4fdfaf 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/FalMetadataCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/FalMetadataCest.php @@ -108,7 +108,10 @@ final class FalMetadataCest $I->waitForElementNotVisible('#t3js-ui-block'); $I->waitForText('Edit Page Content "tt_content with image" on page "styleguide TCA demo"'); $I->click('Images'); - $I->click('.form-irre-header'); + if (count($I->grabMultiple('.panel-collapsed .form-irre-header')) > 0) { + $I->click('.panel-collapsed .form-irre-header'); + } + $I->waitForElement('.t3js-form-field-eval-null-placeholder-checkbox'); $I->see('(Default: "Test title")', '.t3js-form-field-eval-null-placeholder-checkbox'); $I->see('(Default: "Test alternative")', '.t3js-form-field-eval-null-placeholder-checkbox'); @@ -203,7 +206,10 @@ final class FalMetadataCest $I->waitForElementNotVisible('#t3js-ui-block'); $I->waitForText('Edit Page Content "tt_content with image" on page "styleguide TCA demo"'); $I->click('Images'); - $I->click('.form-irre-header'); + if (count($I->grabMultiple('.panel-collapsed .form-irre-header')) > 0) { + $I->click('.panel-collapsed .form-irre-header'); + } + $I->waitForElement('.t3js-form-field-eval-null-placeholder-checkbox'); $I->see('(Default: "Test title")', '.t3js-form-field-eval-null-placeholder-checkbox'); $I->see('(Default: "Test alternative")', '.t3js-form-field-eval-null-placeholder-checkbox'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/Inline1nCest.php b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/Inline1nCest.php index 8128e2e91203f92e045d7321105294e4f45a94e0..92b92d22d9ce10fef1a9c1d71cc0af0b10cdff69 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/Inline1nCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/Inline1nCest.php @@ -89,6 +89,11 @@ final class Inline1nCest $I->see('lipsum', '#recordlist-tx_styleguide_inline_1n_inline_1_child > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(3) > a'); $I->see('Fo Bar', '#recordlist-tx_styleguide_inline_1n_inline_1_child > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(2) > tr:nth-child(4) > td:nth-child(3) > a'); + + $I->click('button[data-table="tx_styleguide_inline_1n"] .icon-actions-view-list-expand'); + $I->wait(1); + $I->click('button[data-table="pages_translated"] .icon-actions-view-list-expand'); + $I->wait(1); } /** @@ -111,6 +116,11 @@ final class Inline1nCest $I->wantTo('Check new sorting'); $I->see('Fo Bar', '#recordlist-tx_styleguide_inline_1n_inline_1_child > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(3) > a'); $I->see('lipsum', '#recordlist-tx_styleguide_inline_1n_inline_1_child > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(2) > tr:nth-child(2) > td:nth-child(3) > a'); + + $I->click('button[data-table="tx_styleguide_inline_1n"] .icon-actions-view-list-expand'); + $I->wait(1); + $I->click('button[data-table="pages_translated"] .icon-actions-view-list-expand'); + $I->wait(1); } public function changeInline1nInlineInput(ApplicationTester $I): void diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/ContentElementsCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/ContentElementsCest.php index 7e0fdf5552d9edc9c3838a4354e48e7fe2b5ed07..43580c49874e38b7fec8c57570f636c5198104ad 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/ContentElementsCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/ContentElementsCest.php @@ -31,6 +31,8 @@ final class ContentElementsCest $I->click('Page'); $pageTree->openPath(['styleguide frontend demo']); $I->switchToContentFrame(); + $I->waitForElementVisible('select[name=actionMenu]'); + $I->selectOption('select[name=actionMenu]', 'Layout'); $I->waitForElementVisible('.t3js-module-docheader-bar a[title="View webpage"]'); $I->wait(1); $I->click('.t3js-module-docheader-bar a[title="View webpage"]'); @@ -49,9 +51,13 @@ final class ContentElementsCest // Close FE tab again and switch to BE to avoid side effects $I->executeInSelenium(static function (RemoteWebDriver $webdriver) { $handles = $webdriver->getWindowHandles(); - $webdriver->close(); - $firstWindow = current($handles); - $webdriver->switchTo()->window($firstWindow); + // Avoid closing the main backend tab (holds the webdriver session) if the test failed to open the frontend tab + // (All subsequent tests would fail with "[Facebook\WebDriver\Exception\InvalidSessionIdException] invalid session id" + if (count($handles) > 1) { + $webdriver->close(); + $firstWindow = current($handles); + $webdriver->switchTo()->window($firstWindow); + } }); } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FormFrameworkCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FormFrameworkCest.php index 370fefff61cb3c8e2a011c3a87a71e92fd9e0d02..e04b2dfc765e5eceee2d7d8d5c583a45ba169712 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FormFrameworkCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FormFrameworkCest.php @@ -37,6 +37,8 @@ final class FormFrameworkCest $I->click('Page'); $pageTree->openPath(['styleguide frontend demo']); $I->switchToContentFrame(); + $I->waitForElementVisible('select[name=actionMenu]'); + $I->selectOption('select[name=actionMenu]', 'Layout'); $I->waitForElementVisible('.t3js-module-docheader-bar a[title="View webpage"]'); $I->wait(1); $I->click('.t3js-module-docheader-bar a[title="View webpage"]'); @@ -57,9 +59,13 @@ final class FormFrameworkCest // Close FE tab again and switch to BE to avoid side effects $I->executeInSelenium(static function (RemoteWebDriver $webdriver) { $handles = $webdriver->getWindowHandles(); - $webdriver->close(); - $firstWindow = current($handles); - $webdriver->switchTo()->window($firstWindow); + // Avoid closing the main backend tab (holds the webdriver session) if the test failed to open the frontend tab + // (All subsequent tests would fail with "[Facebook\WebDriver\Exception\InvalidSessionIdException] invalid session id" + if (count($handles) > 1) { + $webdriver->close(); + $firstWindow = current($handles); + $webdriver->switchTo()->window($firstWindow); + } }); } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FrontendLoginCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FrontendLoginCest.php index 30f2e0f3a0a0eccaba9ace469be1f863ea662ce5..03597fa699212e71a033f7664bf283e70ff73582 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FrontendLoginCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/FrontendLoginCest.php @@ -35,6 +35,8 @@ final class FrontendLoginCest $I->click('Page'); $pageTree->openPath(['styleguide frontend demo']); $I->switchToContentFrame(); + $I->waitForElementVisible('select[name=actionMenu]'); + $I->selectOption('select[name=actionMenu]', 'Layout'); $I->waitForElementVisible('.t3js-module-docheader-bar a[title="View webpage"]'); $I->wait(1); $I->click('.t3js-module-docheader-bar a[title="View webpage"]'); @@ -55,9 +57,13 @@ final class FrontendLoginCest // Close FE tab again and switch to BE to avoid side effects $I->executeInSelenium(static function (RemoteWebDriver $webdriver) { $handles = $webdriver->getWindowHandles(); - $webdriver->close(); - $firstWindow = current($handles); - $webdriver->switchTo()->window($firstWindow); + // Avoid closing the main backend tab (holds the webdriver session) if the test failed to open the frontend tab + // (All subsequent tests would fail with "[Facebook\WebDriver\Exception\InvalidSessionIdException] invalid session id" + if (count($handles) > 1) { + $webdriver->close(); + $firstWindow = current($handles); + $webdriver->switchTo()->window($firstWindow); + } }); } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/IndexedSearchCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/IndexedSearchCest.php index e61b74ec66e1528baf8a4c7583cf1b30913328c5..363a8878f2c345a919ac489dc688dbed0aac0012 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Frontend/IndexedSearchCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Frontend/IndexedSearchCest.php @@ -36,6 +36,8 @@ final class IndexedSearchCest $I->click('Page'); $pageTree->openPath(['styleguide frontend demo']); $I->switchToContentFrame(); + $I->waitForElementVisible('select[name=actionMenu]'); + $I->selectOption('select[name=actionMenu]', 'Layout'); $I->waitForElementVisible('.t3js-module-docheader-bar a[title="View webpage"]'); $I->wait(1); $I->click('.t3js-module-docheader-bar a[title="View webpage"]'); @@ -56,9 +58,13 @@ final class IndexedSearchCest // Close FE tab again and switch to BE to avoid side effects $I->executeInSelenium(static function (RemoteWebDriver $webdriver) { $handles = $webdriver->getWindowHandles(); - $webdriver->close(); - $firstWindow = current($handles); - $webdriver->switchTo()->window($firstWindow); + // Avoid closing the main backend tab (holds the webdriver session) if the test failed to open the frontend tab + // (All subsequent tests would fail with "[Facebook\WebDriver\Exception\InvalidSessionIdException] invalid session id" + if (count($handles) > 1) { + $webdriver->close(); + $firstWindow = current($handles); + $webdriver->switchTo()->window($firstWindow); + } }); } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ImportCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ImportCest.php index 7ec1bc16c6ef272887311d6bedbb503e1005c95d..c337096351c4663345ce1cfcfa431dfe9d3a81f0 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ImportCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ImportCest.php @@ -17,7 +17,6 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\Impexp; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; use TYPO3\CMS\Core\Tests\Acceptance\Support\Helper\ModalDialog; use TYPO3\CMS\Core\Tests\Acceptance\Support\Helper\PageTree; @@ -143,7 +142,7 @@ final class ImportCest extends AbstractCest $I->canSeeElement($this->inModuleTabs . ' ' . $this->tabMessages); $flashMessage = $I->grabTextFrom($this->inFlashMessages . ' .alert.alert-success .alert-message'); preg_match('/[^"]+"([^"]+)"[^"]+"([^"]+)"[^"]+/', $flashMessage, $flashMessageParts); - $loadFilePath = Environment::getProjectPath() . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; + $loadFilePath = getenv('TYPO3_ACCEPTANCE_PATH_WEB') . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; $I->assertFileExists($loadFilePath); $this->testFilesToDelete[] = $loadFilePath; @@ -180,7 +179,7 @@ final class ImportCest extends AbstractCest $I->cantSeeElement($this->inModuleTabs . ' ' . $this->tabMessages); $flashMessage = $I->grabTextFrom($this->inFlashMessages . ' .alert.alert-success .alert-message'); preg_match('/[^"]+"([^"]+)"[^"]+"([^"]+)"[^"]+/', $flashMessage, $flashMessageParts); - $loadFilePath = Environment::getProjectPath() . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; + $loadFilePath = getenv('TYPO3_ACCEPTANCE_PATH_WEB') . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; $I->assertFileExists($loadFilePath); $this->testFilesToDelete[] = $loadFilePath; @@ -221,7 +220,7 @@ final class ImportCest extends AbstractCest $I->cantSeeElement($this->inModuleTabs . ' ' . $this->tabMessages); $flashMessage = $I->grabTextFrom($this->inFlashMessages . ' .alert.alert-success .alert-message'); preg_match('/[^"]+"([^"]+)"[^"]+"([^"]+)"[^"]+/', $flashMessage, $flashMessageParts); - $loadFilePath = Environment::getProjectPath() . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; + $loadFilePath = getenv('TYPO3_ACCEPTANCE_PATH_WEB') . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; $I->assertFileExists($loadFilePath); $this->testFilesToDelete[] = $loadFilePath; @@ -264,7 +263,7 @@ final class ImportCest extends AbstractCest $I->cantSeeElement($this->inModuleTabs . ' ' . $this->tabMessages); $flashMessage = $I->grabTextFrom($this->inFlashMessages . ' .alert.alert-success .alert-message'); preg_match('/[^"]+"([^"]+)"[^"]+"([^"]+)"[^"]+/', $flashMessage, $flashMessageParts); - $loadFilePath = Environment::getProjectPath() . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; + $loadFilePath = getenv('TYPO3_ACCEPTANCE_PATH_WEB') . '/fileadmin' . $flashMessageParts[2] . $flashMessageParts[1]; $I->assertFileExists($loadFilePath); $this->testFilesToDelete[] = $loadFilePath; diff --git a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/AbstractCest.php b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/AbstractCest.php index bee44be2fb937aee7c4cc82dd2e032ceab4e2cac..cf832d38256e9e0031e91a217cd4f8c56019a26f 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/AbstractCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/AbstractCest.php @@ -17,7 +17,6 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\InstallTool; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -25,7 +24,7 @@ use TYPO3\CMS\Install\Service\EnableFileService; class AbstractCest { - private const ADDITIONAL_CONFIGURATION_FILEPATH = 'typo3conf/system/additional.php'; + private const ADDITIONAL_CONFIGURATION_FILEPATH = '/system/additional.php'; protected const INSTALL_TOOL_PASSWORD = 'temporary password'; public function _before(ApplicationTester $I): void @@ -40,15 +39,19 @@ class AbstractCest $I->waitForText('The Install Tool is locked', 20); $I->amGoingTo('clean up created files'); - unlink(Environment::getProjectPath() . '/' . self::ADDITIONAL_CONFIGURATION_FILEPATH); + if (getenv('TYPO3_ACCEPTANCE_INSTALLTOOL_PW_PRESET') !== '1') { + unlink(getenv('TYPO3_ACCEPTANCE_PATH_CONFIG') . self::ADDITIONAL_CONFIGURATION_FILEPATH); + } $I->dontSeeFileFound($this->getEnableInstallToolFilePath()); - $I->dontSeeFileFound(Environment::getProjectPath() . '/' . self::ADDITIONAL_CONFIGURATION_FILEPATH); + if (getenv('TYPO3_ACCEPTANCE_INSTALLTOOL_PW_PRESET') !== '1') { + $I->dontSeeFileFound(getenv('TYPO3_ACCEPTANCE_PATH_CONFIG') . self::ADDITIONAL_CONFIGURATION_FILEPATH); + } } protected function getEnableInstallToolFilePath(): string { - return Environment::getVarPath() . '/transient/' . EnableFileService::INSTALL_TOOL_ENABLE_FILE_PATH; + return getenv('TYPO3_ACCEPTANCE_PATH_VAR') . '/transient/' . EnableFileService::INSTALL_TOOL_ENABLE_FILE_PATH; } protected function logIntoInstallTool(ApplicationTester $I): void @@ -65,11 +68,14 @@ class AbstractCest private function setInstallToolPassword(ApplicationTester $I): string { - $hashMethod = GeneralUtility::makeInstance(Argon2iPasswordHash::class); $password = self::INSTALL_TOOL_PASSWORD; + if (getenv('TYPO3_ACCEPTANCE_INSTALLTOOL_PW_PRESET') === '1') { + return $password; + } + $hashMethod = GeneralUtility::makeInstance(Argon2iPasswordHash::class); $hashedPassword = $hashMethod->getHashedPassword($password); $I->writeToFile( - self::ADDITIONAL_CONFIGURATION_FILEPATH, + getenv('TYPO3_ACCEPTANCE_PATH_CONFIG') . self::ADDITIONAL_CONFIGURATION_FILEPATH, '<?php' . PHP_EOL . '$GLOBALS[\'TYPO3_CONF_VARS\'][\'BE\'][\'installToolPassword\'] = \'' . $hashedPassword . '\';' ); return $password; diff --git a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/MaintenanceCest.php b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/MaintenanceCest.php index 677ab3c0eab3859e47eaf5a4fc62f10e7e3299d3..4e6ac683c183f0f836e0f163e7f9deeb49ed1ee2 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/MaintenanceCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/MaintenanceCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\InstallTool; +use Codeception\Attribute\Env; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; final class MaintenanceCest extends AbstractCest @@ -56,6 +57,7 @@ final class MaintenanceCest extends AbstractCest $I->waitForElementNotVisible('.modal-dialog'); } + #[Env('classic')] public function dumpAutoloadWorks(ApplicationTester $I): void { $I->click('Dump autoload'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/SettingsCest.php b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/SettingsCest.php index 882f1af6d1d338d0862701569a9e0c31bcdade03..50b30206d13f10e9595945906729b24db5a102c8 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/SettingsCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/SettingsCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\InstallTool; +use Codeception\Scenario; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; use TYPO3\CMS\Core\Tests\Acceptance\Support\Helper\ModalDialog; @@ -151,7 +152,7 @@ final class SettingsCest extends AbstractCest $this->closeModalAndHideFlashMessage($I); } - public function seeFeatureToggles(ApplicationTester $I, ModalDialog $modalDialog): void + public function seeFeatureToggles(ApplicationTester $I, ModalDialog $modalDialog, Scenario $scenario): void { $button = 'Configure Features…'; $modalButton = 'Save'; @@ -169,7 +170,12 @@ final class SettingsCest extends AbstractCest // Switch back hit count feature toggle $I->click($button); $modalDialog->canSeeDialog(); - $I->cantSeeCheckboxIsChecked($featureToggle); + if (str_contains($scenario->current('env'), 'classic')) { + // ['features']['redirects.hitCount'] is enabled by default in classic mode (set by TF BackendEnvironment setup) + $I->cantSeeCheckboxIsChecked($featureToggle); + } else { + $I->canSeeCheckboxIsChecked($featureToggle); + } $I->amGoingTo('reset hit count feature toggle and save it'); $I->click($featureToggle); $I->click($modalButton, ModalDialog::$openedModalButtonContainerSelector); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/UpgradeCest.php b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/UpgradeCest.php index 997c4107a82c0c9323332c5d9ddc231602028195..816a05b9a223672fb872fd854f83a67a1fab72ed 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/UpgradeCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/InstallTool/UpgradeCest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Acceptance\Application\InstallTool; +use Codeception\Attribute\Env; use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester; use TYPO3\CMS\Core\Tests\Acceptance\Support\Helper\ModalDialog; @@ -30,6 +31,7 @@ final class UpgradeCest extends AbstractCest $I->see('Upgrade', 'h1'); } + #[Env('classic')] public function seeUpgradeCore(ApplicationTester $I, ModalDialog $modalDialog): void { $I->click('Update Core…'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Redirect/RedirectModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Redirect/RedirectModuleCest.php index b219db8a3dc7c76b09a988058b60824773c01e8c..6f48e185c9d65348263a588e729eef69d1d6c732 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Redirect/RedirectModuleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Redirect/RedirectModuleCest.php @@ -71,7 +71,7 @@ final class RedirectModuleCest $I->canSee('Redirect Management', 'h1'); $I->amGoingTo('test edit on edit button'); - $I->click('table.table-striped > tbody > tr > td:nth-child(8) > div > a:nth-child(2)'); + $I->click('table.table-striped > tbody > tr > td.col-control > div > a:nth-child(2)'); $I->waitForElementNotVisible('#t3js-ui-block'); $I->canSee('Edit Redirect "' . $sourceHost . ', ' . $sourcePath . '" on root level'); $I->click('div.module-docheader .btn.t3js-editform-close'); diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Scheduler/TasksCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Scheduler/TasksCest.php index f6ac098e002a188e7b2685a56aace8cc66317b79..8be04e089a78def0e57c3926df207e288e230d8d 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Scheduler/TasksCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Scheduler/TasksCest.php @@ -126,6 +126,7 @@ final class TasksCest $I->waitForElementVisible('[data-module-name="scheduler_availabletasks"]'); $I->see('Available scheduler commands & tasks'); $I->canSeeNumberOfElements('[data-module-name="scheduler_availabletasks"] table tbody tr', [1, 10000]); + $I->selectOption('select[name=moduleMenu]', 'Scheduled tasks'); } public function canCreateNewTaskGroupFromEditForm(ApplicationTester $I, ModalDialog $modalDialog): void diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Template/TemplateCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Template/TemplateCest.php index bc37fa0239c5037ca590bf60820d6b28aca6c143..b33275258b89538eed92a06421ab99cc2d95ebb0 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Template/TemplateCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Template/TemplateCest.php @@ -166,6 +166,10 @@ final class TemplateCest $I->waitForText('Edit the whole TypoScript record'); $I->see('Edit the whole TypoScript record'); $I->click('Edit the whole TypoScript record'); + // Avoid race condition: + // SEVERE - http://web/typo3/sysext/backend/Resources/Public/JavaScript/code-editor/autocomplete/ts-ref.js?bust=[…] + // 12:613 Uncaught TypeError: Cannot convert undefined or null to object + $I->waitForElementNotVisible('#nprogress', 120); } public function createExtensionTemplate(ApplicationTester $I, PageTree $pageTree): void diff --git a/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationComposerEnvironment.php b/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationComposerEnvironment.php new file mode 100644 index 0000000000000000000000000000000000000000..2a19602a628ded9d47bf799f15f29fe421fc2bc2 --- /dev/null +++ b/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationComposerEnvironment.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Acceptance\Support\Extension; + +use Codeception\Events; +use Codeception\Extension; + +/** + * @internal Used by core, do not use in extensions, may vanish later. + */ +final class ApplicationComposerEnvironment extends Extension +{ + /** + * @var array Default configuration values + */ + protected array $config = [ + 'typo3InstancePath' => 'typo3temp/var/tests/acceptance-composer', + ]; + + public static $events = [ + Events::SUITE_BEFORE => 'bootstrapTypo3Environment', + ]; + + public function bootstrapTypo3Environment() + { + // @todo: ugly workaround for InstallTool/AbstractCest.php + $root = realpath(__DIR__ . '/../../../../../../../' . $this->config['typo3InstancePath']); + chdir($root); + putenv('TYPO3_ACCEPTANCE_PATH_WEB=' . $root . '/public'); + putenv('TYPO3_ACCEPTANCE_PATH_VAR=' . $root . '/var'); + putenv('TYPO3_ACCEPTANCE_PATH_CONFIG=' . $root . '/config'); + putenv('TYPO3_ACCEPTANCE_INSTALLTOOL_PW_PRESET=1'); + } +} diff --git a/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationEnvironment.php b/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationEnvironment.php index dfd3c333099841958573ed053e9e76d3c121683e..df19aa525cbeda5b1bd4b45d963d856f709362aa 100644 --- a/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationEnvironment.php +++ b/typo3/sysext/core/Tests/Acceptance/Support/Extension/ApplicationEnvironment.php @@ -152,6 +152,10 @@ final class ApplicationEnvironment extends BackendEnvironment ); } } + // @todo: ugly workaround for InstallTool/AbstractCest.php + putenv('TYPO3_ACCEPTANCE_PATH_WEB=' . $instancePath); + putenv('TYPO3_ACCEPTANCE_PATH_VAR=' . $instancePath . '/typo3temp/var'); + putenv('TYPO3_ACCEPTANCE_PATH_CONFIG=' . $instancePath . '/typo3conf'); } // @todo Eventually move this up to TF::BackendEnvironment, but then as protected.