From 10ea4d771d0e82a7602592630db5ad8409fca7c9 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 13:14:12 +0100 Subject: [PATCH 01/52] Align 3.1 --- .github/release-drafter.yml | 14 + .github/workflows/build-docs.yml | 15 + .github/workflows/build-release.yml | 56 ++ .github/workflows/test-unit.yml | 404 +++++++++++++ .github/workflows/unit.yml | 65 --- .gitignore | 2 + .php-cs-fixer.dist.php | 28 +- behat.yml.dist | 27 +- bower.json | 2 +- codecov.yml | 13 + composer.json | 29 +- demos/_demo-data/create-db.php | 26 + demos/_includes/Model/Post.php | 16 + demos/db.default.php | 20 + demos/index.php | 17 +- demos/init-app.php | 38 ++ demos/init-autoloader.php | 17 + demos/init-db.php | 15 + phpstan.neon.dist | 21 +- phpunit.xml.dist | 44 +- tests-behat/Bootstrap/Context.php | 781 -------------------------- tests-behat/Bootstrap/ContextDump.php | 38 -- tests/TextEditorTest.php | 4 +- tools/CoverageUtil.php | 38 ++ 24 files changed, 770 insertions(+), 960 deletions(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/build-docs.yml create mode 100644 .github/workflows/build-release.yml create mode 100644 .github/workflows/test-unit.yml delete mode 100644 .github/workflows/unit.yml create mode 100644 codecov.yml create mode 100644 demos/_demo-data/create-db.php create mode 100644 demos/_includes/Model/Post.php create mode 100644 demos/db.default.php create mode 100644 demos/init-app.php create mode 100644 demos/init-autoloader.php create mode 100644 demos/init-db.php delete mode 100644 tests-behat/Bootstrap/Context.php delete mode 100644 tests-behat/Bootstrap/ContextDump.php create mode 100644 tools/CoverageUtil.php diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..e37c078 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,14 @@ +categories: + - title: "Breaking Changes" + labels: + - "BC-break" + - title: "Major Features" + labels: + - "MAJOR" + - title: "Documentation enhancements" + labels: + - "Documentation :books:" +template: | + ## What’s Changed + + $CHANGES diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 0000000..ab60038 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,15 @@ +name: Build Docs + +on: + push: + branches: + - develop + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - name: Run Release Drafter + uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..5e6812c --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,56 @@ +name: Build Release + +on: + push: + branches: + - '**\.build' + - 'release/*' + - '!**\.gen' + +jobs: + autocommit: + name: Build Release + runs-on: ubuntu-latest + container: + image: ghcr.io/mvorisek/image-php:latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} + + - name: Install PHP dependencies + run: composer update --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader + + - name: Composer unset version + run: composer config version --unset + + - name: Update composer.json + run: >- + php -r ' + $f = __DIR__ . "/composer.json"; + $data = json_decode(file_get_contents($f), true); + foreach ($data as $k => $v) { + if (preg_match("~^(.+)-release$~", $k, $matches)) { + $data[$matches[1]] = $data[$k]; unset($data[$k]); + } + } + $str = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"; + echo $str; + file_put_contents($f, $str); + ' + + - name: Composer validate config + run: composer validate --strict --no-check-lock && composer normalize --dry-run --no-check-lock + + - name: Commit + run: | + git config --global user.name "$(git show -s --format='%an')" + git config --global user.email "$(git show -s --format='%ae')" + git add -A && git diff --staged && git commit -m "Build Release" + + - name: Push + uses: ad-m/github-push-action@master + with: + branch: ${{ github.ref }}.gen + force: true + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml new file mode 100644 index 0000000..96c7997 --- /dev/null +++ b/.github/workflows/test-unit.yml @@ -0,0 +1,404 @@ +name: Unit + +on: + pull_request: + push: + schedule: + - cron: '0 0/2 * * *' + +jobs: + smoke-test: + name: Smoke + runs-on: ubuntu-latest + container: + image: ghcr.io/mvorisek/image-php:${{ matrix.php }} + strategy: + fail-fast: false + matrix: + php: ['latest'] + type: ['Phpunit'] + include: + - php: 'latest' + type: 'CodingStyle' + - php: 'latest' + type: 'StaticAnalysis' + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Configure PHP + run: | + rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + php --version + + - name: Setup cache 1/2 + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Setup cache 2/2 + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-smoke-${{ matrix.php }}-${{ matrix.type }}-${{ hashFiles('composer.json') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install PHP dependencies + run: | + if [ "${{ matrix.type }}" != "Phpunit" ] && [ "${{ matrix.type }}" != "StaticAnalysis" ]; then composer remove --no-interaction --no-update phpunit/phpunit johnkary/phpunit-speedtrap --dev; fi + if [ "${{ matrix.type }}" != "CodingStyle" ]; then composer remove --no-interaction --no-update friendsofphp/php-cs-fixer --dev; fi + if [ "${{ matrix.type }}" != "StaticAnalysis" ]; then composer remove --no-interaction --no-update phpstan/\* behat/\* --dev; fi + composer update --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader + + - name: "Run tests: SQLite (only for Phpunit)" + if: startsWith(matrix.type, 'Phpunit') + run: | + php demos/_demo-data/create-db.php + vendor/bin/phpunit --exclude-group none --no-coverage -v + + - name: Check Coding Style (only for CodingStyle) + if: matrix.type == 'CodingStyle' + run: | + if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Ui\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi + vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --verbose + + - name: Run Static Analysis (only for StaticAnalysis) + if: matrix.type == 'StaticAnalysis' + run: | + echo "memory_limit = 2G" > /usr/local/etc/php/conf.d/custom-memory-limit.ini + vendor/bin/phpstan analyse + + unit-test: + name: Unit + runs-on: ubuntu-latest + container: + image: ghcr.io/mvorisek/image-php:${{ matrix.php }} + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0', '8.1'] + type: ['Phpunit', 'Phpunit Lowest'] + include: + - php: '8.1' # TODO replace with 'latest' once it represents at least PHP 8.1 + type: 'Phpunit Burn' + env: + LOG_COVERAGE: "${{ fromJSON('{true: \"1\", false: \"\"}')[matrix.php == '8.0' && matrix.type == 'Phpunit' && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master')))] }}" + services: + mysql: + image: mysql:8 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test --entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password" + mariadb: + image: mariadb + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test + postgres: + image: postgres:12-alpine + env: + POSTGRES_USER: atk4_test_user + POSTGRES_PASSWORD: atk4_pass + POSTGRES_DB: atk4_test + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + mssql: + image: mcr.microsoft.com/mssql/server + env: + ACCEPT_EULA: Y + SA_PASSWORD: atk4_pass + oracle: + image: gvenzl/oracle-xe:18 + env: + ORACLE_PASSWORD: atk4_pass + options: --health-cmd healthcheck.sh --health-interval=10s --health-timeout=5s --health-retries=10 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Configure PHP + run: | + if [ -n "$LOG_COVERAGE" ]; then echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; else rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; fi + php --version + + - name: Setup cache 1/2 + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Setup cache 2/2 + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.type }}-${{ hashFiles('composer.json') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install PHP dependencies + run: | + if [ "${{ matrix.type }}" != "Phpunit" ] && [ "${{ matrix.type }}" != "Phpunit Lowest" ] && [ "${{ matrix.type }}" != "Phpunit Burn" ]; then composer remove --no-interaction --no-update phpunit/phpunit johnkary/phpunit-speedtrap --dev; fi + if [ "${{ matrix.type }}" != "CodingStyle" ]; then composer remove --no-interaction --no-update friendsofphp/php-cs-fixer --dev; fi + if [ "${{ matrix.type }}" != "StaticAnalysis" ]; then composer remove --no-interaction --no-update phpstan/\* behat/\* --dev; fi + if [ -n "$LOG_COVERAGE" ]; then composer require --no-interaction --no-update phpunit/phpcov; fi + composer update --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader + if [ "${{ matrix.type }}" = "Phpunit Lowest" ]; then composer update --ansi --prefer-dist --prefer-lowest --prefer-stable --no-interaction --no-progress --optimize-autoloader; fi + if [ "${{ matrix.type }}" = "Phpunit Burn" ]; then sed -i 's~ *public function runBare(): void~public function runBare(): void { gc_collect_cycles(); gc_collect_cycles(); $memDiffs = array_fill(0, '"$(if [ \"$GITHUB_EVENT_NAME\" == \"schedule\" ]; then echo 64; else echo 16; fi)"', 0); for ($i = -1; $i < count($memDiffs); ++$i) { $this->_runBare(); gc_collect_cycles(); gc_collect_cycles(); $mem = memory_get_usage(); if ($i !== -1) { $memDiffs[$i] = $mem - $memPrev; } $memPrev = $mem; rsort($memDiffs); if (array_sum($memDiffs) >= 4096 * 1024 || $memDiffs[2] > 0) { $this->onNotSuccessfulTest(new AssertionFailedError( "Memory leak detected! (" . implode(" + ", array_map(fn ($v) => number_format($v / 1024, 3, ".", " "), array_filter($memDiffs))) . " KB, " . ($i + 2) . " iterations)" )); } } } private function _runBare(): void~' vendor/phpunit/phpunit/src/Framework/TestCase.php && cat vendor/phpunit/phpunit/src/Framework/TestCase.php | grep '_runBare('; fi + + - name: Init + run: | + php -r '(new PDO("mysql:host=mysql", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");' + php -r '(new PDO("mysql:host=mariadb", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");' + php -r '(new PDO("pgsql:host=postgres;dbname=atk4_test", "atk4_test_user", "atk4_pass"))->exec("ALTER ROLE atk4_test_user CONNECTION LIMIT 1");' + if [ -n "$LOG_COVERAGE" ]; then mkdir coverage && cp tools/CoverageUtil.php demos; fi + + - name: "Run tests: SQLite" + run: | + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-sqlite.cov; fi + + - name: "Run tests: MySQL" + env: + DB_DSN: "mysql:host=mysql;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mysql.cov; fi + + - name: "Run tests: MariaDB" + env: + DB_DSN: "mysql:host=mariadb;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mariadb.cov; fi + + - name: "Run tests: PostgreSQL" + env: + DB_DSN: "pgsql:host=postgres;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-postgres.cov; fi + + - name: "Run tests: MSSQL" + env: + DB_DSN: "sqlsrv:host=mssql;dbname=master" + DB_USER: sa + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mssql.cov; fi + + - name: "Run tests: Oracle" + env: + DB_DSN: "oci:dbname=oracle/xe" + DB_USER: system + DB_PASSWORD: atk4_pass + NLS_LANG: AMERICAN_AMERICA.AL32UTF8 + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v + if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-oracle.cov; fi + + - name: Upload coverage logs 1/2 (only for latest Phpunit) + if: env.LOG_COVERAGE + run: | + ls -l coverage | wc -l + php -d memory_limit=2G vendor/bin/phpcov merge coverage/ --clover coverage/merged.xml + + - name: Upload coverage logs 2/2 (only for latest Phpunit) + if: env.LOG_COVERAGE + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage/merged.xml + + behat-test: + name: Behat + runs-on: ubuntu-latest + container: + image: ghcr.io/mvorisek/image-php:${{ matrix.php }}-selenium + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0', '8.1'] + type: ['Chrome', 'Chrome Lowest'] + include: + - php: 'latest' + type: 'Firefox' + - php: 'latest' + type: 'Chrome Slow' + env: + LOG_COVERAGE: "${{ fromJSON('{true: \"1\", false: \"\"}')[matrix.php == '8.0' && matrix.type == 'Chrome' && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master')))] }}" + services: + mysql: + image: mysql:8 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test --entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password" + mariadb: + image: mariadb + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test + postgres: + image: postgres:12-alpine + env: + POSTGRES_USER: atk4_test_user + POSTGRES_PASSWORD: atk4_pass + POSTGRES_DB: atk4_test + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + mssql: + image: mcr.microsoft.com/mssql/server + env: + ACCEPT_EULA: Y + SA_PASSWORD: atk4_pass + oracle: + image: gvenzl/oracle-xe:18 + env: + ORACLE_PASSWORD: atk4_pass + options: --health-cmd healthcheck.sh --health-interval=10s --health-timeout=5s --health-retries=10 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Configure PHP + run: | + if [ -n "$LOG_COVERAGE" ]; then echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; else rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; fi + php --version + + - name: Setup cache 1/2 + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Setup cache 2/2 + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-behat-${{ matrix.php }}-${{ matrix.type }}-${{ hashFiles('composer.json') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install JS dependencies (only for Slow) + if: matrix.type == 'Chrome Slow' + run: | + npm install --loglevel=error -g pug-cli + + - name: Build/diff HTML files (only for Slow) + if: matrix.type == 'Chrome Slow' + run: | + for f in $(find template demos -name '*.pug' -o -name '*.html'); do + fpug=${f/.[a-z]*/.pug} + fhtml=${fpug/.pug/.html} + mv "$fhtml" "$fhtml.orig" + pug --silent --pretty "$fpug" + diff "$fhtml.orig" "$fhtml" + rm "$fhtml.orig" + done + + - name: Install PHP dependencies + run: | + composer remove --no-interaction --no-update phpunit/phpunit johnkary/phpunit-speedtrap --dev + composer remove --no-interaction --no-update friendsofphp/php-cs-fixer --dev + composer remove --no-interaction --no-update phpstan/\* --dev + if [ -n "$LOG_COVERAGE" ]; then composer require --no-interaction --no-update phpunit/phpcov; fi + composer update --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader + if [ "${{ matrix.type }}" = "Chrome Lowest" ]; then composer update --ansi --prefer-dist --prefer-lowest --prefer-stable --no-interaction --no-progress --optimize-autoloader; fi + + - name: Init + run: | + php -r '(new PDO("mysql:host=mysql", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");' + php -r '(new PDO("mysql:host=mariadb", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");' + php -r '(new PDO("pgsql:host=postgres;dbname=atk4_test", "atk4_test_user", "atk4_pass"))->exec("ALTER ROLE atk4_test_user CONNECTION LIMIT 1");' + if [ -n "$LOG_COVERAGE" ]; then mkdir coverage && cp tools/CoverageUtil.php demos; fi + sed -i "s~'https://raw.githack.com/atk4/ui/develop/public.*~'/vendor/atk4/ui/public',~" vendor/atk4/ui/src/App.php + ci_wait_until () { timeout 30 sh -c "until { $1 2> /dev/null; }; do sleep 0.02; done" || timeout 15 sh -c "$1" || { echo "health timeout: $1"; exit 1; }; } + php -S 127.0.0.1:8888 > /dev/null 2>&1 & + ci_wait_until 'nc -w 1 127.0.0.1 8888' + if [ -f /etc/alpine-release ]; then addgroup browser && adduser browser -G browser -D -s /bin/sh; else adduser browser --gecos "" --disabled-login -shell /bin/sh > /dev/null; fi + { Xvfb -ac :99 -screen 0 1920x1200x24 2> /dev/null & } && export DISPLAY=:99 + ci_wait_until '[ -e /tmp/.X11-unix/X99 ]' + su browser -c 'java -Dwebdriver.chrome.whitelistedIps=127.0.0.1 -jar /opt/selenium-server-standalone.jar -role standalone -host 127.0.0.1 -port 4444 -sessionTimeout 15 -browserTimeout 12 > /dev/null 2>&1 &' + ci_wait_until 'nc -w 1 127.0.0.1 4444' + if [ "${{ matrix.type }}" = "Firefox" ]; then sed -i "s~chrome~firefox~" behat.yml.dist; fi + if [ "${{ matrix.type }}" = "Chrome Slow" ]; then echo 'sleep(1);' >> demos/init-app.php; fi + + - name: "Run tests: SQLite" + run: | + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: "Run tests: MySQL (only for coverage or cron)" + if: env.LOG_COVERAGE || github.event_name == 'schedule' + env: + DB_DSN: "mysql:host=mysql;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: "Run tests: MariaDB (only for coverage or cron)" + if: env.LOG_COVERAGE || github.event_name == 'schedule' + env: + DB_DSN: "mysql:host=mariadb;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: "Run tests: PostgreSQL (only for coverage or cron)" + if: env.LOG_COVERAGE || github.event_name == 'schedule' + env: + DB_DSN: "pgsql:host=postgres;dbname=atk4_test" + DB_USER: atk4_test_user + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: "Run tests: MSSQL (only for coverage or cron)" + if: env.LOG_COVERAGE || github.event_name == 'schedule' + env: + DB_DSN: "sqlsrv:host=mssql;dbname=master" + DB_USER: sa + DB_PASSWORD: atk4_pass + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: "Run tests: Oracle (only for coverage or cron)" + if: env.LOG_COVERAGE || github.event_name == 'schedule' + env: + DB_DSN: "oci:dbname=oracle/xe" + DB_USER: system + DB_PASSWORD: atk4_pass + NLS_LANG: AMERICAN_AMERICA.AL32UTF8 + run: | + sed -E "s~(\\\$db = new.+Persistence\\\\Sql)\(.+\);~\\1('$DB_DSN', '$DB_USER', '$DB_PASSWORD');~g" -i demos/db.default.php + php demos/_demo-data/create-db.php + vendor/bin/behat -vv --config behat.yml.dist + + - name: Upload coverage logs 1/2 (only for latest Chrome) + if: env.LOG_COVERAGE + run: | + ls -l coverage | wc -l + php -d memory_limit=2G vendor/bin/phpcov merge coverage/ --clover coverage/merged.xml + + - name: Upload coverage logs 2/2 (only for latest Chrome) + if: env.LOG_COVERAGE + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage/merged.xml diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml deleted file mode 100644 index b36390f..0000000 --- a/.github/workflows/unit.yml +++ /dev/null @@ -1,65 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: UnitTest - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - pull_request: - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: ['7.3', '7.4'] - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - name: Workaround composer failed opening composer.lock - run: | - sudo chown -R runner . - - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: intl - tools: composer:v2 behat - coverage: xdebug - - - name: Install dependencies - run: composer install --prefer-dist - - - name: PhpStan - run: vendor/bin/phpstan - - - name: PHP-CS-Fixer - run: vendor/bin/php-cs-fixer fix - - - name: PHPUnit (php-actions) - run: vendor/bin/phpunit - - - uses: nanasess/setup-chromedriver@master - with: - chromedriver-version: '2.44' - - run: export DISPLAY=:99 - - - name: Behat - run: | - wget https://selenium-release.storage.googleapis.com/2.43/selenium-server-standalone-2.43.1.jar - java -jar selenium-server-standalone-2.43.1.jar > /dev/null 2>/dev/null & - php -S 0.0.0.0:8888 & - sleep 10 - vendor/bin/behat \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1c2faa9..5ab35f6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ docs/build yarn.lock run_local.sh /coverage +/bower_components/ +/assets/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index aad5eed..f0a6b51 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,21 +1,20 @@ in([__DIR__]) ->exclude([ 'cache', 'build', 'vendor', - ]) - ->in(__DIR__) -; + ]); -$config = new PhpCsFixer\Config(); -$config->setRiskyAllowed(true) +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) ->setRules([ '@PhpCsFixer' => true, - '@PhpCsFixer:risky' =>true, - '@PHP71Migration:risky' => true, - '@PHP73Migration' => true, + '@PhpCsFixer:risky' => true, + '@PHP74Migration:risky' => true, + '@PHP74Migration' => true, // required by PSR-12 'concat_space' => [ @@ -37,10 +36,8 @@ 'equal' => false, 'identical' => false, ], + 'native_constant_invocation' => true, 'native_function_invocation' => false, - 'non_printable_character' => [ - 'use_escape_sequences_in_strings' => true, - ], 'void_return' => false, 'blank_line_before_statement' => [ 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'exit'], @@ -58,15 +55,16 @@ 'phpdoc_add_missing_param_annotation' => false, 'return_assignment' => false, 'comment_to_phpdoc' => false, - 'list_syntax' => ['syntax' => 'short'], 'general_phpdoc_annotation_remove' => [ 'annotations' => ['author', 'copyright', 'throws'], ], 'nullable_type_declaration_for_default_null_value' => [ 'use_nullable_type_declaration' => false, ], + + // fn => without curly brackets is less readable, + // also prevent bounding of unwanted variables for GC + 'use_arrow_functions' => false, ]) ->setFinder($finder) - ->setCacheFile(__DIR__ . '/.php_cs.cache'); - -return $config; \ No newline at end of file + ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer.' . md5(__DIR__) . '.cache'); diff --git a/behat.yml.dist b/behat.yml.dist index 368d565..10c40d4 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -1,24 +1,25 @@ default: suites: - atk4_login: + main: paths: features: '%paths.base%/tests-behat' contexts: - - TextEditor\Behat\Context - Behat\MinkExtension\Context\MinkContext + - Atk4\Ui\Behat\Context extensions: Behat\MinkExtension: - show_cmd: 'open %s' - base_url: 'http://127.0.0.1:8888/demos/' + base_url: 'http://127.0.0.1:8888/demos' sessions: default: selenium2: - browser: chrome - wd_host: http://127.0.0.1:4444/wd/hub - capabilities: - chrome: - switches: - - "--headless" - - "--disable-gpu" - - "--window-size=1920,1080" - - "--no-sandbox" \ No newline at end of file + browser: chrome + wd_host: 'http://127.0.0.1:4444/wd/hub' + capabilities: + extra_capabilities: + chrome: + args: + - '--no-sandbox' + - '--headless' + - '--disable-dev-shm-usage' + - '--disable-gpu' + - '--window-size=1920,1200' diff --git a/bower.json b/bower.json index 234414e..1c1ba86 100644 --- a/bower.json +++ b/bower.json @@ -2,6 +2,6 @@ "name": "atk4-trumbowyg", "version": "1.0.0", "dependencies": { - "trumbowyg": "^2.19.0" + "trumbowyg": "^2.25.1" } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..f12b2f0 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +ignore: + - demos + - docs + - tools +comment: false +coverage: + status: + project: + default: + target: auto + threshold: 0.025 + patch: false + changes: false diff --git a/composer.json b/composer.json index b212eba..d8387ad 100644 --- a/composer.json +++ b/composer.json @@ -9,17 +9,28 @@ } ], "require": { - "php": ">=7.3", - "atk4/ui": "2.4" + "php": ">=7.4 <8.2", + "atk4/ui": "dev-develop" + }, + "require-release": { + "php": ">=7.4 <8.2", + "atk4/ui": "3.1" }, "require-dev": { - "behat/behat": "3.8.1", - "behat/mink": "1.8.1", - "behat/mink-extension": "2.3.1", - "behat/mink-selenium2-driver": "1.4.0", - "friendsofphp/php-cs-fixer": "3.0.2", - "phpstan/phpstan": "0.12.94", - "phpunit/phpunit": "9.5.8" + "behat/behat": "^3.9", + "behat/mink": "^1.9", + "behat/mink-extension": "^2.3.1", + "behat/mink-selenium2-driver": "^1.5", + "ergebnis/composer-normalize": "^2.13", + "friendsofphp/php-cs-fixer": "^3.0", + "instaclick/php-webdriver": "^1.4.7", + "johnkary/phpunit-speedtrap": "^3.3", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpunit/phpunit": "^9.5.5", + "symfony/console": "^4.4.30 || ^5.3.7", + "symfony/css-selector": "^4.4.24 || ^5.2.9" }, "autoload": { "psr-4": { diff --git a/demos/_demo-data/create-db.php b/demos/_demo-data/create-db.php new file mode 100644 index 0000000..6e496a4 --- /dev/null +++ b/demos/_demo-data/create-db.php @@ -0,0 +1,26 @@ +create(); + +echo 'import complete!' . "\n\n"; diff --git a/demos/_includes/Model/Post.php b/demos/_includes/Model/Post.php new file mode 100644 index 0000000..a67b423 --- /dev/null +++ b/demos/_includes/Model/Post.php @@ -0,0 +1,16 @@ +addField('subject'); + $this->addField('body'); + } +} \ No newline at end of file diff --git a/demos/db.default.php b/demos/db.default.php new file mode 100644 index 0000000..fac51bf --- /dev/null +++ b/demos/db.default.php @@ -0,0 +1,20 @@ + 'Agile toolkit WYSIWYG', - 'call_exit' => false, -]); $app->initLayout([Centered::class]); $form = \Atk4\Ui\Form::addTo($app); +$form->setModel((new Post($app->db))->createEntity(), []); + $form->addControl('subject'); -$form->addControl('editor', [ +$form->addControl('body', [ \Atk4\Ui\Form\Control\TextEditor::class, 'placeholder' => 'test placeholder', ]); @@ -28,7 +29,7 @@ $view = new \Atk4\Ui\Message(); $view->invokeInit(); $view->text->addParagraph('subject : ' . $f->model->get('subject')); - $view->text->addParagraph('editor : ' . $f->model->get('editor')); + $view->text->addParagraph('body : ' . $f->model->get('body')); return $view; }); diff --git a/demos/init-app.php b/demos/init-app.php new file mode 100644 index 0000000..d4d5bbd --- /dev/null +++ b/demos/init-app.php @@ -0,0 +1,38 @@ +onHook(\Atk4\Ui\App::HOOK_BEFORE_EXIT, function () { + \CoverageUtil::saveData(); + }); +} + +try { + /** @var Persistence\Sql $db */ + require_once __DIR__ . '/init-db.php'; + $app->db = $db; + unset($db); +} catch (\Throwable $e) { + throw new \Atk4\Ui\Exception('Database error: ' . $e->getMessage()); +} + +$app->invokeInit(); diff --git a/demos/init-autoloader.php b/demos/init-autoloader.php new file mode 100644 index 0000000..3e259aa --- /dev/null +++ b/demos/init-autoloader.php @@ -0,0 +1,17 @@ +setClassMapAuthoritative(false); +$loader->setPsr4('Atk4\Ui\Demos\\', __DIR__ . '/_includes'); +unset($isRootProject, $loader); diff --git a/demos/init-db.php b/demos/init-db.php new file mode 100644 index 0000000..f4fde13 --- /dev/null +++ b/demos/init-db.php @@ -0,0 +1,15 @@ +addMoreInfo('PDO error', $e->getMessage()); +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f1d4217..d0d9cc9 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,14 +1,23 @@ -includes: - - vendor/mahalux/atk4-hintable/phpstan-ext.neon - parameters: - level: 4 + level: 6 paths: - ./ - excludes_analyse: + excludePaths: - cache/ - build/ - vendor/ # TODO review once we drop PHP 7.x support - treatPhpDocTypesAsCertain: false \ No newline at end of file + treatPhpDocTypesAsCertain: false + + # some extra rules + checkAlwaysTrueCheckTypeFunctionCall: true + checkAlwaysTrueInstanceof: true + checkAlwaysTrueStrictComparison: true + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + # TODO checkMissingClosureNativeReturnTypehintRule: true + reportMaybesInMethodSignatures: true + reportStaticMethodSignatures: true + checkTooWideReturnTypesInProtectedAndPublicMethods: true + checkMissingIterableValueType: false # TODO diff --git a/phpunit.xml.dist b/phpunit.xml.dist index bbfa462..3952d4c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,23 @@ - - - - - src - - - - - - - - - - - - - - tests - - - + + + + + + + + + tests + + + + + + + + src + + + + + diff --git a/tests-behat/Bootstrap/Context.php b/tests-behat/Bootstrap/Context.php deleted file mode 100644 index 759561d..0000000 --- a/tests-behat/Bootstrap/Context.php +++ /dev/null @@ -1,781 +0,0 @@ -getMink()->getSession($name); - } - - /** - * @BeforeStep - */ - public function closeAllToasts(BeforeStepScope $event): void - { - if (!$this->getSession()->getDriver()->isStarted()) { - return; - } - - if (strpos($event->getStep()->getText(), 'Toast display should contains text ') !== 0) { - $this->getSession()->executeScript('$(\'.toast-box > .ui.toast\').toast(\'close\');'); - } - } - - /** - * @AfterStep - */ - public function waitUntilLoadingAndAnimationFinished(AfterStepScope $event): void - { - $this->jqueryWait(); - $this->disableAnimations(); - $this->assertNoException(); - $this->disableDebounce(); - } - - protected function disableAnimations(): void - { - // disable all CSS/jQuery animations/transitions - $toCssFx = function ($selector, $cssPairs) { - $css = []; - foreach ($cssPairs as $k => $v) { - foreach ([$k, '-moz-' . $k, '-webkit-' . $k] as $k2) { - $css[] = $k2 . ': ' . $v . ' !important;'; - } - } - - return $selector . ' { ' . implode(' ', $css) . ' }'; - }; - - $durationAnimation = 0.005; - $durationToast = 5; - $css = $toCssFx('*', [ - 'animation-delay' => $durationAnimation . 's', - 'animation-duration' => $durationAnimation . 's', - 'transition-delay' => $durationAnimation . 's', - 'transition-duration' => $durationAnimation . 's', - ]) . $toCssFx('.ui.toast-container .toast-box .progressing.wait', [ - 'animation-duration' => $durationToast . 's', - 'transition-duration' => $durationToast . 's', - ]); - - $this->getSession()->executeScript( - 'if (Array.prototype.filter.call(document.getElementsByTagName("style"), e => e.getAttribute("about") === "atk-test-behat").length === 0) {' - . ' $(\'\').appendTo(\'head\');' - . ' }' - . 'jQuery.fx.off = true;' - ); - } - - protected function assertNoException(): void - { - foreach ($this->getSession()->getPage()->findAll('css', 'div.ui.negative.icon.message > div.content > div.header') as $elem) { - if ($elem->getText() === 'Critical Error') { - throw new Exception('Page contains uncaught exception'); - } - } - } - - protected function disableDebounce(): void - { - $this->getSession()->executeScript('atk.options.set("debounceTimeout", 20)'); - } - - /** - * Sleep for a certain time in ms. - * - * @Then I wait :arg1 ms - */ - public function iWait($arg1) - { - $this->getSession()->wait($arg1); - } - - /** - * @When I press button :arg1 - */ - public function iPressButton($arg1) - { - $button = $this->getSession()->getPage()->find('xpath', '//div[text()="' . $arg1 . '"]'); - // store button id. - $this->buttonId = $button->getAttribute('id'); - // fix "is out of bounds of viewport width and height" for Firefox - $button->focus(); - $button->click(); - } - - /** - * @Then I press menu button :arg1 using class :arg2 - */ - public function iPressMenuButtonUsingClass($arg1, $arg2) - { - $menu = $this->getSession()->getPage()->find('css', '.ui.menu.' . $arg2); - if (!$menu) { - throw new Exception('Unable to find a menu with class ' . $arg2); - } - - $link = $menu->find('xpath', '//a[text()="' . $arg1 . '"]'); - if (!$link) { - throw new Exception('Unable to find menu with title ' . $arg1); - } - - $this->getSession()->executeScript('$("#' . $link->getAttribute('id') . '").click()'); - } - - /** - * @Then I set calendar input name :arg1 with value :arg2 - */ - public function iSetCalendarInputNameWithValue($arg1, $arg2) - { - $script = '$(\'input[name="' . $arg1 . '"]\').get(0)._flatpickr.setDate("' . $arg2 . '")'; - $this->getSession()->executeScript($script); - } - - /** - * @Given I click link :arg1 - */ - public function iClickLink($arg1) - { - $link = $this->getSession()->getPage()->find('xpath', '//a[text()="' . $arg1 . '"]'); - $link->click(); - } - - /** - * @Then I click filter column name :arg1 - */ - public function iClickFilterColumnName($arg1) - { - $column = $this->getSession()->getPage()->find('css', "th[data-column='" . $arg1 . "']"); - if (!$column) { - throw new Exception('Unable to find a column ' . $arg1); - } - - $icon = $column->find('css', 'i'); - if (!$icon) { - throw new Exception('Column does not contain clickable icon.'); - } - - $this->getSession()->executeScript('$("#' . $icon->getAttribute('id') . '").click()'); - } - - /** - * @Given I click tab with title :arg1 - */ - public function iClickTabWithTitle($arg1) - { - $tabMenu = $this->getSession()->getPage()->find('css', '.ui.tabular.menu'); - if (!$tabMenu) { - throw new Exception('Unable to find a tab menu.'); - } - - $link = $tabMenu->find('xpath', '//a[text()="' . $arg1 . '"]'); - if (!$link) { - throw new Exception('Unable to find tab with title ' . $arg1); - } - - $this->getSession()->executeScript('$("#' . $link->getAttribute('id') . '").click()'); - } - - /** - * @Then I click first card on page - */ - public function iClickFirstCardOnPage() - { - $this->getSession()->executeScript('$(".atk-card")[0].click()'); - } - - /** - * @Then I click first element using class :arg1 - */ - public function iClickFirstElementUsingClass($arg1) - { - $this->getSession()->executeScript('$("' . $arg1 . '")[0].click()'); - } - - /** - * @Then I click paginator page :arg1 - */ - public function iClickPaginatorPage($arg1) - { - $this->getSession()->executeScript('$("a.item[data-page=' . $arg1 . ']").click()'); - } - - /** - * @Then I see button :arg1 - */ - public function iSee($arg1) - { - $element = $this->getSession()->getPage()->find('xpath', '//div[text()="' . $arg1 . '"]'); - if ($element->getAttribute('style')) { - throw new Exception("Element with text \"{$arg1}\" must be invisible"); - } - } - - /** - * @Then dump :arg1 - */ - public function dump($arg1) - { - $element = $this->getSession()->getPage()->find('xpath', '//div[text()="' . $arg1 . '"]'); - var_dump($element->getOuterHtml()); - } - - /** - * @Then I don't see button :arg1 - */ - public function iDontSee($arg1) - { - $element = $this->getSession()->getPage()->find('xpath', '//div[text()="' . $arg1 . '"]'); - if (mb_strpos('display: none', $element->getAttribute('style')) !== false) { - throw new Exception("Element with text \"{$arg1}\" must be invisible"); - } - } - - /** - * @Then Label changes to a number - */ - public function labelChangesToNumber() - { - $element = $this->getSession()->getPage()->findById($this->buttonId); - $value = trim($element->getHtml()); - if (!is_numeric($value)) { - throw new Exception('Label must be numeric on button: ' . $this->buttonId . ' : ' . $value); - } - } - - /** - * @Then /^container "([^"]*)" should display "([^"]*)" item\(s\)$/ - */ - public function containerShouldHaveNumberOfItem($selector, int $numberOfitems) - { - $items = $this->getSession()->getPage()->findAll('css', $selector); - $count = 0; - foreach ($items as $el => $item) { - ++$count; - } - if ($count !== $numberOfitems) { - throw new Exception('Items does not match. There were ' . $count . ' item in container'); - } - } - - /** - * @Then I press Modal button :arg - */ - public function iPressModalButton($arg) - { - $modal = $this->getSession()->getPage()->find('css', '.modal.transition.visible.active.front'); - if ($modal === null) { - throw new Exception('No modal found'); - } - // find button in modal - $btn = $modal->find('xpath', '//div[text()="' . $arg . '"]'); - if (!$btn) { - throw new Exception('Cannot find button in modal'); - } - $btn->click(); - } - - /** - * @Then Modal is open with text :arg1 - * - * Check if text is present in modal or dynamic modal. - */ - public function modalIsOpenWithText($arg1) - { - $modal = $this->waitForNodeElement('.modal.transition.visible.active.front'); - if ($modal === null) { - throw new Exception('No modal found'); - } - // find text in modal - $text = $modal->find('xpath', '//div[text()="' . $arg1 . '"]'); - if (!$text || trim($text->getText()) !== $arg1) { - throw new Exception('No such text in modal'); - } - } - - /** - * @Then Modal is showing text :arg1 inside tag :arg2 - */ - public function modalIsShowingText($arg1, $arg2) - { - // get modal - $modal = $this->waitForNodeElement('.modal.transition.visible.active.front'); - if ($modal === null) { - throw new Exception('No modal found'); - } - // find text in modal - $text = $modal->find('xpath', '//' . $arg2 . '[text()="' . $arg1 . '"]'); - if (!$text || $text->getText() !== $arg1) { - throw new Exception('No such text in modal'); - } - } - - /** - * Get a node element by it's selector. - * Will try to get element for 20ms. - * Exemple: Use with a modal window where reloaded content - * will resize it's window thus making it not accessible at first. - */ - private function waitForNodeElement(string $selector, int $ms = 20): ?NodeElement - { - $counter = 0; - $element = null; - while ($counter < $ms) { - $element = $this->getSession()->getPage()->find('css', $selector); - if ($element === null) { - usleep(1000); - ++$counter; - } else { - break; - } - } - - return $element; - } - - /** - * @Then Active tab should be :arg1 - */ - public function activeTabShouldBe($arg1) - { - $tab = $this->getSession()->getPage()->find('css', '.ui.tabular.menu > .item.active'); - if ($tab->getText() !== $arg1) { - throw new Exception('Active tab is not ' . $arg1); - } - } - - /** - * @Then I hide js modal - * - * Hide js modal. - */ - public function iHideJsModal() - { - $this->getSession()->executeScript('$(".modal.active.front").modal("hide")'); - } - - /** - * @Then I scroll to top - */ - public function iScrollToTop() - { - $this->getSession()->executeScript('window.scrollTo(0,0)'); - } - - /** - * @Then Toast display should contains text :arg1 - */ - public function toastDisplayShouldContainText($arg1) - { - // get toast - $toast = $this->getSession()->getPage()->find('css', '.ui.toast-container'); - if ($toast === null) { - throw new Exception('No toast found'); - } - $content = $toast->find('css', '.content'); - if ($content === null) { - throw new Exception('No Content in Toast'); - } - // find text in toast - $text = $content->find('xpath', '//div'); - if (!$text || mb_strpos($text->getText(), $arg1) === false) { - throw new Exception('No such text in toast'); - } - } - - /** - * @Then I select value :arg1 in lookup :arg2 - * - * Select a value in a lookup control. - */ - public function iSelectValueInLookup($arg1, $arg2) - { - // get dropdown item from semantic ui which is direct parent of input html element - $inputElem = $this->getSession()->getPage()->find('css', 'input[name=' . $arg2 . ']'); - if ($inputElem === null) { - throw new Exception('Lookup element not found: ' . $arg2); - } - $lookupElem = $inputElem->getParent(); - - // open dropdown and wait till fully opened (just a click is not triggering it) - $this->getSession()->executeScript('$("#' . $lookupElem->getAttribute('id') . '").dropdown("show")'); - $this->jqueryWait('$("#' . $lookupElem->getAttribute('id') . '").hasClass("visible")'); - - // select value - $valueElem = $lookupElem->find('xpath', '//div[text()="' . $arg1 . '"]'); - if ($valueElem === null || $valueElem->getText() !== $arg1) { - throw new Exception('Value not found: ' . $arg1); - } - $this->getSession()->executeScript('$("#' . $lookupElem->getAttribute('id') . '").dropdown("set selected", ' . $valueElem->getAttribute('data-value') . ');'); - $this->jqueryWait(); - - // hide dropdown and wait till fully closed - $this->getSession()->executeScript('$("#' . $lookupElem->getAttribute('id') . '").dropdown("hide");'); - $this->jqueryWait(); - // for unknown reasons, dropdown very often remains visible in CI, so hide twice - $this->getSession()->executeScript('$("#' . $lookupElem->getAttribute('id') . '").dropdown("hide");'); - $this->jqueryWait('!$("#' . $lookupElem->getAttribute('id') . '").hasClass("visible")'); - } - - /** - * @Then I search grid for :arg1 - */ - public function iSearchGridFor($arg1) - { - $search = $this->getSession()->getPage()->find('css', 'input.atk-grid-search'); - if (!$search) { - throw new Exception('Unable to find search input.'); - } - - $search->setValue($arg1); - } - - /** - * @Then /^page url should contains \'([^\']*)\'$/ - */ - public function pageUrlShouldContains($text) - { - $url = $this->getSession()->getCurrentUrl(); - if (!strpos($url, $text)) { - throw new Exception('Text : "' . $text . '" not found in ' . $url); - } - } - - /** - * @Then /^I wait for the page to be loaded$/ - */ - public function waitForThePageToBeLoaded() - { - // This line in test-unit.yml is causing test to fail. Need to increase wait time to compensate. - // sed -i 's/usleep(100000)/usleep(5000)/' vendor/behat/mink-selenium2-driver/src/Selenium2Driver.php - usleep(500000); - $this->getSession()->wait(10000, "document.readyState === 'complete'"); - } - - /** - * @Then I click icon using css :arg1 - */ - public function iClickIconUsingCss($arg1) - { - $icon = $this->getSession()->getPage()->find('css', $arg1); - if (!$icon) { - throw new Exception('Unable to find search remove icon.'); - } - - $icon->click(); - } - - /** - * Generic ScopeBuilder rule with select operator and input value. - * - * @Then /^rule "([^"]*)" operator is "([^"]*)" and value is "([^"]*)"$/ - */ - public function scopeBuilderRule($name, $operator, $value) - { - $rule = $this->assertScopeBuilderRuleExist($name); - $this->assertSelectedValue($rule, $operator, '.vqb-rule-operator select'); - $this->assertInputValue($rule, $value); - } - - /** - * hasOne reference or enum type rule for ScopeBuilder. - * - * @Then /^reference rule "([^"]*)" operator is "([^"]*)" and value is "([^"]*)"$/ - */ - public function scopeBuilderReferenceRule($name, $operator, $value) - { - $rule = $this->assertScopeBuilderRuleExist($name); - $this->assertSelectedValue($rule, $operator, '.vqb-rule-operator select'); - $this->assertDropdownValue($rule, $value, '.vqb-rule-input .active.item'); - } - - /** - * hasOne select or enum type rule for ScopeBuilder. - * - * @Then /^select rule "([^"]*)" operator is "([^"]*)" and value is "([^"]*)"$/ - */ - public function scopeBuilderSelectRule($name, $operator, $value) - { - $rule = $this->assertScopeBuilderRuleExist($name); - $this->assertSelectedValue($rule, $operator, '.vqb-rule-operator select'); - $this->assertSelectedValue($rule, $value, '.vqb-rule-input select'); - } - - /** - * Date, Time or Datetime rule for ScopeBuilder. - * - * @Then /^date rule "([^"]*)" operator is "([^"]*)" and value is "([^"]*)"$/ - */ - public function scopeBuilderDateRule($name, $operator, $value) - { - $rule = $this->assertScopeBuilderRuleExist($name); - $this->assertSelectedValue($rule, $operator, '.vqb-rule-operator select'); - $this->assertInputValue($rule, $value, 'input.form-control'); - } - - /** - * Boolean type rule for ScopeBuilder. - * - * @Then /^bool rule "([^"]*)" has value "([^"]*)"$/ - */ - public function scopeBuilderBoolRule($name, $value) - { - $this->assertScopeBuilderRuleExist($name); - $idx = ($value === 'Yes') ? 0 : 1; - $isChecked = $this->getSession()->evaluateScript('return $(\'[data-name="' . $name . '"]\').find(\'input\')[' . $idx . '].checked'); - if (!$isChecked) { - throw new Exception('Radio value selected is not: ' . $value); - } - } - - /** - * @Then /^I check if text in "([^"]*)" match text in "([^"]*)"/ - */ - public function compareElementText($compareSelector, $compareToSelector) - { - $compareContainer = $this->getSession()->getPage()->find('css', $compareSelector); - if (!$compareContainer) { - throw new Exception('Unable to find compare container: ' . $compareSelector); - } - - $expectedText = $compareContainer->getText(); - - $compareToContainer = $this->getSession()->getPage()->find('css', $compareToSelector); - if (!$compareToContainer) { - throw new Exception('Unable to find compare to container: ' . $compareToSelector); - } - - $compareToText = $compareToContainer->getText(); - - if ($expectedText !== $compareToText) { - throw new Exception('Data word does not match: ' . $compareToText . ' expected: ' . $expectedText); - } - } - - /** - * @Then /^I check if input value for "([^"]*)" match text in "([^"]*)"$/ - */ - public function compareInputValueToElementText($inputName, $selector) - { - $expected = $this->getSession()->getPage()->find('css', $selector)->getText(); - $input = $this->getSession()->getPage()->find('css', 'input[name="' . $inputName . '"]'); - if (!$input) { - throw new Exception('Unable to find input name: ' . $inputName); - } - - if (preg_replace('~\s*~', '', $expected) !== preg_replace('~\s*~', '', $input->getValue())) { - throw new Exception('Input value does not match: ' . $input->getValue() . ' expected: ' . $expected); - } - } - - /** - * @Then /^text in container using \'([^\']*)\' should contains \'([^\']*)\'$/ - */ - public function textInContainerUsingShouldContains($containerCss, $text) - { - $container = $this->getSession()->getPage()->find('css', $containerCss); - if (!$container) { - throw new Exception('Unable to find container: ' . $containerCss); - } - - if (trim($container->getText()) !== $text) { - throw new Exception('Text not in container ' . $text . ' - ' . $container->getText()); - } - } - - /** - * Find a dropdown component within an html element - * and check if value is set in dropdown. - */ - private function assertDropdownValue(NodeElement $element, string $value, string $selector) - { - $dropdown = $element->find('css', $selector); - if (!$dropdown) { - throw new Exception('Dropdown input not found using selector: ' . $selector); - } - - $dropdownValue = $dropdown->getHtml(); - if ($dropdownValue !== $value) { - throw new Exception('Value: "' . $value . '" not set using selector: ' . $selector); - } - } - - /** - * Find a select input type within an html element - * and check if value is selected. - */ - private function assertSelectedValue(NodeElement $element, string $value, string $selector) - { - $select = $element->find('css', $selector); - if (!$select) { - throw new Exception('Select input not found using selector: ' . $selector); - } - $selectValue = $select->getValue(); - if ($selectValue !== $value) { - throw new Exception('Value: "' . $value . '" not set using selector: ' . $selector); - } - } - - /** - * Find an input within an html element and check - * if value is set. - */ - private function assertInputValue(NodeElement $element, string $value, string $selector = 'input') - { - $input = $element->find('css', $selector); - if (!$input) { - throw new Exception('Input not found in selector: ' . $selector); - } - $inputValue = $input->getValue(); - if ($inputValue !== $value) { - throw new Exception('Input value not is not: ' . $value); - } - } - - private function assertScopeBuilderRuleExist(string $ruleName): NodeElement - { - $rule = $this->getSession()->getPage()->find('css', '.vqb-rule[data-name=' . $ruleName . ']'); - if (!$rule) { - throw new Exception('Rule not found: ' . $ruleName); - } - - return $rule; - } - - /** - * Wait for an element, usually an auto trigger element, to show that loading has start" - * Example, when entering value in JsSearch for grid. We need to auto trigger to fire before - * doing waiting for callback. - * $arg1 should represent the element selector for jQuery. - * - * @Then I wait for loading to start in :arg1 - */ - public function iWaitForLoadingToStartIn($arg1) - { - $this->getSession()->wait(2000, '$("' . $arg1 . '").hasClass("loading")'); - } - - protected function getFinishedScript(): string - { - return 'document.readyState === \'complete\'' - . ' && typeof jQuery !== \'undefined\' && jQuery.active === 0' - . ' && typeof atk !== \'undefined\' && atk.vueService.areComponentsLoaded()'; - } - - /** - * Wait till jQuery AJAX request finished and no animation is perform. - */ - protected function jqueryWait(string $extraWaitCondition = 'true', $maxWaitdurationMs = 5000) - { - $finishedScript = '(' . $this->getFinishedScript() . ') && (' . $extraWaitCondition . ')'; - - $s = microtime(true); - $c = 0; - while (microtime(true) - $s <= $maxWaitdurationMs / 1000) { - $this->getSession()->wait($maxWaitdurationMs, $finishedScript); - usleep(10000); - if ($this->getSession()->evaluateScript($finishedScript)) { - if (++$c >= 2) { - return; - } - } else { - $c = 0; - usleep(50000); - } - } - - throw new Exception('jQuery did not finished within a time limit'); - } - - /** - * @Then /^the field "([^"]*)" should start with "([^"]*)"$/ - */ - public function theShouldStartWith($arg1, $arg2) - { - $field = $this->assertSession()->fieldExists($arg1); - - if (!is_a($field, NodeElement::class, true)) { - throw new Exception('Field' . $arg1 . ' does not exist'); - } - - if (mb_strpos($field->getValue(), $arg2) === false) { - throw new Exception('Field value ' . $field->getValue() . ' does not start with ' . $arg2); - } - } - - /** - * @When I type in editor :id with text :text - * - * @param string $id - * @param string $text - */ - public function iFocusOnId($id, $text) - { - $session = $this->getSession(); - $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - //$element = $session->getPage()->find('css','div[id="'.$id.'"]'); - // get focus - $script = "$('#" . $id . "')[0].innerHTML = '" . $text . "';"; - - $session->executeScript($script); - $session->executeScript("$('#" . $id . "').trigger('keyup')"); - } - - /** - * @Then Modal opens with text :arg1 - * - * Check if text is present in modal or dynamic modal. - */ - public function modalOpensWithText($arg1) - { - //wait until modal open - $this->getSession()->wait(2000, '$(".modal.transition.visible.active").length'); - //wait for dynamic modal - $this->jqueryWait("true", 10000); - //get modal - $modal = $this->getSession()->getPage()->find('css', '.modal.transition.visible.active'); - if ($modal === null) { - throw new Exception('No modal found'); - } - //find text in modal - $text = $modal->find('xpath', '//*[text()="' . $arg1 . '"]'); - if (!$text || $text->getText() !== $arg1) { - throw new Exception('No such text in modal'); - } - } - - /** - * @When I use form with button :arg1 - */ - public function iUseFormWithButton($arg1) - { - $button = $this->getSession()->getPage()->find('xpath', "//div[text()='" . $arg1 . "']"); - $this->buttonId = $button->getAttribute('id'); - $button->click(); - } - - /** - * @When I press editor button :arg1 - */ - public function iPressEditorButton($arg1) - { - $button = $this->getSession()->getPage()->find('css', '.' . $arg1); - $button->click(); - } -} diff --git a/tests-behat/Bootstrap/ContextDump.php b/tests-behat/Bootstrap/ContextDump.php deleted file mode 100644 index b4f451a..0000000 --- a/tests-behat/Bootstrap/ContextDump.php +++ /dev/null @@ -1,38 +0,0 @@ -getTestResult()->getResultCode() === TestResult::FAILED) { - if ($this->getSession()->getDriver() instanceof \Behat\Mink\Driver\Selenium2Driver) { - echo 'Dump of failed step:' . "\n"; - echo 'Current page URL: ' . $this->getSession()->getCurrentUrl() . "\n"; - global $dumpPageCount; - if (++$dumpPageCount <= 1) { // prevent huge tests output - // upload screenshot here if needed in the future - // $screenshotData = $this->getSession()->getScreenshot(); - // echo 'Screenshot URL: ' . $screenshotUrl . "\n"; - echo 'Page source: ' . $this->getSession()->getPage()->getContent() . "\n"; - } else { - echo 'Page source: Source code is dumped for the first failed step only.' . "\n"; - } - } - } - } -} diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index 8c8cb7a..35e649d 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -4,13 +4,13 @@ namespace TextEditor\Tests; -use Atk4\Core\AtkPhpunit; +use Atk4\Data\Schema\TestCase; use Atk4\Ui\App; use Atk4\Ui\Form; use Atk4\Ui\Form\Control\TextEditor; use Atk4\Ui\Layout\Centered; -class TextEditorTest extends AtkPhpunit\TestCase +class TextEditorTest extends TestCase { public function testInit() { diff --git a/tools/CoverageUtil.php b/tools/CoverageUtil.php new file mode 100644 index 0000000..90a0d84 --- /dev/null +++ b/tools/CoverageUtil.php @@ -0,0 +1,38 @@ +includeDirectory(__DIR__ . '/../src'); + self::$coverage = new CodeCoverage((new DriverSelector())->forLineCoverage($filter), $filter); + self::$coverage->start($_SERVER['SCRIPT_NAME']); + } + + public static function saveData(): void + { + self::$coverage->stop(); + $writer = new Report\PHP(); + $writer->process(self::$coverage, dirname(__DIR__) . '/coverage/' . basename($_SERVER['SCRIPT_NAME'], '.php') . '-' . hash('sha256', microtime(true) . random_bytes(64)) . '.cov'); + } +} From dd117f2009cc639d58dcba28dab5a6a04eb76142 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 13:18:32 +0100 Subject: [PATCH 02/52] Fix PHPStan --- demos/_includes/Model/Post.php | 4 +++- demos/init-db.php | 4 +++- src/TextEditor.php | 27 ++++++++++++++------------- tests/TextEditorTest.php | 6 +++--- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/demos/_includes/Model/Post.php b/demos/_includes/Model/Post.php index a67b423..a713ef1 100644 --- a/demos/_includes/Model/Post.php +++ b/demos/_includes/Model/Post.php @@ -1,5 +1,7 @@ addField('subject'); $this->addField('body'); } -} \ No newline at end of file +} diff --git a/demos/init-db.php b/demos/init-db.php index f4fde13..fe7f257 100644 --- a/demos/init-db.php +++ b/demos/init-db.php @@ -1,4 +1,6 @@ - [ ['viewHTML'], ['undo', 'redo'], // Only supported in Blink browsers @@ -34,11 +34,12 @@ class TextEditor extends Textarea 'resetCss' => true, 'autogrow' => true, ]; - public $plugins = []; - protected $required_js = [ + public array $plugins = []; + + private array $required_js = [ '/trumbowyg.js', ]; - protected $required_css = [ + private array $required_css = [ '/ui/trumbowyg.css', ]; @@ -69,7 +70,7 @@ private function addRequiredAssets(): void self::$loaded_assets[] = $js; - $this->getApp()->requireJS($this->assets_path . $js); + $this->getApp()->requireJs($this->assets_path . $js); } foreach ($this->required_css as $css) { @@ -79,16 +80,16 @@ private function addRequiredAssets(): void self::$loaded_assets[] = $css; - $this->getApp()->requireCSS($this->assets_path . $css); + $this->getApp()->requireCss($this->assets_path . $css); } } - private function isAssetLoaded($asset): bool + private function isAssetLoaded(string $asset): bool { return in_array($asset, self::$loaded_assets, false); } - private function addRequiredPlugin($plugin_asset): void + private function addRequiredPlugin(string $plugin_asset): void { $plugin_asset = $this->assets_path . '/plugins/' . $plugin_asset; @@ -98,6 +99,6 @@ private function addRequiredPlugin($plugin_asset): void self::$loaded_assets[] = $plugin_asset; - $this->getApp()->requireJS($plugin_asset); + $this->getApp()->requireJs($plugin_asset); } } diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index 35e649d..a569a11 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -12,7 +12,7 @@ class TextEditorTest extends TestCase { - public function testInit() + public function testInit(): void { $app = new AppFormTestMock([ 'catch_exceptions' => false, @@ -32,7 +32,7 @@ public function testInit() $this->assertSame(1, preg_match('/trumbowyg/m', $app->output)); } - public function testPlugin() + public function testPlugin(): void { $app = new AppFormTestMock([ 'catch_exceptions' => false, @@ -59,7 +59,7 @@ public function testPlugin() class AppFormTestMock extends App { - public $output; + public string $output; protected function outputResponse(string $data, array $headers): void { From 255c77b95c87631c854bc0a4bfb6fe92dd240279 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 15:54:52 +0100 Subject: [PATCH 03/52] Fix Stan & Behat --- demos/_demo-data/create-db.php | 2 +- demos/db.default.php | 2 +- tests-behat/editor.feature | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/demos/_demo-data/create-db.php b/demos/_demo-data/create-db.php index 6e496a4..71a4b7c 100644 --- a/demos/_demo-data/create-db.php +++ b/demos/_demo-data/create-db.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Login\Demos; +namespace Atk4\Ui\Demos; use Atk4\Data\Persistence; use Atk4\Data\Schema\Migrator; diff --git a/demos/db.default.php b/demos/db.default.php index fac51bf..5eccc4a 100644 --- a/demos/db.default.php +++ b/demos/db.default.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Login\Demos; +namespace Atk4\Ui\Demos; use Atk4\Data\Persistence; diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 8e5816a..c3794d4 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -1,13 +1,14 @@ Feature: Editor + Check basic functionalities - Scenario: Test init + Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "editor-editor" with text "editor content" When I use form with button "Save" Then Modal opens with text "editor : editor content" - Scenario: Test see html + Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "editor-editor" with text "editor content" From e15f88c8cdbf4ac3df2a826bed64f788d9e23ab5 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 17:01:32 +0100 Subject: [PATCH 04/52] Fix Stan & Behat --- behat.yml.dist | 2 +- tests-behat/editor.feature | 10 +++++----- tests/Context.php | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 tests/Context.php diff --git a/behat.yml.dist b/behat.yml.dist index 10c40d4..440c704 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -5,7 +5,7 @@ default: features: '%paths.base%/tests-behat' contexts: - Behat\MinkExtension\Context\MinkContext - - Atk4\Ui\Behat\Context + - TextEditor\Tests\Context extensions: Behat\MinkExtension: base_url: 'http://127.0.0.1:8888/demos' diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index c3794d4..04bbf30 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -5,13 +5,13 @@ Feature: Editor Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "editor-editor" with text "editor content" - When I use form with button "Save" - Then Modal opens with text "editor : editor content" + When I press button "Save" + Then Modal is open with text "editor : editor content" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "editor-editor" with text "editor content" - When I press editor button "trumbowyg-viewHTML-button" - When I use form with button "Save" - Then Modal opens with text "editor :

editor content

" \ No newline at end of file + When I press editor button with class "trumbowyg-viewHTML-button" + When I press button "Save" + Then Modal is open with text "editor :

editor content

" \ No newline at end of file diff --git a/tests/Context.php b/tests/Context.php new file mode 100644 index 0000000..578d120 --- /dev/null +++ b/tests/Context.php @@ -0,0 +1,32 @@ +getSession(); + $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); + //$element = $session->getPage()->find('css','div[id="'.$id.'"]'); + // get focus + $script = "$('#" . $id . "')[0].innerHTML = '" . $text . "';"; + + $session->executeScript($script); + $session->executeScript("$('#" . $id . "').trigger('keyup')"); + } + + /** + * @When I press editor button with class :arg1 + */ + public function iPressEditorButtonByClass($arg1): void + { + $button = $this->getSession()->getPage()->find('css', '.' . $arg1); + $button->click(); + } +} From c8923cc5acf3ae02bc4d7864dbdb84fddd8f9f23 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 17:02:18 +0100 Subject: [PATCH 05/52] Fix Stan & Behat --- tests/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Context.php b/tests/Context.php index 578d120..305fd5e 100644 --- a/tests/Context.php +++ b/tests/Context.php @@ -24,7 +24,7 @@ public function iTypeInEditor(string $id, string $text): void /** * @When I press editor button with class :arg1 */ - public function iPressEditorButtonByClass($arg1): void + public function iPressEditorButtonByClass(string $arg1): void { $button = $this->getSession()->getPage()->find('css', '.' . $arg1); $button->click(); From b8ec6962be34615922136b72f0568de1da5d5a3a Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 17:06:55 +0100 Subject: [PATCH 06/52] Fix Stan & Behat --- composer.json | 8 ++++++-- tests-behat/editor.feature | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index d8387ad..35f17bd 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "name": "abbadon1334/atk4-trumbowyg", "description": "ATK4 WYSIWYG editor using javascript library trumbowyg", "license": "MIT", + "type": "library", "authors": [ { "name": "Francesco Danti", @@ -32,6 +33,8 @@ "symfony/console": "^4.4.30 || ^5.3.7", "symfony/css-selector": "^4.4.24 || ^5.2.9" }, + "minimum-stability": "dev", + "prefer-stable": true, "autoload": { "psr-4": { "Atk4\\Ui\\Form\\Control\\": "src/" @@ -43,6 +46,7 @@ "TextEditor\\Behat\\": "tests-behat/Bootstrap/" } }, - "minimum-stability": "dev", - "prefer-stable": true + "config": { + "sort-packages": true + } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 04bbf30..d103067 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -4,14 +4,14 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "editor-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I press button "Save" Then Modal is open with text "editor : editor content" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "editor-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I press editor button with class "trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with text "editor :

editor content

" \ No newline at end of file From 9402db883d755bb897a59a3d6c092703aaab7375 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 17:19:11 +0100 Subject: [PATCH 07/52] Fix Stan & Behat --- tests-behat/editor.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index d103067..579f877 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -6,7 +6,7 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body-editor" with text "editor content" When I press button "Save" - Then Modal is open with text "editor : editor content" + Then Modal is open with text "body : editor content" Scenario: Given I am on "index.php" @@ -14,4 +14,4 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I press editor button with class "trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "editor :

editor content

" \ No newline at end of file + Then Modal is open with text "body :

editor content

" \ No newline at end of file From 58e5357b28d58346f47b7ce0dae838a7635091eb Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 17:51:09 +0100 Subject: [PATCH 08/52] Fix Stan & Behat --- tests/Context.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Context.php b/tests/Context.php index 305fd5e..3f2789d 100644 --- a/tests/Context.php +++ b/tests/Context.php @@ -4,6 +4,8 @@ namespace TextEditor\Tests; +use Behat\Mink\Element\NodeElement; + class Context extends \Atk4\Ui\Behat\Context { /** @@ -21,6 +23,37 @@ public function iTypeInEditor(string $id, string $text): void $session->executeScript("$('#" . $id . "').trigger('keyup')"); } + /** + * @Then Modal is open with text :arg1 + * @Then Modal is open with text :arg1 in tag :arg2 + * + * Check if text is present in modal or dynamic modal. + */ + public function modalIsOpenWithText(string $text, string $tag = 'div'): void + { + //wait until modal open + $this->getSession()->wait(2000, '$(".modal.transition.visible.active").length'); + + //wait for dynamic modal + $this->jqueryWait('true', 10000); + + //get modal + $modal = $this->getSession()->getPage()->find('css', '.modal.transition.visible.active'); + if ($modal === null) { + throw new \Exception('No modal found'); + } + + //find text in modal + $modal_text = $modal->find('xpath', '//*[text()="' . $text . '"]'); + if (!is_a($modal_text, NodeElement::class, true)) { + throw new \Exception('Text not found element'); + } + + if ($modal_text->getText() !== $text) { + throw new \Exception('Text not found, but found this text in element :' . $modal_text->getText()); + } + } + /** * @When I press editor button with class :arg1 */ From 06cf213ac77cc5313b6953499215d1ab84e41ab0 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 18:01:03 +0100 Subject: [PATCH 09/52] Fix Stan & Behat --- tests/Context.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/Context.php b/tests/Context.php index 3f2789d..f7d0f5c 100644 --- a/tests/Context.php +++ b/tests/Context.php @@ -23,12 +23,6 @@ public function iTypeInEditor(string $id, string $text): void $session->executeScript("$('#" . $id . "').trigger('keyup')"); } - /** - * @Then Modal is open with text :arg1 - * @Then Modal is open with text :arg1 in tag :arg2 - * - * Check if text is present in modal or dynamic modal. - */ public function modalIsOpenWithText(string $text, string $tag = 'div'): void { //wait until modal open From 8b1b2088b463ef56cd8f8deb8a889c67357c85c1 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sat, 29 Jan 2022 18:03:36 +0100 Subject: [PATCH 10/52] Fix Stan & Behat --- tests-behat/editor.feature | 4 ++-- tests/Context.php | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 579f877..dc59995 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -6,7 +6,7 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body-editor" with text "editor content" When I press button "Save" - Then Modal is open with text "body : editor content" + Then Modal Editor is open with text "body : editor content" Scenario: Given I am on "index.php" @@ -14,4 +14,4 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I press editor button with class "trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "body :

editor content

" \ No newline at end of file + Then Modal Editor is open with text "body :

editor content

" \ No newline at end of file diff --git a/tests/Context.php b/tests/Context.php index f7d0f5c..9c797d8 100644 --- a/tests/Context.php +++ b/tests/Context.php @@ -23,7 +23,12 @@ public function iTypeInEditor(string $id, string $text): void $session->executeScript("$('#" . $id . "').trigger('keyup')"); } - public function modalIsOpenWithText(string $text, string $tag = 'div'): void + /** + * @Then Modal Editor is open with text :arg1 + * + * Check if text is present in modal or dynamic modal. + */ + public function editorModalIsOpenWithText(string $text): void { //wait until modal open $this->getSession()->wait(2000, '$(".modal.transition.visible.active").length'); From 1e6aed714430dede529657d652871f099948c25a Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 00:02:14 +0100 Subject: [PATCH 11/52] Fix Stan & Behat --- .github/workflows/test-unit.yml | 2 +- behat.yml.dist | 2 +- composer.json | 9 ++- demos/_demo-data/create-db.php | 2 +- demos/_includes/Model/Post.php | 2 +- demos/index.php | 4 +- demos/init-app.php | 2 +- demos/init-autoloader.php | 6 +- demos/init-db.php | 2 +- src/TextEditor.php | 12 ++-- {tests => tests-behat}/Context.php | 2 +- tests/TextEditorTest.php | 109 +++++++++++++++++++++-------- 12 files changed, 102 insertions(+), 52 deletions(-) rename {tests => tests-behat}/Context.php (99%) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 96c7997..b0b98bb 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -60,7 +60,7 @@ jobs: - name: Check Coding Style (only for CodingStyle) if: matrix.type == 'CodingStyle' run: | - if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Ui\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi + if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Component\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --verbose - name: Run Static Analysis (only for StaticAnalysis) diff --git a/behat.yml.dist b/behat.yml.dist index 440c704..b1e3dc3 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -5,7 +5,7 @@ default: features: '%paths.base%/tests-behat' contexts: - Behat\MinkExtension\Context\MinkContext - - TextEditor\Tests\Context + - Atk4\Ui\Component\Tests\Context extensions: Behat\MinkExtension: base_url: 'http://127.0.0.1:8888/demos' diff --git a/composer.json b/composer.json index 35f17bd..75d9b58 100644 --- a/composer.json +++ b/composer.json @@ -42,11 +42,14 @@ }, "autoload-dev": { "psr-4": { - "TextEditor\\Tests\\": "tests/", - "TextEditor\\Behat\\": "tests-behat/Bootstrap/" + "Atk4\\Ui\\Component\\Tests\\": "tests/", + "Atk4\\Ui\\Component\\Behat\\": "tests-behat/" } }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "ergebnis/composer-normalize": true + } } } diff --git a/demos/_demo-data/create-db.php b/demos/_demo-data/create-db.php index 71a4b7c..dab80ea 100644 --- a/demos/_demo-data/create-db.php +++ b/demos/_demo-data/create-db.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; use Atk4\Data\Persistence; use Atk4\Data\Schema\Migrator; diff --git a/demos/_includes/Model/Post.php b/demos/_includes/Model/Post.php index a713ef1..b5596a6 100644 --- a/demos/_includes/Model/Post.php +++ b/demos/_includes/Model/Post.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos\Model; +namespace Atk4\Ui\Component\Demos\Model; class Post extends \Atk4\Data\Model { diff --git a/demos/index.php b/demos/index.php index 9a0c335..2f135c8 100644 --- a/demos/index.php +++ b/demos/index.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; -use Atk4\Ui\Demos\Model\Post; +use Atk4\Ui\Component\Demos\Model\Post; use Atk4\Ui\Layout\Centered; date_default_timezone_set('UTC'); diff --git a/demos/init-app.php b/demos/init-app.php index d4d5bbd..4181df6 100644 --- a/demos/init-app.php +++ b/demos/init-app.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; use Atk4\Data\Persistence; use Atk4\Ui\App; diff --git a/demos/init-autoloader.php b/demos/init-autoloader.php index 3e259aa..43c8256 100644 --- a/demos/init-autoloader.php +++ b/demos/init-autoloader.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; -use TextEditor\Tests\TextEditorTest; +use Atk4\Ui\Component\Tests\TextEditorTest; $isRootProject = file_exists(__DIR__ . '/../vendor/autoload.php'); /** @var \Composer\Autoload\ClassLoader $loader */ @@ -13,5 +13,5 @@ throw new \Error('Demos can be run only if atk4/login is a root composer project or if dev files are autoloaded'); } $loader->setClassMapAuthoritative(false); -$loader->setPsr4('Atk4\Ui\Demos\\', __DIR__ . '/_includes'); +$loader->setPsr4('Atk4\\Ui\\Component\\Demos\\', __DIR__ . '/_includes'); unset($isRootProject, $loader); diff --git a/demos/init-db.php b/demos/init-db.php index fe7f257..e87f147 100644 --- a/demos/init-db.php +++ b/demos/init-db.php @@ -4,7 +4,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; try { require_once file_exists(__DIR__ . '/db.php') diff --git a/src/TextEditor.php b/src/TextEditor.php index 7fd4a70..68ce3fb 100644 --- a/src/TextEditor.php +++ b/src/TextEditor.php @@ -4,11 +4,9 @@ namespace Atk4\Ui\Form\Control; -// @TODO find a better way to load assets - class TextEditor extends Textarea { - private static array $loaded_assets = []; + protected static array $loaded_assets = []; public $defaultTemplate = __DIR__ . '/../template/trumbowyg.html'; @@ -34,6 +32,7 @@ class TextEditor extends Textarea 'resetCss' => true, 'autogrow' => true, ]; + public array $plugins = []; private array $required_js = [ @@ -48,6 +47,7 @@ protected function init(): void parent::init(); $this->addRequiredAssets(); + foreach ($this->plugins as $plugin) { $this->addRequiredPlugin($plugin); } @@ -68,7 +68,7 @@ private function addRequiredAssets(): void continue; } - self::$loaded_assets[] = $js; + static::$loaded_assets[] = $js; $this->getApp()->requireJs($this->assets_path . $js); } @@ -78,7 +78,7 @@ private function addRequiredAssets(): void continue; } - self::$loaded_assets[] = $css; + static::$loaded_assets[] = $css; $this->getApp()->requireCss($this->assets_path . $css); } @@ -97,7 +97,7 @@ private function addRequiredPlugin(string $plugin_asset): void return; } - self::$loaded_assets[] = $plugin_asset; + static::$loaded_assets[] = $plugin_asset; $this->getApp()->requireJs($plugin_asset); } diff --git a/tests/Context.php b/tests-behat/Context.php similarity index 99% rename from tests/Context.php rename to tests-behat/Context.php index 9c797d8..0125264 100644 --- a/tests/Context.php +++ b/tests-behat/Context.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace TextEditor\Tests; +namespace Atk4\Ui\Component\Behat; use Behat\Mink\Element\NodeElement; diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index a569a11..1875f1e 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace TextEditor\Tests; +namespace Atk4\Ui\Component\Tests; use Atk4\Data\Schema\TestCase; use Atk4\Ui\App; @@ -12,48 +12,95 @@ class TextEditorTest extends TestCase { + protected function setUp(): void + { + parent::setUp(); + + $reflectedClass = new \ReflectionClass(TextEditor::class); + $reflectedClass->setStaticPropertyValue('loaded_assets', []); + } + public function testInit(): void { - $app = new AppFormTestMock([ - 'catch_exceptions' => false, - 'always_run' => false, - 'catch_runaway_callbacks' => false, - 'call_exit' => false, - ]); - $app->initLayout([Centered::class]); - $form = Form::addTo($app); - $form->addControl('subject'); - $form->addControl('editor', [ - TextEditor::class, - 'placeholder' => 'test placeholder', - ]); - $app->run(); + ob_start(); + try { + $app = $this->getApp(); + + $app->initLayout([Centered::class]); - $this->assertSame(1, preg_match('/trumbowyg/m', $app->output)); + $form = Form::addTo($app); + $form->addControl('subject'); + $form->addControl('editor', [ + TextEditor::class, + 'placeholder' => 'test placeholder', + ]); + $app->run(); + } finally { + $output = ob_get_clean(); + } + + $this->assertSame(1, substr_count($output, '/libs/Trumbowyg/2.20.0/trumbowyg.js')); + } + + public function testCheckDouble(): void + { + ob_start(); + try { + $app = $this->getApp(); + + $app->initLayout([Centered::class]); + + $form = Form::addTo($app); + $form->addControl('subject'); + $form->addControl('editor', [ + TextEditor::class, + 'placeholder' => 'test placeholder', + ]); + $form->addControl('editor2', [ + TextEditor::class, + 'placeholder' => 'test placeholder', + ]); + $app->run(); + } finally { + $output = ob_get_clean(); + } + + $this->assertSame(1, substr_count($output, '/libs/Trumbowyg/2.20.0/trumbowyg.js')); } public function testPlugin(): void { - $app = new AppFormTestMock([ + ob_start(); + try { + $app = $this->getApp(); + + $app->initLayout([Centered::class]); + + $form = Form::addTo($app); + $form->addControl('subject'); + $form->addControl('editor', [ + TextEditor::class, + 'placeholder' => 'test placeholder', + 'plugins' => [ + 'base64', + ], + ]); + $app->run(); + } finally { + $output = ob_get_clean(); + } + + $this->assertStringContainsString('plugins/base64', $output); + } + + private function getApp(): App + { + return new App([ 'catch_exceptions' => false, 'always_run' => false, 'catch_runaway_callbacks' => false, 'call_exit' => false, ]); - - $app->initLayout([Centered::class]); - $form = Form::addTo($app); - $form->addControl('subject'); - $form->addControl('editor', [ - TextEditor::class, - 'placeholder' => 'test placeholder', - 'plugins' => [ - 'base64', - ], - ]); - $app->run(); - - $this->assertSame(1, preg_match('/base64/m', $app->output)); } } From c70c7af2c0b7ecd01e48ce4ddc47e3950044b4b0 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 00:06:15 +0100 Subject: [PATCH 12/52] Fix Stan & Behat --- demos/_demo-data/create-db.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/_demo-data/create-db.php b/demos/_demo-data/create-db.php index dab80ea..8d375ba 100644 --- a/demos/_demo-data/create-db.php +++ b/demos/_demo-data/create-db.php @@ -6,7 +6,7 @@ use Atk4\Data\Persistence; use Atk4\Data\Schema\Migrator; -use Atk4\Ui\Demos\Model\Post; +use Atk4\Ui\Component\Demos\Model\Post; require_once __DIR__ . '/../init-autoloader.php'; From da0744a48902e98e2a10b8865b134ec27dc56065 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 00:07:55 +0100 Subject: [PATCH 13/52] Fix Stan & Behat --- .github/workflows/test-unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index b0b98bb..1cf789f 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -60,7 +60,7 @@ jobs: - name: Check Coding Style (only for CodingStyle) if: matrix.type == 'CodingStyle' run: | - if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Component\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi + if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Ui\\\\Component\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --verbose - name: Run Static Analysis (only for StaticAnalysis) From 495788410373e697501a0e2867b529eba0b53aff Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 00:15:02 +0100 Subject: [PATCH 14/52] Fix Stan & Behat --- behat.yml.dist | 2 +- demos/db.default.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/behat.yml.dist b/behat.yml.dist index b1e3dc3..9f31944 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -5,7 +5,7 @@ default: features: '%paths.base%/tests-behat' contexts: - Behat\MinkExtension\Context\MinkContext - - Atk4\Ui\Component\Tests\Context + - Atk4\Ui\Component\Behat\Context extensions: Behat\MinkExtension: base_url: 'http://127.0.0.1:8888/demos' diff --git a/demos/db.default.php b/demos/db.default.php index 5eccc4a..57acc2c 100644 --- a/demos/db.default.php +++ b/demos/db.default.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Demos; +namespace Atk4\Ui\Component\Demos; use Atk4\Data\Persistence; From 7b6537e4fc95e4ee644cddd9ab04cdc639601d70 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 07:53:14 +0100 Subject: [PATCH 15/52] Use unique Namespace --- .github/workflows/test-unit.yml | 2 +- behat.yml.dist | 2 +- composer.json | 6 +++--- demos/_demo-data/create-db.php | 4 ++-- demos/_includes/Model/Post.php | 2 +- demos/db.default.php | 2 +- demos/index.php | 4 ++-- demos/init-app.php | 2 +- demos/init-autoloader.php | 6 +++--- demos/init-db.php | 2 +- src/TextEditor.php | 4 +++- tests-behat/Context.php | 2 +- tests/TextEditorTest.php | 4 ++-- 13 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 1cf789f..ff2326b 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -60,7 +60,7 @@ jobs: - name: Check Coding Style (only for CodingStyle) if: matrix.type == 'CodingStyle' run: | - if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\Ui\\\\Component\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi + if [ "$(find demos/ -name '*.php' -print0 | xargs -0 grep -L "namespace Atk4\\\\TextEditor\\\\Demos[;\\\\]" | tee /dev/fd/2)" ]; then echo 'All demos/ files must have namespace declared' && (exit 1); fi vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --verbose - name: Run Static Analysis (only for StaticAnalysis) diff --git a/behat.yml.dist b/behat.yml.dist index 9f31944..771717f 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -5,7 +5,7 @@ default: features: '%paths.base%/tests-behat' contexts: - Behat\MinkExtension\Context\MinkContext - - Atk4\Ui\Component\Behat\Context + - Atk4\TextEditor\Behat\Context extensions: Behat\MinkExtension: base_url: 'http://127.0.0.1:8888/demos' diff --git a/composer.json b/composer.json index 75d9b58..3756a9c 100644 --- a/composer.json +++ b/composer.json @@ -37,13 +37,13 @@ "prefer-stable": true, "autoload": { "psr-4": { - "Atk4\\Ui\\Form\\Control\\": "src/" + "Atk4\\TextEditor\\": "src/" } }, "autoload-dev": { "psr-4": { - "Atk4\\Ui\\Component\\Tests\\": "tests/", - "Atk4\\Ui\\Component\\Behat\\": "tests-behat/" + "Atk4\\TextEditor\\Tests\\": "tests/", + "Atk4\\TextEditor\\Behat\\": "tests-behat/" } }, "config": { diff --git a/demos/_demo-data/create-db.php b/demos/_demo-data/create-db.php index 8d375ba..ec1ea3f 100644 --- a/demos/_demo-data/create-db.php +++ b/demos/_demo-data/create-db.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; use Atk4\Data\Persistence; use Atk4\Data\Schema\Migrator; -use Atk4\Ui\Component\Demos\Model\Post; +use Atk4\TextEditor\Demos\Model\Post; require_once __DIR__ . '/../init-autoloader.php'; diff --git a/demos/_includes/Model/Post.php b/demos/_includes/Model/Post.php index b5596a6..1b4ddd2 100644 --- a/demos/_includes/Model/Post.php +++ b/demos/_includes/Model/Post.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos\Model; +namespace Atk4\TextEditor\Demos\Model; class Post extends \Atk4\Data\Model { diff --git a/demos/db.default.php b/demos/db.default.php index 57acc2c..8a71e41 100644 --- a/demos/db.default.php +++ b/demos/db.default.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; use Atk4\Data\Persistence; diff --git a/demos/index.php b/demos/index.php index 2f135c8..ce14994 100644 --- a/demos/index.php +++ b/demos/index.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; -use Atk4\Ui\Component\Demos\Model\Post; +use Atk4\TextEditor\Demos\Model\Post; use Atk4\Ui\Layout\Centered; date_default_timezone_set('UTC'); diff --git a/demos/init-app.php b/demos/init-app.php index 4181df6..e0f95ce 100644 --- a/demos/init-app.php +++ b/demos/init-app.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; use Atk4\Data\Persistence; use Atk4\Ui\App; diff --git a/demos/init-autoloader.php b/demos/init-autoloader.php index 43c8256..88f7229 100644 --- a/demos/init-autoloader.php +++ b/demos/init-autoloader.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; -use Atk4\Ui\Component\Tests\TextEditorTest; +use Atk4\TextEditor\Tests\TextEditorTest; $isRootProject = file_exists(__DIR__ . '/../vendor/autoload.php'); /** @var \Composer\Autoload\ClassLoader $loader */ @@ -13,5 +13,5 @@ throw new \Error('Demos can be run only if atk4/login is a root composer project or if dev files are autoloaded'); } $loader->setClassMapAuthoritative(false); -$loader->setPsr4('Atk4\\Ui\\Component\\Demos\\', __DIR__ . '/_includes'); +$loader->setPsr4('Atk4\\TextEditor\\Demos\\', __DIR__ . '/_includes'); unset($isRootProject, $loader); diff --git a/demos/init-db.php b/demos/init-db.php index e87f147..265eb2b 100644 --- a/demos/init-db.php +++ b/demos/init-db.php @@ -4,7 +4,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Demos; +namespace Atk4\TextEditor\Demos; try { require_once file_exists(__DIR__ . '/db.php') diff --git a/src/TextEditor.php b/src/TextEditor.php index 68ce3fb..0c32e9b 100644 --- a/src/TextEditor.php +++ b/src/TextEditor.php @@ -2,7 +2,9 @@ declare(strict_types=1); -namespace Atk4\Ui\Form\Control; +namespace Atk4\TextEditor; + +use Atk4\Ui\Form\Control\Textarea; class TextEditor extends Textarea { diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 0125264..31d1e4b 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Behat; +namespace Atk4\TextEditor\Behat; use Behat\Mink\Element\NodeElement; diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index 1875f1e..b9ccfb1 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Atk4\Ui\Component\Tests; +namespace Atk4\TextEditor\Tests; use Atk4\Data\Schema\TestCase; use Atk4\Ui\App; use Atk4\Ui\Form; -use Atk4\Ui\Form\Control\TextEditor; +use Atk4\TextEditor\TextEditor; use Atk4\Ui\Layout\Centered; class TextEditorTest extends TestCase From 2972de6f915eb4e6e144bd868cf1e1b5624bbbdf Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 08:02:17 +0100 Subject: [PATCH 16/52] Use unique Namespace --- demos/index.php | 2 +- tests/TextEditorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/index.php b/demos/index.php index ce14994..588f48b 100644 --- a/demos/index.php +++ b/demos/index.php @@ -21,7 +21,7 @@ $form->addControl('subject'); $form->addControl('body', [ - \Atk4\Ui\Form\Control\TextEditor::class, + \Atk4\TextEditor\TextEditor::class, 'placeholder' => 'test placeholder', ]); diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index b9ccfb1..4419229 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -5,9 +5,9 @@ namespace Atk4\TextEditor\Tests; use Atk4\Data\Schema\TestCase; +use Atk4\TextEditor\TextEditor; use Atk4\Ui\App; use Atk4\Ui\Form; -use Atk4\TextEditor\TextEditor; use Atk4\Ui\Layout\Centered; class TextEditorTest extends TestCase From 7d49ef278ca31d41ef2773c1cf5de6eae907f792 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 08:08:38 +0100 Subject: [PATCH 17/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 31d1e4b..b681ebb 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -49,7 +49,7 @@ public function editorModalIsOpenWithText(string $text): void } if ($modal_text->getText() !== $text) { - throw new \Exception('Text not found, but found this text in element :' . $modal_text->getText()); + throw new \Exception('Text not found, but found this text in element'); } } From e1c590af516bed00b59bb1a2df14879701c22c54 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 08:51:47 +0100 Subject: [PATCH 18/52] Fix Behat test 8.1 --- tests-behat/Context.php | 39 -------------------------------------- tests-behat/editor.feature | 6 +++--- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index b681ebb..cad3a4a 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -22,43 +22,4 @@ public function iTypeInEditor(string $id, string $text): void $session->executeScript($script); $session->executeScript("$('#" . $id . "').trigger('keyup')"); } - - /** - * @Then Modal Editor is open with text :arg1 - * - * Check if text is present in modal or dynamic modal. - */ - public function editorModalIsOpenWithText(string $text): void - { - //wait until modal open - $this->getSession()->wait(2000, '$(".modal.transition.visible.active").length'); - - //wait for dynamic modal - $this->jqueryWait('true', 10000); - - //get modal - $modal = $this->getSession()->getPage()->find('css', '.modal.transition.visible.active'); - if ($modal === null) { - throw new \Exception('No modal found'); - } - - //find text in modal - $modal_text = $modal->find('xpath', '//*[text()="' . $text . '"]'); - if (!is_a($modal_text, NodeElement::class, true)) { - throw new \Exception('Text not found element'); - } - - if ($modal_text->getText() !== $text) { - throw new \Exception('Text not found, but found this text in element'); - } - } - - /** - * @When I press editor button with class :arg1 - */ - public function iPressEditorButtonByClass(string $arg1): void - { - $button = $this->getSession()->getPage()->find('css', '.' . $arg1); - $button->click(); - } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index dc59995..c881102 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -6,12 +6,12 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body-editor" with text "editor content" When I press button "Save" - Then Modal Editor is open with text "body : editor content" + Then Modal is open with text "body : editor content" in tag "*" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body-editor" with text "editor content" - When I press editor button with class "trumbowyg-viewHTML-button" + When I click first element using class "trumbowyg-viewHTML-button" When I press button "Save" - Then Modal Editor is open with text "body :

editor content

" \ No newline at end of file + Then Modal is open with text "editor content" in tag "p" \ No newline at end of file From 9e02062d31af918aea314695ca059c611e0a017d Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 08:52:55 +0100 Subject: [PATCH 19/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 -- tests-behat/editor.feature | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index cad3a4a..aa241ef 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -4,8 +4,6 @@ namespace Atk4\TextEditor\Behat; -use Behat\Mink\Element\NodeElement; - class Context extends \Atk4\Ui\Behat\Context { /** diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index c881102..bab8348 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -1,5 +1,6 @@ Feature: Editor - Check basic functionalities + Check after save + Check after save html Scenario: Given I am on "index.php" From d4eba66f92b70e59537254fb7e4a33e18bef05f5 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 08:57:20 +0100 Subject: [PATCH 20/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index bab8348..c847d22 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -13,6 +13,6 @@ Feature: Editor Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body-editor" with text "editor content" - When I click first element using class "trumbowyg-viewHTML-button" + When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with text "editor content" in tag "p" \ No newline at end of file From 176ccc6af3c0c6dca9320933a0d5b52a9441fdaf Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 09:06:03 +0100 Subject: [PATCH 21/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index c847d22..fc3abe1 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -15,4 +15,4 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "editor content" in tag "p" \ No newline at end of file + Then Modal is open with text "<p>editor content</p>" in tag "*" \ No newline at end of file From 92e2932e01901568c02be5b1167dce0bb9a3b797 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 09:23:13 +0100 Subject: [PATCH 22/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index fc3abe1..4886dda 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -15,4 +15,4 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "<p>editor content</p>" in tag "*" \ No newline at end of file + Then Modal is open with text "body :

editor content

" in tag "*" \ No newline at end of file From f997475cb9563ec4f13409975c7d399ca1b34bc3 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 09:28:05 +0100 Subject: [PATCH 23/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 4886dda..e9fc7cc 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -15,4 +15,4 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "body :

editor content

" in tag "*" \ No newline at end of file + Then Modal is open with text "body : <p>editor content</p>" in tag "*" \ No newline at end of file From 055d5814de2bd9da55db167539bd27adc926d4f3 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 11:39:51 +0100 Subject: [PATCH 24/52] Fix Behat test 8.1 --- tests-behat/Context.php | 34 ++++++++++++++++++++++++++++++---- tests-behat/editor.feature | 28 ++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index aa241ef..c02d484 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -13,11 +13,37 @@ public function iTypeInEditor(string $id, string $text): void { $session = $this->getSession(); $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - //$element = $session->getPage()->find('css','div[id="'.$id.'"]'); - // get focus - $script = "$('#" . $id . "')[0].innerHTML = '" . $text . "';"; - $session->executeScript($script); + $session->executeScript("$('#" . $id . "').innerHTML = '" . $text . "';"); $session->executeScript("$('#" . $id . "').trigger('keyup')"); } + + /** + * @Then Modal is open with raw text :arg1 + * @Then Modal is open with raw text :arg1 in tag :arg2 + * + * Check if text is present in modal or dynamic modal. + */ + public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void + { + $modal = $this->getElementInPage('.modal.visible.active.front'); + $this->getElementInElement($modal, '//' . $tag . '[text()["' . $text . '"]]', 'xpath'); + } + + /** + * @Then I check if editor :selector value is equal to :text + */ + public function iCheckIfEditorValueMatchesText(string $selector, string $text): void + { + $element = $this->getElementInPage($selector); + $value = trim($element->getValue()); + + if (empty($value)) { + throw new \Exception('Editor value is empty'); + } + + if ($value !== $text) { + throw new \Exception('Text is not matching'); + } + } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index e9fc7cc..934645f 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -1,7 +1,11 @@ Feature: Editor - Check after save - Check after save html - + + Scenario: + Given I am on "index.php" + When I fill in "subject" with "the subject" + When I type in editor "body-editor" with text "editor content" + Then I check if editor "body-editor" value is equal to "

editor content

" + Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" @@ -15,4 +19,20 @@ Feature: Editor When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with text "body : <p>editor content</p>" in tag "*" \ No newline at end of file + Then Modal is open with raw text "body : <p>editor content</p>" in tag "*" + + Scenario: + Given I am on "index.php" + When I fill in "subject" with "the subject" + When I type in editor "body-editor" with text "editor content" + When I click first element using class ".trumbowyg-viewHTML-button" + When I press button "Save" + Then Modal is open with raw text "body :

editor content

" in tag "*" + + Scenario: + Given I am on "index.php" + When I fill in "subject" with "the subject" + When I type in editor "body-editor" with text "editor content" + When I click first element using class ".trumbowyg-viewHTML-button" + When I press button "Save" + Then Modal is open with raw text "body :

editor content

" in tag "p" \ No newline at end of file From 2ff699b04686b7b32c27aca500dd4768b2ebb0f6 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 11:58:29 +0100 Subject: [PATCH 25/52] Fix Behat test 8.1 --- tests-behat/Context.php | 6 +++--- tests-behat/editor.feature | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index c02d484..aba7757 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -9,13 +9,13 @@ class Context extends \Atk4\Ui\Behat\Context /** * @When I type in editor :id with text :text */ - public function iTypeInEditor(string $id, string $text): void + public function iTypeInEditor(string $selector, string $text): void { $session = $this->getSession(); $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - $session->executeScript("$('#" . $id . "').innerHTML = '" . $text . "';"); - $session->executeScript("$('#" . $id . "').trigger('keyup')"); + $session->executeScript("$('" . $selector . "').innerHTML = '" . $text . "';"); + $session->executeScript("$('" . $selector . "').trigger('keyup')"); } /** diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 934645f..b14430f 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -1,22 +1,23 @@ Feature: Editor - + Testing basic functionalities + Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" - Then I check if editor "body-editor" value is equal to "

editor content

" + When I type in editor "#body-editor" with text "editor content" + Then I check if editor "#body-editor" value is equal to "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "#body-editor" with text "editor content" When I press button "Save" Then Modal is open with text "body : editor content" in tag "*" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "#body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body : <p>editor content</p>" in tag "*" @@ -24,7 +25,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "#body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "*" @@ -32,7 +33,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "#body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "p" \ No newline at end of file From a23509f8d4d22a427e44166f895979523a0bf9c6 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 12:07:20 +0100 Subject: [PATCH 26/52] Fix Behat test 8.1 --- tests-behat/Context.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index aba7757..ee72e80 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -35,8 +35,9 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function iCheckIfEditorValueMatchesText(string $selector, string $text): void { - $element = $this->getElementInPage($selector); - $value = trim($element->getValue()); + $this->getElementInPage($selector); // exception if not found + + $value = $this->getSession()->evaluateScript("return $('" . $selector . "]').val()"); if (empty($value)) { throw new \Exception('Editor value is empty'); From 3b7032ea4f985ca7dffcab6790da5846914a8fde Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 12:20:25 +0100 Subject: [PATCH 27/52] Fix Behat test 8.1 --- tests-behat/Context.php | 16 +++++++++------- tests-behat/editor.feature | 12 ++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index ee72e80..9b0e458 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -9,13 +9,13 @@ class Context extends \Atk4\Ui\Behat\Context /** * @When I type in editor :id with text :text */ - public function iTypeInEditor(string $selector, string $text): void + public function iTypeInEditor(string $id, string $text): void { $session = $this->getSession(); $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - $session->executeScript("$('" . $selector . "').innerHTML = '" . $text . "';"); - $session->executeScript("$('" . $selector . "').trigger('keyup')"); + $session->executeScript("$('#" . $id . "').innerHTML = '" . $text . "';"); + $session->executeScript("$('#" . $id . "').trigger('keyup')"); } /** @@ -31,13 +31,15 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void } /** - * @Then I check if editor :selector value is equal to :text + * @Then I check if editor :id value is equal to :text */ - public function iCheckIfEditorValueMatchesText(string $selector, string $text): void + public function iCheckIfEditorValueMatchesText(string $id, string $text): void { - $this->getElementInPage($selector); // exception if not found + if ($this->getSession()->getPage()->findById($id) === null) { + throw new \Exception('Could not get element with id : ' . $id); + } - $value = $this->getSession()->evaluateScript("return $('" . $selector . "]').val()"); + $value = $this->getSession()->evaluateScript("return $('#" . $id . "]').val()"); if (empty($value)) { throw new \Exception('Editor value is empty'); diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index b14430f..33ab07f 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -4,20 +4,20 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "#body-editor" with text "editor content" - Then I check if editor "#body-editor" value is equal to "

editor content

" + When I type in editor "body-editor" with text "editor content" + Then I check if editor "body-editor" value is equal to "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "#body-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I press button "Save" Then Modal is open with text "body : editor content" in tag "*" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "#body-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body : <p>editor content</p>" in tag "*" @@ -25,7 +25,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "#body-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "*" @@ -33,7 +33,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "#body-editor" with text "editor content" + When I type in editor "body-editor" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "p" \ No newline at end of file From 1cc0b585ed0300bb66fa8e49b66e805bd04ce836 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 12:25:17 +0100 Subject: [PATCH 28/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 9b0e458..ba404a1 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -39,7 +39,7 @@ public function iCheckIfEditorValueMatchesText(string $id, string $text): void throw new \Exception('Could not get element with id : ' . $id); } - $value = $this->getSession()->evaluateScript("return $('#" . $id . "]').val()"); + $value = $this->getSession()->evaluateScript("return $('#" . $id . "').val()"); if (empty($value)) { throw new \Exception('Editor value is empty'); From 169544a840cac635205d01f625742d8968560e23 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 12:32:30 +0100 Subject: [PATCH 29/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index ba404a1..7759c40 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -39,7 +39,7 @@ public function iCheckIfEditorValueMatchesText(string $id, string $text): void throw new \Exception('Could not get element with id : ' . $id); } - $value = $this->getSession()->evaluateScript("return $('#" . $id . "').val()"); + $value = $this->getSession()->evaluateScript("return $('#" . $id . "').value"); if (empty($value)) { throw new \Exception('Editor value is empty'); From d0d60a3c7991f29c19da043480c239359b401e72 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:09:57 +0100 Subject: [PATCH 30/52] Fix Behat test 8.1 --- tests-behat/Context.php | 14 ++++++-------- tests-behat/editor.feature | 12 ++++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 7759c40..8e90d81 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -14,8 +14,8 @@ public function iTypeInEditor(string $id, string $text): void $session = $this->getSession(); $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - $session->executeScript("$('#" . $id . "').innerHTML = '" . $text . "';"); - $session->executeScript("$('#" . $id . "').trigger('keyup')"); + $session->executeScript("$('#" . $id . "-editor').innerHTML = '" . $text . "';"); + $session->executeScript("$('#" . $id . "-editor').trigger('keyup')"); } /** @@ -33,20 +33,18 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void /** * @Then I check if editor :id value is equal to :text */ - public function iCheckIfEditorValueMatchesText(string $id, string $text): void + public function iCheckIfEditorValueMatchesText(string $name, string $text): void { - if ($this->getSession()->getPage()->findById($id) === null) { - throw new \Exception('Could not get element with id : ' . $id); - } + $this->assertSession()->fieldExists($name); - $value = $this->getSession()->evaluateScript("return $('#" . $id . "').value"); + $value = $this->getSession()->evaluateScript("return $('textarea[name=\"" . $name . "\"]').value"); if (empty($value)) { throw new \Exception('Editor value is empty'); } if ($value !== $text) { - throw new \Exception('Text is not matching'); + throw new \Exception('Text is not matching, found : ' . $value); } } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 33ab07f..5d725c7 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -4,20 +4,20 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" - Then I check if editor "body-editor" value is equal to "

editor content

" + When I type in editor "body" with text "editor content" + Then I check if editor "body" value is equal to "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "body" with text "editor content" When I press button "Save" Then Modal is open with text "body : editor content" in tag "*" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body : <p>editor content</p>" in tag "*" @@ -25,7 +25,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "*" @@ -33,7 +33,7 @@ Feature: Editor Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" - When I type in editor "body-editor" with text "editor content" + When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then Modal is open with raw text "body :

editor content

" in tag "p" \ No newline at end of file From d410cfa0bc3c740105104c2f423b2da72d7a0f0d Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:22:46 +0100 Subject: [PATCH 31/52] Fix Behat test 8.1 --- tests-behat/Context.php | 18 ------------------ tests-behat/editor.feature | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 8e90d81..d5c5f31 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -29,22 +29,4 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void $modal = $this->getElementInPage('.modal.visible.active.front'); $this->getElementInElement($modal, '//' . $tag . '[text()["' . $text . '"]]', 'xpath'); } - - /** - * @Then I check if editor :id value is equal to :text - */ - public function iCheckIfEditorValueMatchesText(string $name, string $text): void - { - $this->assertSession()->fieldExists($name); - - $value = $this->getSession()->evaluateScript("return $('textarea[name=\"" . $name . "\"]').value"); - - if (empty($value)) { - throw new \Exception('Editor value is empty'); - } - - if ($value !== $text) { - throw new \Exception('Text is not matching, found : ' . $value); - } - } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 5d725c7..ef7c709 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -5,14 +5,14 @@ Feature: Editor Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - Then I check if editor "body" value is equal to "

editor content

" + Then input "body" value should start with "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" When I press button "Save" - Then Modal is open with text "body : editor content" in tag "*" + Then Modal is open with raw text "body : editor content" in tag "*" Scenario: Given I am on "index.php" From a64881d341c5731dfb48c5175cdfbd59b6955921 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:31:55 +0100 Subject: [PATCH 32/52] Fix Behat test 8.1 --- .github/workflows/test-unit.yml | 1 + tests-behat/Context.php | 18 ++++++++++++++++++ tests-behat/editor.feature | 18 +----------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index ff2326b..900c82f 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -2,6 +2,7 @@ name: Unit on: pull_request: + types: [opened, reopened] push: schedule: - cron: '0 0/2 * * *' diff --git a/tests-behat/Context.php b/tests-behat/Context.php index d5c5f31..eab33cd 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -4,6 +4,8 @@ namespace Atk4\TextEditor\Behat; +use Behat\Behat\Tester\Exception\PendingException; + class Context extends \Atk4\Ui\Behat\Context { /** @@ -29,4 +31,20 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void $modal = $this->getElementInPage('.modal.visible.active.front'); $this->getElementInElement($modal, '//' . $tag . '[text()["' . $text . '"]]', 'xpath'); } + + /** + * @Then /^Editor "([^"]*)" value should be equal to "([^"]*)"$/ + */ + public function editorValueShouldBeEqualTo(string $name, string $excepted): void + { + $value = $this->getSession()->evaluateScript('return $(\'textarea[name="' . $name . '"]\').val()'); + + if (empty($value)) { + throw new \Exception('Editor value is empty'); + } + + if ($value !== $excepted) { + throw new \Exception('Editor value not matching : ' . $excepted . ', found :' . $value); + } + } } diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index ef7c709..b0b9347 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -5,7 +5,7 @@ Feature: Editor Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - Then input "body" value should start with "

editor content

" + Then Editor "body" value should be equal to "

editor content

" Scenario: Given I am on "index.php" @@ -14,22 +14,6 @@ Feature: Editor When I press button "Save" Then Modal is open with raw text "body : editor content" in tag "*" - Scenario: - Given I am on "index.php" - When I fill in "subject" with "the subject" - When I type in editor "body" with text "editor content" - When I click first element using class ".trumbowyg-viewHTML-button" - When I press button "Save" - Then Modal is open with raw text "body : <p>editor content</p>" in tag "*" - - Scenario: - Given I am on "index.php" - When I fill in "subject" with "the subject" - When I type in editor "body" with text "editor content" - When I click first element using class ".trumbowyg-viewHTML-button" - When I press button "Save" - Then Modal is open with raw text "body :

editor content

" in tag "*" - Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" From 7c13a8f35145bf801dee2091bc6bec6bd3766f52 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:32:08 +0100 Subject: [PATCH 33/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index eab33cd..12afd13 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -4,8 +4,6 @@ namespace Atk4\TextEditor\Behat; -use Behat\Behat\Tester\Exception\PendingException; - class Context extends \Atk4\Ui\Behat\Context { /** From 69959ea191dbfb72c00654bd9ced78ad40693d92 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:43:23 +0100 Subject: [PATCH 34/52] Fix Behat test 8.1 --- tests-behat/Context.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 12afd13..7506fd1 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -35,7 +35,9 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getSession()->evaluateScript('return $(\'textarea[name="' . $name . '"]\').val()'); + $editor = $this->getSession()->getPage()->find('css', 'textarea[name="' . $name . '"]'); + + $value = $editor->getValue(); if (empty($value)) { throw new \Exception('Editor value is empty'); From 79f4e4c7df56779d06af40b4f5cb526cf8a2f6c5 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:51:26 +0100 Subject: [PATCH 35/52] Fix Behat test 8.1 --- tests-behat/Context.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 7506fd1..12afd13 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -35,9 +35,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $editor = $this->getSession()->getPage()->find('css', 'textarea[name="' . $name . '"]'); - - $value = $editor->getValue(); + $value = $this->getSession()->evaluateScript('return $(\'textarea[name="' . $name . '"]\').val()'); if (empty($value)) { throw new \Exception('Editor value is empty'); From dc37696c25cbf98e168514a0c2550c6d45d5df90 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 13:57:32 +0100 Subject: [PATCH 36/52] Fix Behat test 8.1 --- tests-behat/Context.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 12afd13..2db231f 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -12,7 +12,7 @@ class Context extends \Atk4\Ui\Behat\Context public function iTypeInEditor(string $id, string $text): void { $session = $this->getSession(); - $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); + //$session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); $session->executeScript("$('#" . $id . "-editor').innerHTML = '" . $text . "';"); $session->executeScript("$('#" . $id . "-editor').trigger('keyup')"); @@ -35,7 +35,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getSession()->evaluateScript('return $(\'textarea[name="' . $name . '"]\').val()'); + $value = $this->getSession()->evaluateScript('return $(\'[name="' . $name . '"]\').val()'); if (empty($value)) { throw new \Exception('Editor value is empty'); From 241ea99081c28817fbe45bd1275e4013bc201ed7 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 14:03:22 +0100 Subject: [PATCH 37/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 2db231f..dca7958 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -35,7 +35,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getSession()->evaluateScript('return $(\'[name="' . $name . '"]\').val()'); + $value = $this->getSession()->evaluateScript('return document.getElementsByName(\'' . $name . '\')[0].value'); if (empty($value)) { throw new \Exception('Editor value is empty'); From 261d7261358307137d96a0af80a5304aa148c65b Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 14:21:24 +0100 Subject: [PATCH 38/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- tests-behat/editor.feature | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index dca7958..d65a089 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -35,7 +35,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getSession()->evaluateScript('return document.getElementsByName(\'' . $name . '\')[0].value'); + $value = $this->getElementInPage("textarea[name='" . $name . "']")->getValue(); if (empty($value)) { throw new \Exception('Editor value is empty'); diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index b0b9347..f8b48db 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -6,13 +6,28 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" Then Editor "body" value should be equal to "

editor content

" - + + Scenario: + Given I am on "index.php" + When I fill in "subject" with "the subject" + When I type in editor "body" with text "editor content" + When I press button "Save" + Then Modal is open with text "editor content" in tag "*" + Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" When I press button "Save" - Then Modal is open with raw text "body : editor content" in tag "*" + Then Modal is open with text "editor error content" in tag "*" + + Scenario: + Given I am on "index.php" + When I fill in "subject" with "the subject" + When I type in editor "body" with text "editor content" + When I click first element using class ".trumbowyg-viewHTML-button" + When I press button "Save" + Then Modal is open with raw text "body :

editor content

" in tag "p" Scenario: Given I am on "index.php" @@ -20,4 +35,4 @@ Feature: Editor When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then Modal is open with raw text "body :

editor content

" in tag "p" \ No newline at end of file + Then Modal is open with raw text "body :

editor error content

" in tag "p" \ No newline at end of file From 998ac990e20d101cb6a27bb89e024707d83406f0 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 14:39:59 +0100 Subject: [PATCH 39/52] Fix Behat test 8.1 --- tests-behat/Context.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index d65a089..c87d702 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -26,8 +26,15 @@ public function iTypeInEditor(string $id, string $text): void */ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void { - $modal = $this->getElementInPage('.modal.visible.active.front'); - $this->getElementInElement($modal, '//' . $tag . '[text()["' . $text . '"]]', 'xpath'); + $html = $this->getElementInPage('.modal.visible.active.front')->getHtml(); + + if (empty($html)) { + throw new \Exception('Modal html is empty'); + } + + if (strpos($html, $text) === false) { + throw new \Exception('Text not found'); + } } /** @@ -35,7 +42,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getElementInPage("textarea[name='" . $name . "']")->getValue(); + $value = $this->getElementInPage('textarea[name="' . $name . '"]')->getValue(); if (empty($value)) { throw new \Exception('Editor value is empty'); From 369cc117a9f624bbdf51b086bc84e7b4436d0d54 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 14:47:52 +0100 Subject: [PATCH 40/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index f8b48db..6e8b7f0 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -12,6 +12,7 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" When I press button "Save" + Then I wait 1000 ms Then Modal is open with text "editor content" in tag "*" Scenario: @@ -19,6 +20,7 @@ Feature: Editor When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" When I press button "Save" + Then I wait 1000 ms Then Modal is open with text "editor error content" in tag "*" Scenario: @@ -27,6 +29,7 @@ Feature: Editor When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" + Then I wait 1000 ms Then Modal is open with raw text "body :

editor content

" in tag "p" Scenario: @@ -35,4 +38,5 @@ Feature: Editor When I type in editor "body" with text "editor content" When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" + Then I wait 1000 ms Then Modal is open with raw text "body :

editor error content

" in tag "p" \ No newline at end of file From c179d4eadefd33558239997d55ac87a8cefec9fb Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 14:56:31 +0100 Subject: [PATCH 41/52] Fix Behat test 8.1 --- tests-behat/editor.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 6e8b7f0..64fd1df 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -21,7 +21,7 @@ Feature: Editor When I type in editor "body" with text "editor content" When I press button "Save" Then I wait 1000 ms - Then Modal is open with text "editor error content" in tag "*" + Then Modal is open with text "body : <p>editor content</p>" in tag "*" Scenario: Given I am on "index.php" @@ -39,4 +39,4 @@ Feature: Editor When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" Then I wait 1000 ms - Then Modal is open with raw text "body :

editor error content

" in tag "p" \ No newline at end of file + Then Modal is open with raw text "body : <p>editor content</p>" in tag "p" \ No newline at end of file From b8351781f72b9966dd884d7021e81103cf8adcc8 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 15:01:00 +0100 Subject: [PATCH 42/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index c87d702..abe8c93 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -12,7 +12,7 @@ class Context extends \Atk4\Ui\Behat\Context public function iTypeInEditor(string $id, string $text): void { $session = $this->getSession(); - //$session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); + $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); $session->executeScript("$('#" . $id . "-editor').innerHTML = '" . $text . "';"); $session->executeScript("$('#" . $id . "-editor').trigger('keyup')"); From d25507ca5ab5d43cc07624dd349cc1284d633eac Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 15:33:16 +0100 Subject: [PATCH 43/52] Fix Behat test 8.1 --- tests-behat/Context.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index abe8c93..1e197aa 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -33,7 +33,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void } if (strpos($html, $text) === false) { - throw new \Exception('Text not found'); + throw new \Exception('Text not found, found : ' . $html); } } From 8159bdd22ccb08b7dcdf5bdb98f2c6276daa6cba Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 15:34:51 +0100 Subject: [PATCH 44/52] Fix Behat test 8.1 --- template/trumbowyg.html | 3 ++- template/trumbowyg.pug | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/template/trumbowyg.html b/template/trumbowyg.html index 9b8ff41..a84f74d 100644 --- a/template/trumbowyg.html +++ b/template/trumbowyg.html @@ -1,3 +1,4 @@ +
{$BeforeInput}{$AfterBeforeInput}{Input}{/}{$AfterInput}{$AfterAfterInput} -
\ No newline at end of file + diff --git a/template/trumbowyg.pug b/template/trumbowyg.pug index 819c65e..84d8f09 100644 --- a/template/trumbowyg.pug +++ b/template/trumbowyg.pug @@ -1,3 +1,3 @@ |
| {$BeforeInput}{$AfterBeforeInput}{Input}{/}{$AfterInput}{$AfterAfterInput} -|
\ No newline at end of file +| From a63678413639d6cda64e6212f8cca997b5c56c39 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 15:47:45 +0100 Subject: [PATCH 45/52] Fix Behat test 8.1 --- tests-behat/Context.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 1e197aa..9c42e67 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -12,10 +12,8 @@ class Context extends \Atk4\Ui\Behat\Context public function iTypeInEditor(string $id, string $text): void { $session = $this->getSession(); - $session->evaluateScript('$.fn.extend({backspace:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):(n.callback.call(t),$(t).dequeue())},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},a(e)})})},typetype:function(e,t){var n;return n=$.extend({callback:function(){},keypress:function(){},t:100,e:.04},t),this.each(function(){var t;t=this,$(t).queue(function(){var i,a,c;a=function(e,i){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]=t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"].slice(0,-1),n.keypress.call(t),setTimeout(function(){a(e-1,i)},n.t)):i()},i=function(e,a){e?(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[0],n.keypress.call(t),setTimeout(function(){i(e.slice(1),a)},n.t)):a()},(c=function(u){var s,l;s=function(){return setTimeout(function(){c(u)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))},l=Math.random()/n.e,e.length>=u?.3>l&&e[u-1]!==e[u]&&e.length>u+4?i(e.slice(u,u+4),function(){a(4,s)}):.7>l&&u>1&&/[A-Z]/.test(e[u-2]&&e.length>u+4)?i(e[u-1].toUpperCase()+e.slice(u,u+4),function(){a(5,s)}):.5>l&&e[u-1]!==e[u]&&e.length>u?i(e[u],function(){a(1,s)}):1>l&&e[u-1]!==e[u]&&e.length>u?i(e[u]+e[u-1],function(){a(2,s)}):.5>l&&/[A-Z]/.test(e[u])?i(e[u].toLowerCase(),function(){a(1,s)}):(t[/(np|x)/i.test(t.tagName)?"value":"innerHTML"]+=e[u-1],n.keypress.call(t),setTimeout(function(){c(u+1)},Math.random()*n.t*(e[u-1]===e[u]?1.6:"."===e[u-1]?12:"!"===e[u-1]?12:"?"===e[u-1]?12:"\n"===e[u-1]?12:","===e[u-1]?8:";"===e[u-1]?8:":"===e[u-1]?8:" "===e[u-1]?3:2))):(n.callback.call(t),$(t).dequeue())})(1)})})}});'); - $session->executeScript("$('#" . $id . "-editor').innerHTML = '" . $text . "';"); - $session->executeScript("$('#" . $id . "-editor').trigger('keyup')"); + $session->executeScript("$('#" . $id . "-editor').html('" . $text . "')"); } /** From ce0605565a95588e6453d52b78b59741bbd6dc02 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 15:48:15 +0100 Subject: [PATCH 46/52] Fix Behat test 8.1 --- tests-behat/Context.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 9c42e67..99924ff 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -11,9 +11,7 @@ class Context extends \Atk4\Ui\Behat\Context */ public function iTypeInEditor(string $id, string $text): void { - $session = $this->getSession(); - - $session->executeScript("$('#" . $id . "-editor').html('" . $text . "')"); + $this->getSession()->executeScript("$('#" . $id . "-editor').html('" . $text . "')"); } /** From a558b4d13afc890c39320743f42880d9e05d5b03 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 20:18:32 +0100 Subject: [PATCH 47/52] Fix Behat test 8.1 --- bower.json | 2 +- demos/index.php | 25 +++++++++++++++++++++++++ src/TextEditor.php | 32 +++++++++++++++++++++++++++----- tests-behat/Context.php | 8 ++++---- tests-behat/editor.feature | 20 ++++++++------------ 5 files changed, 65 insertions(+), 22 deletions(-) diff --git a/bower.json b/bower.json index 1c1ba86..6f052d0 100644 --- a/bower.json +++ b/bower.json @@ -2,6 +2,6 @@ "name": "atk4-trumbowyg", "version": "1.0.0", "dependencies": { - "trumbowyg": "^2.25.1" + "trumbowyg": "2.25.1" } } diff --git a/demos/index.php b/demos/index.php index 588f48b..2dc5fae 100644 --- a/demos/index.php +++ b/demos/index.php @@ -5,6 +5,9 @@ namespace Atk4\TextEditor\Demos; use Atk4\TextEditor\Demos\Model\Post; +use Atk4\TextEditor\TextEditor; +use Atk4\Ui\Button; +use Atk4\Ui\Form\Control\Input; use Atk4\Ui\Layout\Centered; date_default_timezone_set('UTC'); @@ -34,4 +37,26 @@ return $view; }); +/** @var Input $input */ +$input = $form->getControl('subject'); + +/** @var TextEditor $editor */ +$editor = $form->getControl('body'); + +Button::addTo($app, ['set editor content with random value'])->on('click', function ($jq) use ($editor) { + return $editor->jsSetHtml(true, (string) random_int(0, 10000)); +}); + +Button::addTo($app, ['get editor content'])->on('click', function ($jq, $content) { + return $content; +}, [$editor->jsGetHtml()]); + +Button::addTo($app, ['refresh editor'])->on('click', function ($jq) use ($editor) { + return $editor->jsReload(); +}); + +Button::addTo($app, ['refresh input'])->on('click', function ($jq) use ($input) { + return $input->jsReload(); +}); + $app->run(); diff --git a/src/TextEditor.php b/src/TextEditor.php index 0c32e9b..bae7f78 100644 --- a/src/TextEditor.php +++ b/src/TextEditor.php @@ -5,6 +5,7 @@ namespace Atk4\TextEditor; use Atk4\Ui\Form\Control\Textarea; +use Atk4\Ui\Jquery; class TextEditor extends Textarea { @@ -13,7 +14,7 @@ class TextEditor extends Textarea public $defaultTemplate = __DIR__ . '/../template/trumbowyg.html'; //public $assets_path = '/assets'; - public string $assets_path = 'https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.20.0'; + public string $assets_path = 'https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.25.1'; public bool $option_resetCss = true; public bool $option_autogrow = true; public array $editor_options = [ @@ -44,23 +45,44 @@ class TextEditor extends Textarea '/ui/trumbowyg.css', ]; - protected function init(): void + /** + * Render view. + */ + protected function renderView(): void { - parent::init(); + parent::renderView(); $this->addRequiredAssets(); foreach ($this->plugins as $plugin) { $this->addRequiredPlugin($plugin); } - //$this->setStyle('display','block'); $this->editor_options['resetCss'] = $this->option_resetCss; $this->editor_options['autogrow'] = $this->option_autogrow; $jsInput = $this->jsInput(true); $jsInput->trumbowyg($this->editor_options); // @phpstan-ignore-line - $jsInput->parent()->find('.trumbowyg-editor')->attr('id', $this->short_name . '-editor'); + } + + /** + * Will return jQuery expression to set editor html content. + * + * @param string|bool|null $when Event when chain will be executed + */ + public function jsSetHtml($when = null, string $html = ''): Jquery + { + return $this->jsInput($when)->trumbowyg('html', $html); // @phpstan-ignore-line + } + + /** + * Will return jQuery expression to get editor html content. + * + * @param string|bool|null $when Event when chain will be executed + */ + public function jsGetHtml($when = null): Jquery + { + return $this->jsInput($when)->trumbowyg('html'); // @phpstan-ignore-line } private function addRequiredAssets(): void diff --git a/tests-behat/Context.php b/tests-behat/Context.php index 99924ff..1072171 100644 --- a/tests-behat/Context.php +++ b/tests-behat/Context.php @@ -7,11 +7,11 @@ class Context extends \Atk4\Ui\Behat\Context { /** - * @When I type in editor :id with text :text + * @When I type in editor :name with text :text */ - public function iTypeInEditor(string $id, string $text): void + public function iTypeInEditor(string $name, string $text): void { - $this->getSession()->executeScript("$('#" . $id . "-editor').html('" . $text . "')"); + $this->getSession()->executeScript("$('textarea[name=" . $name . "]').trumbowyg('html', '" . $text . "')"); } /** @@ -38,7 +38,7 @@ public function modalIsOpenWithRawText(string $text, string $tag = 'div'): void */ public function editorValueShouldBeEqualTo(string $name, string $excepted): void { - $value = $this->getElementInPage('textarea[name="' . $name . '"]')->getValue(); + $value = $this->getSession()->evaluateScript("$('textarea[name=" . $name . "]').trumbowyg('html')"); if (empty($value)) { throw new \Exception('Editor value is empty'); diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 64fd1df..6bde4d5 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -5,38 +5,34 @@ Feature: Editor Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - Then Editor "body" value should be equal to "

editor content

" + Then Editor "body" value should be equal to "editor content" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - When I press button "Save" - Then I wait 1000 ms - Then Modal is open with text "editor content" in tag "*" + When I click icon using css ".trumbowyg-viewHTML-button" + Then Editor "body" value should be equal to "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - When I press button "Save" - Then I wait 1000 ms - Then Modal is open with text "body : <p>editor content</p>" in tag "*" + When I click icon using css ".trumbowyg-viewHTML-button" + When I click icon using css ".trumbowyg-viewHTML-button" + Then Editor "body" value should be equal to "

editor content

" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - When I click first element using class ".trumbowyg-viewHTML-button" When I press button "Save" - Then I wait 1000 ms - Then Modal is open with raw text "body :

editor content

" in tag "p" + Then Modal is open with raw text "body : editor content" in tag "p" Scenario: Given I am on "index.php" When I fill in "subject" with "the subject" When I type in editor "body" with text "editor content" - When I click first element using class ".trumbowyg-viewHTML-button" + When I click icon using css ".trumbowyg-viewHTML-button" When I press button "Save" - Then I wait 1000 ms Then Modal is open with raw text "body : <p>editor content</p>" in tag "p" \ No newline at end of file From 24f89fddbd6aeca2f5072e20c212a31b2a8f10b5 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 21:44:55 +0100 Subject: [PATCH 48/52] Fix Behat test 8.1 --- src/TextEditor.php | 2 -- template/trumbowyg.html | 4 ---- template/trumbowyg.pug | 3 --- tests/TextEditorTest.php | 6 ++++-- 4 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 template/trumbowyg.html delete mode 100644 template/trumbowyg.pug diff --git a/src/TextEditor.php b/src/TextEditor.php index bae7f78..3706084 100644 --- a/src/TextEditor.php +++ b/src/TextEditor.php @@ -11,8 +11,6 @@ class TextEditor extends Textarea { protected static array $loaded_assets = []; - public $defaultTemplate = __DIR__ . '/../template/trumbowyg.html'; - //public $assets_path = '/assets'; public string $assets_path = 'https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.25.1'; public bool $option_resetCss = true; diff --git a/template/trumbowyg.html b/template/trumbowyg.html deleted file mode 100644 index a84f74d..0000000 --- a/template/trumbowyg.html +++ /dev/null @@ -1,4 +0,0 @@ - -
-{$BeforeInput}{$AfterBeforeInput}{Input}{/}{$AfterInput}{$AfterAfterInput} -
diff --git a/template/trumbowyg.pug b/template/trumbowyg.pug deleted file mode 100644 index 84d8f09..0000000 --- a/template/trumbowyg.pug +++ /dev/null @@ -1,3 +0,0 @@ -|
-| {$BeforeInput}{$AfterBeforeInput}{Input}{/}{$AfterInput}{$AfterAfterInput} -|
diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index 4419229..07f9630 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -39,7 +39,8 @@ public function testInit(): void $output = ob_get_clean(); } - $this->assertSame(1, substr_count($output, '/libs/Trumbowyg/2.20.0/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/ui/trumbowyg.css')); } public function testCheckDouble(): void @@ -65,7 +66,8 @@ public function testCheckDouble(): void $output = ob_get_clean(); } - $this->assertSame(1, substr_count($output, '/libs/Trumbowyg/2.20.0/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/ui/trumbowyg.css')); } public function testPlugin(): void From 43eb00b45ddd893b3e94325cfc358833c3c46c7a Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Sun, 30 Jan 2022 21:46:43 +0100 Subject: [PATCH 49/52] Fix Behat test 8.1 --- tests/TextEditorTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/TextEditorTest.php b/tests/TextEditorTest.php index 07f9630..0e599c4 100644 --- a/tests/TextEditorTest.php +++ b/tests/TextEditorTest.php @@ -39,8 +39,8 @@ public function testInit(): void $output = ob_get_clean(); } - $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/trumbowyg.js')); - $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/ui/trumbowyg.css')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path . '/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path . '/ui/trumbowyg.css')); } public function testCheckDouble(): void @@ -66,8 +66,8 @@ public function testCheckDouble(): void $output = ob_get_clean(); } - $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/trumbowyg.js')); - $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path.'/ui/trumbowyg.css')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path . '/trumbowyg.js')); + $this->assertSame(1, substr_count($output, (new TextEditor())->assets_path . '/ui/trumbowyg.css')); } public function testPlugin(): void From 4fad74572229cb3a2c4512af44edafbe2c0d6d56 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Tue, 22 Feb 2022 08:19:05 +0100 Subject: [PATCH 50/52] Removed test where firefox fails --- tests-behat/editor.feature | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tests-behat/editor.feature b/tests-behat/editor.feature index 6bde4d5..ae461c0 100644 --- a/tests-behat/editor.feature +++ b/tests-behat/editor.feature @@ -7,20 +7,22 @@ Feature: Editor When I type in editor "body" with text "editor content" Then Editor "body" value should be equal to "editor content" - Scenario: - Given I am on "index.php" - When I fill in "subject" with "the subject" - When I type in editor "body" with text "editor content" - When I click icon using css ".trumbowyg-viewHTML-button" - Then Editor "body" value should be equal to "

