diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f79d2538..63afdbe8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -t ${{ matrix.TYPO3 }} -s functional if: matrix.TYPO3 == '14' - - name: Acceptance Tests + - name: Acceptance Tests Classic run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -t ${{ matrix.TYPO3 }} -s acceptance -- --fail-fast - name: Archive acceptance tests results uses: actions/upload-artifact@v6 @@ -54,6 +54,14 @@ jobs: name: acceptance-test-reports-${{ matrix.php }}-${{ matrix.TYPO3 }} path: .Build/Web/typo3temp/var/tests/_output + - name: Acceptance Tests Composer + run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -t ${{ matrix.TYPO3 }} -s acceptanceComposer -- --fail-fast + - name: Archive acceptance tests results + uses: actions/upload-artifact@v6 + if: always() + with: + name: acceptance-test-composer-reports-${{ matrix.php }}-${{ matrix.TYPO3 }} + path: .Build/Web/typo3temp/var/tests_composer/_output - name: Archive composer.lock uses: actions/upload-artifact@v6 if: always() diff --git a/.gitignore b/.gitignore index 2d37b767..119b2597 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ config .cache composer.json.orig composer.json.testing -bck-runTests.sh \ No newline at end of file +Build/acceptance_tests/composer.json +Build/acceptance_tests/composer.lock +Build/acceptance_tests/vendor +Build/acceptance_tests/b13-container +Build/acceptance_tests/public \ No newline at end of file diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index f73307a7..12426204 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -1,12 +1,41 @@ #!/usr/bin/env bash # -# TYPO3 core test runner based on docker. +# TYPO3 core test runner based on docker or podman. # if [ "${CI}" != "true" ]; then trap 'echo "runTests.sh SIGINT signal emitted";cleanUp;exit 2' SIGINT fi +printSummary() { + cleanUp + + echo "" >&2 + echo "###########################################################################" >&2 + echo "Result of ${TEST_SUITE}" >&2 + echo "Container runtime: ${CONTAINER_BIN}" >&2 + echo "Container suffix: ${SUFFIX}" + echo "PHP: ${PHP_VERSION}" >&2 + if [[ ${TEST_SUITE} =~ ^(functional|acceptance|acceptanceComposer)$ ]]; then + case "${DBMS}" in + mariadb|mysql|postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS}" >&2 + ;; + esac + fi + if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 + else + echo "FAILURE" >&2 + fi + echo "###########################################################################" >&2 + echo "" >&2 + exit ${SUITE_EXIT_CODE} +} + waitFor() { local HOST=${1} local PORT=${2} @@ -53,7 +82,7 @@ handleDbmsOptions() { exit 1 fi [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" - if ! [[ ${DBMS_VERSION} =~ ^(10.1|10.2|10.3|10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1|11.2|11.3|11.4)$ ]]; 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 @@ -69,7 +98,7 @@ handleDbmsOptions() { exit 1 fi [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" - if ! [[ ${DBMS_VERSION} =~ ^(5.5|5.6|5.7|8.0|8.1|8.2|8.3|8.4)$ ]]; then + if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3|8.4)$ ]]; 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 @@ -126,7 +155,8 @@ Usage: $0 [options] [file] Options: -s <...> Specifies the test suite to run - - acceptance: backend acceptance tests + - acceptance: backend acceptance tests (classic mode, fixture-based instance) + - acceptanceComposer: backend acceptance tests against a real composer-installed TYPO3 instance - cgl: test and fix all core php files - composer: "composer" command dispatcher, to execute various composer commands - composerInstall: "composer install", handy if host has no PHP, uses composer cache of users home @@ -165,7 +195,7 @@ Options: - pdo_mysql -d - 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 @@ -175,9 +205,6 @@ Options: -i version Specify a specific database version With "-d mariadb": - - 10.1 short-term, no longer maintained - - 10.2 short-term, no longer maintained - - 10.3 short-term, maintained until 2023-05-25 - 10.4 short-term, maintained until 2024-06-18 (default) - 10.5 short-term, maintained until 2025-06-24 - 10.6 long-term, maintained until 2026-06 @@ -187,19 +214,19 @@ Options: - 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 mariadb": - - 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) + - 11.1 short-term development series, maintained until 2024-08 + - 11.2 short-term development series, maintained until 2024-11 + - 11.3 short-term development series, rolling release + - 11.4 long-term, maintained until 2029-05 + With "-d mysql": + - 8.0 maintained until 2026-04 (default) LTS - 8.1 unmaintained since 2023-10 - 8.2 unmaintained since 2024-01 - 8.3 maintained until 2024-04 - 8.4 maintained until 2032-04 LTS With "-d postgres": - 10 unmaintained since 2022-11-10 (default) - - 11 maintained until 2023-11-09 + - 11 unmaintained since 2023-11-09 - 12 maintained until 2024-11-14 - 13 maintained until 2025-11-13 - 14 maintained until 2026-11-12 @@ -207,12 +234,12 @@ Options: - 16 maintained until 2028-11-09 -g - Only with -s acceptance + Only with -s acceptance|acceptanceComposer 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|unit|acceptance + Only with -s functional|unit|acceptance|acceptanceComposer 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 @@ -300,11 +327,13 @@ CONTAINER_HOST="host.docker.internal" HOST_UID=$(id -u) HOST_PID=$(id -g) USERSET="" +CI_JOB_ID=${CI_JOB_ID:-} SUFFIX=$(echo $RANDOM) +if [ ${CI_JOB_ID} ]; then + SUFFIX="${CI_JOB_ID}-${SUFFIX}" +fi NETWORK="b13-container-${SUFFIX}" PHPUNIT_EXCLUDE_GROUPS="${PHPUNIT_EXCLUDE_GROUPS:-}" -# @todo Remove USE_APACHE option when TF7 has been dropped (along with TYPO3 v11 support). -USE_APACHE=0 # Option parsing updates above default vars # Reset in case getopts has been used previously in the shell @@ -337,10 +366,6 @@ while getopts "a:b:s:d:i:t:p:xy:o:nhug" OPT; do if ! [[ ${TYPO3} =~ ^(13|14|14-dev)$ ]]; then INVALID_OPTIONS+=("${OPTARG}") fi - # @todo Remove USE_APACHE option when TF7 has been dropped (along with TYPO3 v11 support). - [[ "${TYPO3}" -eq 13 ]] && USE_APACHE=1 - [[ "${TYPO3}" -eq 14 ]] && USE_APACHE=1 - [[ "${TYPO3}" -eq "14-dev" ]] && USE_APACHE=1 ;; p) PHP_VERSION=${OPTARG} @@ -391,28 +416,24 @@ fi handleDbmsOptions -# ENV var "CI" is set by gitlab-ci. Use it to force some CI details. -IS_CORE_CI=0 if [ "${CI}" == "true" ]; then - IS_CORE_CI=1 + # ENV var "CI" is set by gitlab-ci. Use it to force some CI details. CONTAINER_INTERACTIVE="" +elif [ ! -t 0 ] || [ ! -t 1 ]; then + # If stdin or stdout is not a TTY (e.g. a script runner, pipe, or non-interactive shell), + # drop the interactive "-it" flags automatically to avoid podman warning "The input device + # is not a TTY." and docker failure, and to keep redirected output free of TTY control characters. + # Keep "--init" so the PID 1 init process still forwards signals (e.g. ctrl-c) to the test process. + CONTAINER_INTERACTIVE="--init" fi # determine default container binary to use: 1. podman 2. docker if [[ -z "${CONTAINER_BIN}" ]]; then - if [[ "${TYPO3}" == "11" ]]; then - if type "docker" >/dev/null 2>&1; then - CONTAINER_BIN="docker" - elif type "podman" >/dev/null 2>&1; then - CONTAINER_BIN="podman" - fi - else if type "podman" >/dev/null 2>&1; then CONTAINER_BIN="podman" elif type "docker" >/dev/null 2>&1; then CONTAINER_BIN="docker" fi - fi fi if [ $(uname) != "Darwin" ] && [ ${CONTAINER_BIN} = "docker" ]; then @@ -455,10 +476,16 @@ ${CONTAINER_BIN} network create ${NETWORK} >/dev/null if [ ${CONTAINER_BIN} = "docker" ]; then # docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}" + TMPFS_MOUNT_OPTIONS="rw,noexec,nosuid,uid=${HOST_UID},gid=${HOST_PID}" else # podman CONTAINER_HOST="host.containers.internal" CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}" + TMPFS_MOUNT_OPTIONS="rw,noexec,nosuid" +fi + +if [[ "${CI}" == "true" ]]; then + CONTAINER_COMMON_PARAMS="${CONTAINER_COMMON_PARAMS} ${CONTAINER_COMMON_PARAMS_CI:-}" fi if [ ${PHP_XDEBUG_ON} -eq 0 ]; then @@ -488,31 +515,17 @@ case ${TEST_SUITE} in 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}/.Build/Web/typo3temp/var/tests/acceptance -e PHPFPM_HOST=phpfpm -e PHPFPM_PORT=9000 -e TYPO3_PATH_ROOT=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -e TYPO3_PATH_APP=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance" ${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 - if [[ "${USE_APACHE}" -eq 1 ]]; 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} -e TYPO3_PATH_ROOT=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -e TYPO3_PATH_APP=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -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 - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${CORE_ROOT}:${CORE_ROOT} ${XDEBUG_MODE} -e typo3TestingAcceptanceBaseUrl=http://web:8000/ -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" -e TYPO3_PATH_ROOT=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -e TYPO3_PATH_APP=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance ${IMAGE_PHP} php -S web:8000 -t ${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance >/dev/null - fi else - if [[ "${USE_APACHE}" -eq 1 ]]; then ${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 -e TYPO3_PATH_ROOT=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -e TYPO3_PATH_APP=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -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 - else - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web -v ${CORE_ROOT}:${CORE_ROOT} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" -e typo3TestingAcceptanceBaseUrl=http://web:8000/ -e TYPO3_PATH_ROOT=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance -e TYPO3_PATH_APP=${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance ${IMAGE_PHP} php -S web:8000 -t ${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance >/dev/null - fi fi waitFor chrome 4444 if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then waitFor chrome 7900 fi - if [[ "${USE_APACHE}" -eq 1 ]]; then - waitFor web 80 - else - waitFor web 8000 - fi + 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 @@ -523,12 +536,7 @@ case ${TEST_SUITE} in ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null DATABASE_IP=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mariadb-ac-${SUFFIX}) waitFor mariadb-ac-${SUFFIX} 3306 - if [[ "${USE_APACHE}" -eq 1 ]]; then - CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" - else - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP} -e typo3TestingAcceptanceBaseUrl=http://web:8000" - fi + CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-mariadb ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; @@ -536,12 +544,7 @@ case ${TEST_SUITE} in ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null DATABASE_IP=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql-ac-${SUFFIX}) waitFor mysql-ac-${SUFFIX} 3306 2 - if [[ "${USE_APACHE}" -eq 1 ]]; then - CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" - else - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP} -e typo3TestingAcceptanceBaseUrl=http://web:8000" - fi + CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-mysql ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; @@ -549,29 +552,85 @@ case ${TEST_SUITE} in ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-ac-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null DATABASE_IP=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres-ac-${SUFFIX}) waitFor postgres-ac-${SUFFIX} 5432 - if [[ "${USE_APACHE}" -eq 1 ]]; then - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=func_test -e typo3DatabaseUsername=funcu -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" - else - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=func_test -e typo3DatabaseUsername=funcu -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP} -e typo3TestingAcceptanceBaseUrl=http://web:8000" - fi + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=func_test -e typo3DatabaseUsername=funcu -e typo3DatabasePassword=funcp -e typo3DatabaseHost=${DATABASE_IP}" ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-postgres ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; sqlite) rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" mkdir -p "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" - if [[ "${USE_APACHE}" -eq 1 ]]; then - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" - else - # @todo Remove fallback when TF7 has been dropped (along with TYPO3 v11 support). - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite -e typo3TestingAcceptanceBaseUrl=http://web:8000" - fi + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-sqlite ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; esac ;; + acceptanceComposer) + rm -rf "${CORE_ROOT}/.Build/Web/typo3temp/var/tests/acceptance-composer" "${CORE_ROOT}/.Build/Web/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=acp -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=acp -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-composer-prepare-${SUFFIX} ${XDEBUG_MODE} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" -e TYPO3=${TYPO3} ${PREPAREPARAMS} ${IMAGE_PHP} "${CORE_ROOT}/Build/Scripts/setupAcceptanceComposer.sh" ".Build/Web/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 + COMMAND=(.Build/vendor/codeception/codeception/codecept run Backend -d -c Tests/codeception.yml ${CODECEPION_ENV} "$@" --html reports.html) + 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}/.Build/Web/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 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + waitFor chrome 7900 + fi + 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 + ADDITIONLPARAMS="-e TESTS_OUTPUT_PATH=../.Build/Web/typo3temp/var/tests_composer/AcceptanceReports -e TESTS_OUTPUT_PATH=../.Build/Web/typo3temp/var/tests_composer/_output" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-${DBMS}-composer-${SUFFIX} ${XDEBUG_MODE} ${ADDITIONLPARAMS} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${TESTPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + fi + ;; cgl) # Active dry-run for cgl needs not "-n" but specific options if [ -n "${CGLCHECK_DRY_RUN}" ]; then @@ -653,7 +712,7 @@ case ${TEST_SUITE} in # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues rm -rf "${CORE_ROOT}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs" mkdir -p "${CORE_ROOT}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/" - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${CORE_ROOT}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${CORE_ROOT}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:${TMPFS_MOUNT_OPTIONS}" ${CONTAINER_BIN} run --rm ${CI_PARAMS} ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; @@ -710,37 +769,5 @@ case ${TEST_SUITE} in ;; 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 -echo "Container runtime: ${CONTAINER_BIN}" >&2 -echo "Container suffix: ${SUFFIX}" -echo "PHP: ${PHP_VERSION}" >&2 -if [[ ${TEST_SUITE} =~ ^(functional|functionalDeprecated|acceptance|acceptanceInstall)$ ]]; then - case "${DBMS}" in - mariadb|mysql|postgres) - echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 - ;; - sqlite) - echo "DBMS: ${DBMS}" >&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 +# Cleanup, print summary && exit with exitcode +printSummary diff --git a/Build/Scripts/setupAcceptanceComposer.sh b/Build/Scripts/setupAcceptanceComposer.sh new file mode 100755 index 00000000..e72a0a47 --- /dev/null +++ b/Build/Scripts/setupAcceptanceComposer.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +# +# Set up a composer-installed TYPO3 test instance for acceptance testing. +# Called by runTests.sh -s acceptanceComposer. +# +# Creates symlinks so that Build/composer/composer.dist.json can reference +# local paths with short names instead of deeply-nested relative URLs. +# Mirrors the approach used in the TYPO3 core's setupAcceptanceComposer.sh. +# + +set -e + +cd "$(dirname $(realpath $0))/../../" + +PROJECT_PATH=${1:-.Build/Web/typo3temp/var/tests/acceptance-composer} +export TYPO3_DB_DRIVER=${TYPO3_DB_DRIVER:-sqlite} +TYPO3_VERSION="${TYPO3:-14}" + +# Compute the relative path from PROJECT_PATH back to the project root. +# e.g. ".Build/Web/typo3temp/var/tests/acceptance-composer" -> "../../../../../../" +RELATIVE_ROOT=$(echo "${PROJECT_PATH}" | sed -e 's/[^\/][^\/]*/../g' -e 's/\/$//') + +mkdir -p "${PROJECT_PATH}" + +# b13-container -> project root (provides the b13/container path repository) +# packages -> Build/tests/packages (provides typo3tests/dataset-import) +ln -snf "${RELATIVE_ROOT}" "${PROJECT_PATH}/b13-container" +ln -snf "${RELATIVE_ROOT}/Build/acceptance_tests/packages" "${PROJECT_PATH}/packages" +rm -f "${PROJECT_PATH}/composer.json" +ln -snf "${RELATIVE_ROOT}/Build/acceptance_tests/composer.${TYPO3_VERSION}.json" "${PROJECT_PATH}/composer.json" +rm -rf "${PROJECT_PATH}/config" +mkdir -p "${PROJECT_PATH}/config/system/" +ln -snf "${RELATIVE_ROOT}/../Build/sites" "${PROJECT_PATH}/config/sites" + +cd "${PROJECT_PATH}" + +rm -rf composer.lock public/ var/ vendor/ + +cat > "config/system/additional.php" <<\EOF +addArgument('path', InputArgument::REQUIRED, 'Path to CSV dataset to import'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + DataSet::import($input->getArgument('path')); + return Command::SUCCESS; + } +} diff --git a/Build/acceptance_tests/packages/dataset_import/Classes/Command/EmptyDbCommand.php b/Build/acceptance_tests/packages/dataset_import/Classes/Command/EmptyDbCommand.php new file mode 100644 index 00000000..1430075c --- /dev/null +++ b/Build/acceptance_tests/packages/dataset_import/Classes/Command/EmptyDbCommand.php @@ -0,0 +1,27 @@ +connectionPool->getConnectionForTable('be_users'); + $connection->truncate('be_users'); + return Command::SUCCESS; + } +} diff --git a/Build/acceptance_tests/packages/dataset_import/Configuration/Services.yaml b/Build/acceptance_tests/packages/dataset_import/Configuration/Services.yaml new file mode 100644 index 00000000..adae8cd8 --- /dev/null +++ b/Build/acceptance_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/acceptance_tests/packages/dataset_import/composer.json b/Build/acceptance_tests/packages/dataset_import/composer.json new file mode 100644 index 00000000..f5c4bc1e --- /dev/null +++ b/Build/acceptance_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.4 || ^14.3", + "typo3/testing-framework": "^9.1" + }, + "extra": { + "typo3/cms": { + "extension-key": "dataset_import" + } + }, + "autoload": { + "psr-4": { + "TYPO3Tests\\DatasetImport\\": "Classes/" + } + } +} diff --git a/Build/acceptance_tests/restart.sh b/Build/acceptance_tests/restart.sh new file mode 100755 index 00000000..64382f33 --- /dev/null +++ b/Build/acceptance_tests/restart.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./stop_env.sh && ./setup.sh && ./stop_env.sh \ No newline at end of file diff --git a/Build/acceptance_tests/setup.sh b/Build/acceptance_tests/setup.sh new file mode 100755 index 00000000..2bac0543 --- /dev/null +++ b/Build/acceptance_tests/setup.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +set -e + +export TYPO3_DB_DBNAME="bar" +export TYPO3_DB_USERNAME="dev" +export TYPO3_DB_PASSWORD="dev" +export TYPO3_DB_HOST="127.0.0.1" +export TYPO3_DB_DRIVER=mysqli +export TYPO3_DB_PORT=3306 +export TYPO3_SERVER_TYPE=apache +export TYPO3_PROJECT_NAME="Container Test" +TYPO3_VERSION="${TYPO3:-14}" +RELATIVE_ROOT="../../" +PROJECT_PATH="." + +echo "drop database if exists ${TYPO3_DB_DBNAME};" |mysql +echo "create database ${TYPO3_DB_DBNAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" |mysql + + +ln -snf "${RELATIVE_ROOT}" "${PROJECT_PATH}/b13-container" + +rm -rf composer.lock config/ public/ var/ vendor/ + +mkdir -p "config/system/" +ln -snf "${RELATIVE_ROOT}sites" "${PROJECT_PATH}/config/sites" +cat > "config/system/additional.php" <<\EOF + var/php_pid +chromedriver --url-base=/wd/hub --port=9515 & echo $! > var/chromedriver_pid \ No newline at end of file diff --git a/Build/acceptance_tests/stop_env.sh b/Build/acceptance_tests/stop_env.sh new file mode 100755 index 00000000..df0eeb90 --- /dev/null +++ b/Build/acceptance_tests/stop_env.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kill `cat var/php_pid` +kill `cat var/chromedriver_pid` \ No newline at end of file diff --git a/Tests/Acceptance/Backend.suite.yml b/Tests/Acceptance/Backend.suite.yml index bf4306fb..55bc2e4c 100644 --- a/Tests/Acceptance/Backend.suite.yml +++ b/Tests/Acceptance/Backend.suite.yml @@ -9,6 +9,10 @@ env: extensions: enabled: - B13\Container\Tests\Acceptance\Support\Extension\BackendContainerEnvironment + composer: + extensions: + enabled: + - B13\Container\Tests\Acceptance\Support\Extension\BackendContainerEnvironmentComposer groups: AcceptanceTests-Job-*: AcceptanceTests-Job-* diff --git a/Tests/Acceptance/Backend/LayoutCest.php b/Tests/Acceptance/Backend/LayoutCest.php index 860c0053..6d64b64e 100644 --- a/Tests/Acceptance/Backend/LayoutCest.php +++ b/Tests/Acceptance/Backend/LayoutCest.php @@ -57,6 +57,9 @@ public function connectedModeShowCorrectContentElements(BackendTester $I, PageTr $I->see('header-header-1', $languageCol . ' td.t3-grid-cell'); $I->dontSee('2cols-header-0', $languageCol); $I->dontSee('header-header-0', $languageCol . ' td.t3-grid-cell'); + // reset + $I->selectLayoutMode(); + $I->selectEnglishInLanguageMenu(); } public function connectedModeShowNoAddContentButton(BackendTester $I, PageTree $pageTree) @@ -70,17 +73,13 @@ public function connectedModeShowNoAddContentButton(BackendTester $I, PageTree $ // we have a "Content" Button for new elements with Fluid based page module $newContentElementLabel = $I->getNewContentElementLabel(); $I->dontSee($newContentElementLabel, '#element-tt_content-102 .t3-page-ce-body'); - if ($I->getTypo3MajorVersion() < 14) { - $I->selectOption('select[name="actionMenu"]', 'Language Comparison'); - } else { - $I->waitForElementVisible('.module-docheader-buttons .btn-group button.dropdown-toggle'); - $I->click('.module-docheader-buttons .btn-group button.dropdown-toggle'); - $I->waitForElementVisible('.module-docheader-buttons .dropdown-menu'); - $I->click('Language Comparison', '.module-docheader-buttons .dropdown-menu'); - } + $I->selectLanguageComparisonMode(); $I->waitForElementNotVisible('#t3js-ui-block'); // but not in Language View $I->dontSee($newContentElementLabel, '#element-tt_content-102'); + // reset + $I->selectLayoutMode(); + $I->selectEnglishInLanguageMenu(); } public function canCreateContainerContentElement(BackendTester $I, PageTree $pageTree) @@ -214,7 +213,6 @@ public function canCreateContentElementInTranslatedContainerInFreeMode(BackendTe $uid = 104; $selector = '#element-tt_content-' . $uid . ' div:nth-child(1) div:nth-child(2)'; - $I->dontSee('german', $selector); $dataColPos = $I->getDataColPos($uid, 200); $colPosSelector = '#element-tt_content-' . $uid . ' [data-colpos="' . $dataColPos . '"]'; $I->clickNewContentElement($colPosSelector); @@ -231,6 +229,8 @@ public function canCreateContentElementInTranslatedContainerInFreeMode(BackendTe $I->click('Close'); $I->waitForElementNotVisible('#t3js-ui-block'); $I->canSeeElement($selector . ' .t3js-flag[title="german"]'); + // reset + $I->selectEnglishInLanguageMenu(); } public function canTranslateChildWithTranslationModule(BackendTester $I, PageTree $pageTree, Scenario $scenario): void @@ -241,8 +241,8 @@ public function canTranslateChildWithTranslationModule(BackendTester $I, PageTre $I->wait(0.2); $I->switchToContentFrame(); - $I->selectLanguageComparisonMode(); $I->selectGermanInLanguageMenu(); + $I->selectLanguageComparisonMode(); if ($I->getTypo3MajorVersion() < 14) { $I->waitForElementVisible('a.t3js-localize'); $I->click('a.t3js-localize'); @@ -254,9 +254,15 @@ public function canTranslateChildWithTranslationModule(BackendTester $I, PageTre $I->switchToIFrame(); if ($I->getTypo3MajorVersion() < 14) { $I->waitForText('(212) headerOfChild'); + $I->click('button.t3js-modal-close'); } else { $I->waitForText('headerOfChild'); + $I->click('button.modal-header-close'); } + $I->switchToContentFrame(); + // reset + $I->selectLayoutMode(); + $I->selectEnglishInLanguageMenu(); } public function canTranslateChild(BackendTester $I, PageTree $pageTree): void diff --git a/Tests/Acceptance/Backend/WorkspaceCest.php b/Tests/Acceptance/Backend/WorkspaceCest.php index bc4080d5..5f0ba1ab 100644 --- a/Tests/Acceptance/Backend/WorkspaceCest.php +++ b/Tests/Acceptance/Backend/WorkspaceCest.php @@ -69,6 +69,7 @@ public function liveWorkspaceShowsLiveElementsForTranslations(BackendTester $I, $I->waitForText('translation-live'); $I->see('translation-live'); $I->dontSee('translation-ws'); + $I->selectEnglishInLanguageMenu(); } #[Group('workspace')] @@ -84,6 +85,8 @@ public function testWorkspaceShowsLiveElementsForTranslations(BackendTester $I, $I->dontSee('translation-live'); $I->see('translation-ws'); $this->switchToLiveWs($I); + $I->switchToContentFrame(); + $I->selectEnglishInLanguageMenu(); } #[Group('workspace')] diff --git a/Tests/Acceptance/Support/BackendTester.php b/Tests/Acceptance/Support/BackendTester.php index 2b8cfc49..651d47a4 100644 --- a/Tests/Acceptance/Support/BackendTester.php +++ b/Tests/Acceptance/Support/BackendTester.php @@ -127,6 +127,22 @@ public function selectGermanInLanguageMenu(): void } } + public function selectEnglishInLanguageMenu(): void + { + if ($this->getTypo3MajorVersion() < 14) { + $this->waitForText('Language'); + $this->click('Language'); + $this->waitForText('english'); + $this->click('english'); + } else { + $this->waitForText('german'); + //$this->click('english'); + $this->click('.module-docheader-column button.dropdown-toggle'); + $this->waitForText('english'); + $this->click('english'); + } + } + public function selectLanguageComparisonMode(): void { if ($this->getTypo3MajorVersion() < 14) { @@ -139,4 +155,17 @@ public function selectLanguageComparisonMode(): void $this->click('Language Comparison', '.module-docheader-buttons .dropdown-menu'); } } + + public function selectLayoutMode(): void + { + if ($this->getTypo3MajorVersion() < 14) { + $this->waitForElement('select[name="actionMenu"]'); + $this->selectOption('select[name="actionMenu"]', 'Layout'); + } else { + $this->waitForElementVisible('.module-docheader-buttons .btn-group button.dropdown-toggle'); + $this->click('.module-docheader-buttons .btn-group button.dropdown-toggle'); + $this->waitForElementVisible('.module-docheader-buttons .dropdown-menu'); + $this->click('Layout', '.module-docheader-buttons .dropdown-menu'); + } + } } diff --git a/Tests/Acceptance/Support/Extension/BackendContainerEnvironmentComposer.php b/Tests/Acceptance/Support/Extension/BackendContainerEnvironmentComposer.php new file mode 100644 index 00000000..cd1d6792 --- /dev/null +++ b/Tests/Acceptance/Support/Extension/BackendContainerEnvironmentComposer.php @@ -0,0 +1,17 @@ +