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.