editor content

" +# Firefox fails : https://github.com/Alex-D/Trumbowyg/issues/999 +# Scenario: +# Given I am on "index.php" +# When I fill in "subject" with "the subject" +# When I type in editor "body" with text "editor content" +# When I click icon using css ".trumbowyg-viewHTML-button" +# Then Editor "body" value should be equal to "

editor content

" - Scenario: - Given I am on "index.php" - When I fill in "subject" with "the subject" - When I type in editor "body" with text "editor content" - When I click icon using css ".trumbowyg-viewHTML-button" - When I click icon using css ".trumbowyg-viewHTML-button" - Then Editor "body" value should be equal to "

editor content

" +# Firefox fails : https://github.com/Alex-D/Trumbowyg/issues/999 +# Scenario: +# Given I am on "index.php" +# When I fill in "subject" with "the subject" +# When I type in editor "body" with text "editor content" +# When I click icon using css ".trumbowyg-viewHTML-button" +# When I click icon using css ".trumbowyg-viewHTML-button" +# Then Editor "body" value should be equal to "

editor content

" Scenario: Given I am on "index.php" From 8d1efbfc7d760cba0c4c7efe8e298d60d5ddeeb1 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Thu, 24 Feb 2022 08:36:41 +0100 Subject: [PATCH 51/52] Fix MSSQL tests --- .github/workflows/test-unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 900c82f..9ec6aca 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -189,7 +189,7 @@ jobs: - name: "Run tests: MSSQL" env: - DB_DSN: "sqlsrv:host=mssql;dbname=master" + DB_DSN: "sqlsrv:host=mssql;dbname=master;driverOptions[TrustServerCertificate]=1" DB_USER: sa DB_PASSWORD: atk4_pass run: | From 3038709783897a4188e37cd6c861053f2d01fd55 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Thu, 24 Feb 2022 09:37:11 +0100 Subject: [PATCH 52/52] Fix composer normalize --- composer.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 3756a9c..d416c51 100644 --- a/composer.json +++ b/composer.json @@ -42,14 +42,15 @@ }, "autoload-dev": { "psr-4": { - "Atk4\\TextEditor\\Tests\\": "tests/", - "Atk4\\TextEditor\\Behat\\": "tests-behat/" + "Atk4\\TextEditor\\Behat\\": "tests-behat/", + "Atk4\\TextEditor\\Tests\\": "tests/" } }, "config": { - "sort-packages": true, "allow-plugins": { - "ergebnis/composer-normalize": true - } + "ergebnis/composer-normalize": true, + "phpstan/extension-installer": true + }, + "sort-packages": true } }