diff --git a/.github/actions/download-wordpress/action.yml b/.github/actions/download-wordpress/action.yml new file mode 100644 index 000000000..470916e58 --- /dev/null +++ b/.github/actions/download-wordpress/action.yml @@ -0,0 +1,28 @@ +name: 'Download and Extract WordPress' +description: > + Downloads WordPress from wordpress.org, extracts it into the target + directory, flattens the contents, and cleans up the archive. + +inputs: + wp-version: + description: 'WordPress version to download (e.g. "latest" or "6.2.8").' + required: true + target-dir: + description: 'Directory to extract WordPress into. Will be created and chowned to the runner.' + required: false + default: '/var/www/html' + +runs: + using: 'composite' + steps: + - name: Download and Extract WordPress + shell: bash + run: | + sudo mkdir -p ${{ inputs.target-dir }} + sudo chown -R runner:docker ${{ inputs.target-dir }} + ls -la ${{ inputs.target-dir }} + cd ${{ inputs.target-dir }} + wget https://wordpress.org/wordpress-${{ inputs.wp-version }}.tar.gz + tar xfz wordpress-${{ inputs.wp-version }}.tar.gz + mv wordpress/* . + rm -rf wordpress wordpress-${{ inputs.wp-version }}.tar.gz diff --git a/.github/actions/install-wordpress/action.yml b/.github/actions/install-wordpress/action.yml new file mode 100644 index 000000000..3ed149199 --- /dev/null +++ b/.github/actions/install-wordpress/action.yml @@ -0,0 +1,67 @@ +name: 'Install WordPress via WP-CLI' +description: > + Installs WP-CLI, generates wp-config.php, and runs the WordPress core + installer. Assumes WordPress core files have already been downloaded and + extracted into the working directory. + +inputs: + root-dir: + description: 'Path to the WordPress root (where wp-config.php will live).' + required: true + db-name: + description: 'Database name.' + required: false + default: 'test' + db-user: + description: 'MySQL user.' + required: false + default: 'root' + db-pass: + description: 'MySQL password.' + required: false + default: 'root' + db-host: + description: 'MySQL host.' + required: false + default: 'localhost' + site-url: + description: 'WordPress site URL passed to wp-cli core install.' + required: false + default: '127.0.0.1' + site-title: + description: 'WordPress site title.' + required: false + default: 'ConvertKit' + admin-user: + description: 'WordPress admin username.' + required: false + default: 'admin' + admin-password: + description: 'WordPress admin password.' + required: false + default: 'password' + admin-email: + description: 'WordPress admin email.' + required: false + default: 'wordpress@convertkit.local' + +runs: + using: 'composite' + steps: + # We install WP-CLI, as it provides useful commands to setup and install WordPress through the command line. + - name: Install WP-CLI + shell: bash + run: | + curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar + chmod +x wp-cli.phar + sudo mv wp-cli.phar /usr/local/bin/wp-cli + + - name: Setup wp-config.php + shell: bash + working-directory: ${{ inputs.root-dir }} + run: wp-cli config create --dbname=${{ inputs.db-name }} --dbuser=${{ inputs.db-user }} --dbpass=${{ inputs.db-pass }} --dbhost=${{ inputs.db-host }} --locale=en_DB + + - name: Install WordPress + shell: bash + working-directory: ${{ inputs.root-dir }} + run: wp-cli core install --url=${{ inputs.site-url }} --title=${{ inputs.site-title }} --admin_user=${{ inputs.admin-user }} --admin_password=${{ inputs.admin-password }} --admin_email=${{ inputs.admin-email }} diff --git a/.github/actions/setup-mysql/action.yml b/.github/actions/setup-mysql/action.yml new file mode 100644 index 000000000..c708193b5 --- /dev/null +++ b/.github/actions/setup-mysql/action.yml @@ -0,0 +1,41 @@ +name: 'Set up MySQL' +description: > + Starts MySQL, creates the test database, and switches the user to + mysql_native_password authentication so WordPress can connect on MySQL 8.0. + Used by every workflow that runs WordPress against MySQL. + +inputs: + db-name: + description: 'Database name to create.' + required: false + default: 'test' + db-user: + description: 'MySQL user.' + required: false + default: 'root' + db-pass: + description: 'MySQL password.' + required: false + default: 'root' + db-host: + description: 'MySQL host.' + required: false + default: 'localhost' + +runs: + using: 'composite' + steps: + - name: Start MySQL + shell: bash + run: sudo systemctl start mysql.service + + - name: Create MySQL Database + shell: bash + run: | + mysql -e 'CREATE DATABASE ${{ inputs.db-name }};' -u${{ inputs.db-user }} -p${{ inputs.db-pass }} + mysql -e 'SHOW DATABASES;' -u${{ inputs.db-user }} -p${{ inputs.db-pass }} + + # WordPress won't be able to connect to the DB if we don't perform this step. + - name: Permit MySQL Password Auth for MySQL 8.0 + shell: bash + run: mysql -e "ALTER USER '${{ inputs.db-user }}'@'${{ inputs.db-host }}' IDENTIFIED WITH mysql_native_password BY '${{ inputs.db-pass }}';" -u${{ inputs.db-user }} -p${{ inputs.db-pass }} diff --git a/.github/workflows/_dependabot-metadata.yml b/.github/workflows/_dependabot-metadata.yml new file mode 100644 index 000000000..23d80b429 --- /dev/null +++ b/.github/workflows/_dependabot-metadata.yml @@ -0,0 +1,30 @@ +name: Dependabot Metadata + +# Reusable workflow that fetches Dependabot metadata for the calling workflow. +# Consumed via `uses: ./.github/workflows/_dependabot-metadata.yml`. + +on: + workflow_call: + outputs: + package-ecosystem: + description: 'The Dependabot package ecosystem (e.g. composer, github-actions).' + value: ${{ jobs.dependabot-metadata.outputs.package-ecosystem }} + +jobs: + dependabot-metadata: + name: Dependabot Metadata + + runs-on: ubuntu-latest + + # Only run when the actor is Dependabot. + if: github.actor == 'dependabot[bot]' + + outputs: + package-ecosystem: ${{ steps.metadata.outputs.package-ecosystem }} + + steps: + - name: Fetch Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v3 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/_run-tests.yml b/.github/workflows/_run-tests.yml new file mode 100644 index 000000000..8dc480003 --- /dev/null +++ b/.github/workflows/_run-tests.yml @@ -0,0 +1,390 @@ +name: Run Test Matrix + +# Reusable workflow that runs a single Codeception test group. +# Consumed by tests.yml and tests-backward-compat.yml via `uses:`. +# +# Inputs that vary per workflow are passed in. Everything else (env, steps, +# install logic) is defined here so it lives in one place. + +on: + workflow_call: + inputs: + wp-version: + description: 'WordPress version (e.g. "latest" or "6.2.8").' + required: true + type: string + php-version: + description: 'PHP version (e.g. "8.1").' + required: true + type: string + test-group: + description: 'Test group path (e.g. "EndToEnd/forms/general").' + required: true + type: string + block-editor-v3-enabled: + description: 'Whether to enable WordPress 6.3+ apiVersion 3 blocks (true/false).' + required: true + type: string + db-sql-dump-file: + description: 'Path to the SQL dump file used to populate the test database.' + required: true + type: string + install-plugins: + description: 'Space-separated WordPress.org plugin slugs to install via wp-cli.' + required: false + type: string + default: '' + install-plugins-urls: + description: 'Space-separated plugin zip URLs to install via wp-cli.' + required: false + type: string + default: '' + install-themes-urls: + description: 'Space-separated theme zip URLs to install via wp-cli.' + required: false + type: string + default: '' + +jobs: + tests: + name: PHP ${{ inputs.php-version }} + + runs-on: ubuntu-latest + + env: + ROOT_DIR: /var/www/html + PLUGIN_DIR: /var/www/html/wp-content/plugins/convertkit + CACHE_DIR: /var/www/html/wp-content/plugins/wp-super-cache/ + DB_NAME: test + DB_USER: root + DB_PASS: root + DB_HOST: localhost + WORDPRESS_V3_BLOCK_EDITOR_ENABLED: ${{ inputs.block-editor-v3-enabled }} + WORDPRESS_DB_SQL_DUMP_FILE: ${{ inputs.db-sql-dump-file }} + INSTALL_PLUGINS: ${{ inputs.install-plugins }} + INSTALL_PLUGINS_URLS: ${{ inputs.install-plugins-urls }} + INSTALL_THEMES_URLS: ${{ inputs.install-themes-urls }} + CONVERTKIT_API_KEY: ${{ secrets.CONVERTKIT_API_KEY }} + CONVERTKIT_API_SECRET: ${{ secrets.CONVERTKIT_API_SECRET }} + CONVERTKIT_API_KEY_NO_DATA: ${{ secrets.CONVERTKIT_API_KEY_NO_DATA }} + CONVERTKIT_API_SECRET_NO_DATA: ${{ secrets.CONVERTKIT_API_SECRET_NO_DATA }} + CONVERTKIT_OAUTH_CLIENT_ID: ${{ secrets.CONVERTKIT_OAUTH_CLIENT_ID }} + CONVERTKIT_OAUTH_REDIRECT_URI: ${{ secrets.CONVERTKIT_OAUTH_REDIRECT_URI }} + KIT_OAUTH_REDIRECT_URI: ${{ secrets.KIT_OAUTH_REDIRECT_URI }} + CONVERTKIT_API_SIGNED_SUBSCRIBER_ID: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} + CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} + CONVERTKIT_API_RECAPTCHA_SITE_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} + CONVERTKIT_API_RECAPTCHA_SECRET_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} + + steps: + - name: Define Test Group Name + id: test-group + uses: mad9000/actions-find-and-replace-string@5 + with: + source: ${{ inputs.test-group }} + find: '/' + replace: '-' + replaceAll: true + + # Checkout Plugin to /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit + # We cannot checkout to ${{ env.PLUGIN_DIR }} as GitHub Actions require it be first placed in /home/runner/work/repo/repo + - name: Checkout Plugin + uses: actions/checkout@v6 + with: + path: /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit + + - name: Set up MySQL + uses: ./convertkit/.github/actions/setup-mysql + with: + db-name: ${{ env.DB_NAME }} + db-user: ${{ env.DB_USER }} + db-pass: ${{ env.DB_PASS }} + db-host: ${{ env.DB_HOST }} + + - name: Download and Extract WordPress + uses: ./convertkit/.github/actions/download-wordpress + with: + wp-version: ${{ inputs.wp-version }} + target-dir: ${{ env.ROOT_DIR }} + + - name: Install WordPress via WP-CLI + uses: ./convertkit/.github/actions/install-wordpress + with: + root-dir: ${{ env.ROOT_DIR }} + db-name: ${{ env.DB_NAME }} + db-user: ${{ env.DB_USER }} + db-pass: ${{ env.DB_PASS }} + db-host: ${{ env.DB_HOST }} + + # env.INSTALL_PLUGINS is a list of Plugin slugs, space separated e.g. contact-form-7 woocommerce. + - name: Install Free Third Party WordPress Plugins + if: ${{ env.INSTALL_PLUGINS != '' }} + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli plugin install ${{ env.INSTALL_PLUGINS }} + + # env.INSTALL_PLUGINS_URLS is a list of Plugin URLs, space separated, to install specific versions of third party Plugins. + - name: Install Free Third Party WordPress Specific Version Plugins + if: ${{ env.INSTALL_PLUGINS_URLS != '' }} + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli plugin install ${{ env.INSTALL_PLUGINS_URLS }} + + # env.INSTALL_THEMES_URLS is a list of Theme URLs, space separated, to install specific versions of third party Themes. + - name: Install Free Third Party WordPress Specific Version Themes + if: ${{ env.INSTALL_THEMES_URLS != '' }} + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli theme install ${{ env.INSTALL_THEMES_URLS }} + + # These should be stored as a separated list of URLs in the repository Settings > Secrets > Repository Secret > CONVERTKIT_PAID_PLUGIN_URLS. + # We cannot include the URLs in this file, as they're not Plugins we are permitted to distribute. + # Skipped silently if the secret is not defined (e.g. backward-compat workflow doesn't need paid plugins). + - name: Install Paid Third Party WordPress Plugins + working-directory: ${{ env.ROOT_DIR }} + env: + PAID_URLS: ${{ secrets.CONVERTKIT_PAID_PLUGIN_URLS }} + run: | + if [ -n "$PAID_URLS" ]; then + wp-cli plugin install $PAID_URLS + else + echo "CONVERTKIT_PAID_PLUGIN_URLS secret not set; skipping." + fi + + # Install 2021 Theme, which provides support for widgets. + # 2021 Theme isn't included in WordPress 6.4 or higher, but is required for our widget tests. + - name: Install WordPress Themes + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli theme install twentytwentyone + + # Move Plugin to PLUGIN_DIR. + - name: Move Plugin + run: mv /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit ${{ env.PLUGIN_DIR }} + + # WP_DEBUG = true is required so all PHP errors are output and caught by tests (E_ALL). + # WP_DEBUG = false for other integration tests, as Elementor in PHP 8.4 throws a deprecation notice. + - name: Enable WP_DEBUG + if: ${{ inputs.test-group != 'EndToEnd/integrations/other' && inputs.php-version != '8.4' }} + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli config set WP_DEBUG true --raw + + # FS_METHOD = direct is required for WP_Filesystem to operate without suppressed PHP fopen() errors that trip up tests. + - name: Enable FS_METHOD + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli config set FS_METHOD direct + + # DISALLOW_FILE_MODS = true is required to disable the block directory's "Available to Install" suggestions, which trips up + # tests that search and wait for block results. + # We don't enable DISALLOW_FILE_MODS for the UninstallCest test, as tests will perform a Plugin deletion + # which requires DISALLOW_FILE_MODS to not be true. + - name: Enable DISALLOW_FILE_MODS + if: ${{ inputs.test-group != 'EndToEnd/general/uninstall' }} + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli config set DISALLOW_FILE_MODS true --raw + + # Prevent WordPress auto updating to the latest version, as we specifically want to use a specific version of WordPress. + - name: Disable WP_AUTO_UPDATE_CORE + working-directory: ${{ env.ROOT_DIR }} + run: wp-cli config set WP_AUTO_UPDATE_CORE false --raw + + # This step is deliberately after WordPress installation and configuration, as enabling PHP 8.x before using WP-CLI results + # in the workflow failing due to incompatibilities between WP-CLI and PHP 8.x. + # By installing PHP at this stage, we can still run our tests against e.g. PHP 8.x. + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php-version }} + coverage: xdebug + + # Workaround for PHP 8.3 (and potentially other versions): the GitHub + # Actions ubuntu-latest runner ships with a pre-installed PHP that leaves + # an empty regular file at /run/php/php-fpm.sock. setup-php + # installs a different patch version on top, but PHP-FPM never replaces + # the stale file with a real Unix socket, so nginx returns 502. + # Removing the stale file and restarting PHP-FPM forces a clean socket. + - name: Restart PHP-FPM + run: | + sudo rm -f /run/php/php${{ inputs.php-version }}-fpm.sock + sudo systemctl restart php${{ inputs.php-version }}-fpm + sleep 1 + ls -la /run/php/ + + # Configure nginx to use the PHP version and WordPress installation at /var/www/html + - name: Configure nginx site + run: | + sudo rm -f /etc/nginx/sites-enabled/default + sudo tee /etc/nginx/sites-available/default > /dev/null << 'EOF' + + server { + listen 80 default_server; + listen [::]:80 default_server; + + root /var/www/html; + index index.php; + + server_name localhost; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/run/php/php${{ inputs.php-version }}-fpm.sock; + + # Prevent 502 Bad Gateway error "upstream sent too big header while reading response header from upstream" + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_busy_buffers_size 64k; + } + + location ~ /\.ht { + deny all; + } + } + EOF + + sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default || true + + - name: Test nginx + run: sudo nginx -t + + - name: Start nginx + run: sudo systemctl start nginx.service + + - name: Start chromedriver + run: | + export DISPLAY=:99 + chromedriver --port=9515 --url-base=/wd/hub & + sudo Xvfb -ac :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & # optional + + # Exchange API Keys and Secrets for OAuth Tokens. + - name: Exchange API Key and Secret for OAuth Tokens + id: get-oauth-tokens + run: | + response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY }}&api_secret=${{ env.CONVERTKIT_API_SECRET }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ inputs.php-version }}") + access_token=$(echo "$response" | jq -r '.oauth.access_token') + refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') + echo "CONVERTKIT_OAUTH_ACCESS_TOKEN=$access_token" >> $GITHUB_ENV + echo "CONVERTKIT_OAUTH_REFRESH_TOKEN=$refresh_token" >> $GITHUB_ENV + response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY_NO_DATA }}&api_secret=${{ env.CONVERTKIT_API_SECRET_NO_DATA }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ inputs.php-version }}") + access_token=$(echo "$response" | jq -r '.oauth.access_token') + refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') + echo "CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=$access_token" >> $GITHUB_ENV + echo "CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=$refresh_token" >> $GITHUB_ENV + + # Write any secrets, such as API keys, to the .env.testing file now. + - name: Define GitHub Secrets in .env.testing + uses: DamianReeves/write-file-action@v1.3 + with: + path: ${{ env.PLUGIN_DIR }}/.env.testing + contents: | + WORDPRESS_V3_BLOCK_EDITOR_ENABLED=${{ env.WORDPRESS_V3_BLOCK_EDITOR_ENABLED }} + WORDPRESS_DB_SQL_DUMP_FILE=${{ env.WORDPRESS_DB_SQL_DUMP_FILE }} + CONVERTKIT_API_KEY=${{ env.CONVERTKIT_API_KEY }} + CONVERTKIT_API_SECRET=${{ env.CONVERTKIT_API_SECRET }} + CONVERTKIT_API_KEY_NO_DATA=${{ env.CONVERTKIT_API_KEY_NO_DATA }} + CONVERTKIT_API_SECRET_NO_DATA=${{ env.CONVERTKIT_API_SECRET_NO_DATA }} + CONVERTKIT_OAUTH_ACCESS_TOKEN=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN }} + CONVERTKIT_OAUTH_REFRESH_TOKEN=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN }} + CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA }} + CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA }} + CONVERTKIT_OAUTH_CLIENT_ID=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }} + CONVERTKIT_OAUTH_REDIRECT_URI=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }} + KIT_OAUTH_REDIRECT_URI=${{ env.KIT_OAUTH_REDIRECT_URI }} + CONVERTKIT_API_SIGNED_SUBSCRIBER_ID=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} + CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} + CONVERTKIT_API_RECAPTCHA_SITE_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} + CONVERTKIT_API_RECAPTCHA_SECRET_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} + + write-mode: overwrite + + # Installs wp-browser, Codeception, PHP CodeSniffer and anything else needed to run tests. + - name: Run Composer + working-directory: ${{ env.PLUGIN_DIR }} + run: composer update + + # Build the frontend CSS and JS assets + - name: Run npm + working-directory: ${{ env.PLUGIN_DIR }} + run: | + npm install + npm run build + + # Confirm that expected files exist if e.g. `composer install` or `npm run build` fails + - name: Check Kit WordPress Libraries and Assets Exists + working-directory: ${{ env.PLUGIN_DIR }} + run: | + set -e + + files=( + "resources/frontend/css/frontend.css" + "resources/frontend/js/dist/frontend.min.asset.php" + "resources/frontend/js/dist/frontend.min.js" + "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-traits.php" + "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php" + "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-log.php" + "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-resource-v4.php" + "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-review-request.php" + ) + + for file in "${files[@]}"; do + echo "Checking: $file" + test -f "$file" || { echo "❌ Missing required file: $file"; exit 1; } + done + + echo "✅ All required files exist." + + # Build PHP Autoloader. + - name: Build PHP Autoloader + working-directory: ${{ env.PLUGIN_DIR }} + run: composer dump-autoload + + # This ensures that applicable files and folders can be written to by WordPress and cache Plugins. + - name: Set File and Folder Permissions + run: | + sudo chmod 767 ${{ env.ROOT_DIR }} + sudo chown www-data:www-data ${{ env.ROOT_DIR }} + + sudo chmod 767 ${{ env.ROOT_DIR }}/wp-config.php + sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-config.php + + sudo chmod 767 ${{ env.ROOT_DIR }}/wp-content + sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content + + sudo chmod -R 767 ${{ env.ROOT_DIR }}/wp-content/uploads + sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content/uploads + + # This ensures the Plugin's log file can be written to. + # We don't recursively do this, as it'll prevent Codeception from writing to the /tests/_output directory. + - name: Set Permissions for Plugin Directory + run: | + sudo chmod g+w ${{ env.PLUGIN_DIR }} + sudo chown www-data:www-data ${{ env.PLUGIN_DIR }} + + - name: Build Tests + working-directory: ${{ env.PLUGIN_DIR }} + run: php vendor/bin/codecept build + + - name: Run tests/${{ inputs.test-group }} + working-directory: ${{ env.PLUGIN_DIR }} + run: php vendor/bin/codecept run tests/${{ inputs.test-group }} --env headless --fail-fast + + # Artifacts are data generated by this workflow that we want to access on test failure. + - name: Upload nginx Error Log to Artifact + if: failure() + uses: actions/upload-artifact@v7 + with: + name: nginx-error-log-${{ steps.test-group.outputs.value }}-${{ inputs.php-version }}.log + path: /var/log/nginx/error.log + + - name: Upload Test Results to Artifact + if: failure() + uses: actions/upload-artifact@v7 + with: + name: test-results-${{ steps.test-group.outputs.value }}-${{ inputs.php-version }} + path: ${{ env.PLUGIN_DIR }}/tests/_output/ + + - name: Upload Plugin Log File to Artifact + if: failure() + uses: actions/upload-artifact@v7 + with: + name: log-${{ steps.test-group.outputs.value }}-${{ inputs.php-version }}.txt + path: ${{ env.PLUGIN_DIR }}/log/log.txt diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 2da178f5d..5c92566de 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -12,27 +12,8 @@ on: jobs: dependabot-metadata: - # Name. - name: Dependabot Metadata - - # Virtual Environment to use. - # @see: https://github.com/actions/virtual-environments - runs-on: ubuntu-latest - - # Don't run if the PR is not from Dependabot. - if: github.actor == 'dependabot[bot]' - - # Outputs. - outputs: - package-ecosystem: ${{ steps.metadata.outputs.package-ecosystem }} - - # Steps to fetch Dependabot metadata. - steps: - - name: Fetch Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v3 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" + uses: ./.github/workflows/_dependabot-metadata.yml + secrets: inherit tests: # Name. @@ -75,54 +56,43 @@ jobs: # Steps to install, configure and run tests steps: - - name: Start MySQL - run: sudo systemctl start mysql.service - - - name: Create MySQL Database - run: | - mysql -e 'CREATE DATABASE test;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - mysql -e 'SHOW DATABASES;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # WordPress won't be able to connect to the DB if we don't perform this step. - - name: Permit MySQL Password Auth for MySQL 8.0 - run: mysql -e "ALTER USER '${{ env.DB_USER }}'@'${{ env.DB_HOST }}' IDENTIFIED WITH mysql_native_password BY '${{ env.DB_PASS }}';" -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # Some workflows checkout WordPress from GitHub, but that seems to bring a bunch of uncompiled files with it. - # Instead download from wordpress.org stable. - - name: Download WordPress - run: wget https://wordpress.org/wordpress-${{ matrix.wp-versions }}.tar.gz - - - name: Extract WordPress - run: tar xfz wordpress-${{ matrix.wp-versions }}.tar.gz - - # Checkout (copy) this repository's Plugin to this VM. + # Checkout Plugin to /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit + # We cannot checkout to ${{ env.PLUGIN_DIR }} as GitHub Actions require it be first placed in /home/runner/work/repo/repo - name: Checkout Plugin uses: actions/checkout@v6 with: - path: ${{ env.PLUGIN_DIR }} + path: /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit - # This step is deliberate, to force PHP 7.4 for WP-CLI to work. - # PHP 8.x results in the workflow failing due to incompatibilities between WP-CLI and PHP 8.x. - - name: Install PHP 7.4.26 - uses: shivammathur/setup-php@v2 + - name: Set up MySQL + uses: ./convertkit/.github/actions/setup-mysql with: - php-version: 7.4.26 - coverage: xdebug + db-name: ${{ env.DB_NAME }} + db-user: ${{ env.DB_USER }} + db-pass: ${{ env.DB_PASS }} + db-host: ${{ env.DB_HOST }} - # We install WP-CLI, as it provides useful commands to setup and install WordPress through the command line. - - name: Install WP-CLI - run: | - curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar - chmod +x wp-cli.phar - sudo mv wp-cli.phar /usr/local/bin/wp-cli - - - name: Setup wp-config.php - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli config create --dbname=${{ env.DB_NAME }} --dbuser=${{ env.DB_USER }} --dbpass=${{ env.DB_PASS }} --dbhost=${{ env.DB_HOST }} --locale=en_DB + - name: Download and Extract WordPress + uses: ./convertkit/.github/actions/download-wordpress + with: + wp-version: ${{ matrix.wp-versions }} + target-dir: ${{ env.ROOT_DIR }} - - name: Install WordPress - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli core install --url=127.0.0.1 --title=ConvertKit --admin_user=admin --admin_password=password --admin_email=wordpress@convertkit.local + # This step is deliberate, to force PHP 7.4 for WP-CLI to work. + # PHP 8.x results in the workflow failing due to incompatibilities between WP-CLI and PHP 8.x. + #- name: Install PHP 7.4.26 + # uses: shivammathur/setup-php@v2 + # with: + # php-version: 7.4.26 + # coverage: xdebug + + - name: Install WordPress via WP-CLI + uses: ./convertkit/.github/actions/install-wordpress + with: + root-dir: ${{ env.ROOT_DIR }} + db-name: ${{ env.DB_NAME }} + db-user: ${{ env.DB_USER }} + db-pass: ${{ env.DB_PASS }} + db-host: ${{ env.DB_HOST }} # env.INSTALL_PLUGINS is a list of Plugin slugs, space separated e.g. contact-form-7 woocommerce. - name: Install Free Third Party WordPress Plugins @@ -135,7 +105,11 @@ jobs: working-directory: ${{ env.ROOT_DIR }} run: wp-cli plugin install ${{ secrets.CONVERTKIT_PAID_PLUGIN_URLS }} --activate - # Install PHP version to run tests against. + # Move Plugin to PLUGIN_DIR. + - name: Move Plugin + run: mv /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit ${{ env.PLUGIN_DIR }} + + # Install PHP version to run coding standards / static analysis against. - name: Install PHP uses: shivammathur/setup-php@v2 with: @@ -148,10 +122,6 @@ jobs: working-directory: ${{ env.PLUGIN_DIR }} run: composer update - - name: Build PHP Autoloader - working-directory: ${{ env.PLUGIN_DIR }} - run: composer dump-autoload - # Installs WordPress scripts for CSS and JS Coding Standards. - name: Run npm install working-directory: ${{ env.PLUGIN_DIR }} diff --git a/.github/workflows/tests-backward-compat.yml b/.github/workflows/tests-backward-compat.yml index 00fdf3de2..2c69e1e3e 100644 --- a/.github/workflows/tests-backward-compat.yml +++ b/.github/workflows/tests-backward-compat.yml @@ -12,37 +12,12 @@ on: jobs: dependabot-metadata: - # Name. - name: Dependabot Metadata - - # Virtual Environment to use. - # @see: https://github.com/actions/virtual-environments - runs-on: ubuntu-latest - - # Don't run if the PR is not from Dependabot. - if: github.actor == 'dependabot[bot]' - - # Outputs. - outputs: - package-ecosystem: ${{ steps.metadata.outputs.package-ecosystem }} - - # Steps to fetch Dependabot metadata. - steps: - - name: Fetch Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v3 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" + uses: ./.github/workflows/_dependabot-metadata.yml + secrets: inherit tests: - # Name. - name: ${{ matrix.test-groups }} / WordPress ${{ matrix.wp-versions }} / PHP ${{ matrix.php-versions }} + name: ${{ matrix.test-group }} / WordPress ${{ matrix.wp-version }} / PHP ${{ matrix.php-version }} - # Virtual Environment to use. - # @see: https://github.com/actions/virtual-environments - runs-on: ubuntu-latest - - # Requieres the dependabot-metadata job to have run successfully. needs: [dependabot-metadata] # Always allow non-Dependabot PRs and pushes. @@ -54,346 +29,27 @@ jobs: needs.dependabot-metadata.outputs.package-ecosystem == 'composer' ) - # Environment Variables. - # Accessible by using ${{ env.NAME }} - # Use ${{ secrets.NAME }} to include any GitHub Secrets in ${{ env.NAME }} - env: - ROOT_DIR: /var/www/html - PLUGIN_DIR: /var/www/html/wp-content/plugins/convertkit - DB_NAME: test - DB_USER: root - DB_PASS: root - DB_HOST: localhost - WORDPRESS_V3_BLOCK_EDITOR_ENABLED: false # Test apiVersion 2 blocks against WordPress 6.2.8 - WORDPRESS_DB_SQL_DUMP_FILE: tests/Support/Data/dump-6.2.8.sql # Used to populate the test site database for WordPress 6.2.8 - INSTALL_PLUGINS_URLS: "https://downloads.wordpress.org/plugin/block-visibility.3.3.0.zip" # URLs to specific third party Plugins or versions that support WordPress 6.2.8 - CONVERTKIT_API_KEY: ${{ secrets.CONVERTKIT_API_KEY }} # ConvertKit API Key, stored in the repository's Settings > Secrets - CONVERTKIT_API_SECRET: ${{ secrets.CONVERTKIT_API_SECRET }} # ConvertKit API Secret, stored in the repository's Settings > Secrets - CONVERTKIT_API_KEY_NO_DATA: ${{ secrets.CONVERTKIT_API_KEY_NO_DATA }} # ConvertKit API Key for ConvertKit account with no data, stored in the repository's Settings > Secrets - CONVERTKIT_API_SECRET_NO_DATA: ${{ secrets.CONVERTKIT_API_SECRET_NO_DATA }} # ConvertKit API Secret for ConvertKit account with no data, stored in the repository's Settings > Secrets - CONVERTKIT_OAUTH_CLIENT_ID: ${{ secrets.CONVERTKIT_OAUTH_CLIENT_ID }} - CONVERTKIT_OAUTH_REDIRECT_URI: ${{ secrets.CONVERTKIT_OAUTH_REDIRECT_URI }} - KIT_OAUTH_REDIRECT_URI: ${{ secrets.KIT_OAUTH_REDIRECT_URI }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} # ConvertKit API Signed Subscriber ID, stored in the repository's Settings > Secrets - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} # ConvertKit API Signed Subscriber ID with no access to Products, stored in the repository's Settings > Secrets - CONVERTKIT_API_RECAPTCHA_SITE_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} # Google reCAPTCHA v3 Site Key, stored in the repository's Settings > Secrets - CONVERTKIT_API_RECAPTCHA_SECRET_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} # Google reCAPTCHA v3 Secret Key, stored in the repository's Settings > Secrets - - # Defines the WordPress and PHP Versions matrix to run tests on - # WordPress 6.2.8 and specific block tests are run to test apiVersion 2 blocks + # WordPress 6.2.8 with apiVersion 2 blocks. We test specific block tests + # that historically depended on apiVersion 2 behaviour. strategy: fail-fast: false matrix: - wp-versions: [ '6.2.8' ] #[ '6.1.1', 'latest' ] - php-versions: [ '8.1' ] #[ '7.4', '8.0', '8.1' ] - - # Folder names within the 'tests' folder to run tests in parallel. - test-groups: [ - 'EndToEnd/broadcasts/blocks-shortcodes/PageBlockBroadcastsCest', - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest', - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormCest', - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormTriggerCest', - 'EndToEnd/products/PageBlockFormatterProductLinkCest', - 'EndToEnd/products/PageBlockProductCest' - ] - - # Steps to install, configure and run tests - steps: - - name: Define Test Group Name - id: test-group - uses: mad9000/actions-find-and-replace-string@5 - with: - source: ${{ matrix.test-groups }} - find: '/' - replace: '-' - replaceAll: true - - # Checkout Plugin to /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit - # We cannot checkout to ${{ env.PLUGIN_DIR }} as GitHub Actions require it be first placed in /home/runner/work/repo/repo - - name: Checkout Plugin - uses: actions/checkout@v6 - with: - path: /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit - - - name: Start MySQL - run: sudo systemctl start mysql.service - - - name: Create MySQL Database - run: | - mysql -e 'CREATE DATABASE test;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - mysql -e 'SHOW DATABASES;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # WordPress won't be able to connect to the DB if we don't perform this step. - - name: Permit MySQL Password Auth for MySQL 8.0 - run: mysql -e "ALTER USER '${{ env.DB_USER }}'@'${{ env.DB_HOST }}' IDENTIFIED WITH mysql_native_password BY '${{ env.DB_PASS }}';" -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # Some workflows checkout WordPress from GitHub, but that seems to bring a bunch of uncompiled files with it. - # Instead download from wordpress.org stable. - - name: Download and Extract WordPress - run: | - sudo chown -R runner:docker /var/www/html - ls -la /var/www/html - cd /var/www/html - wget https://wordpress.org/wordpress-${{ matrix.wp-versions }}.tar.gz - tar xfz wordpress-${{ matrix.wp-versions }}.tar.gz - mv wordpress/* . - rm -rf wordpress wordpress-${{ matrix.wp-versions }}.tar.gz - - # We install WP-CLI, as it provides useful commands to setup and install WordPress through the command line. - - name: Install WP-CLI - run: | - curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar - chmod +x wp-cli.phar - sudo mv wp-cli.phar /usr/local/bin/wp-cli - - - name: Setup wp-config.php - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli config create --dbname=${{ env.DB_NAME }} --dbuser=${{ env.DB_USER }} --dbpass=${{ env.DB_PASS }} --dbhost=${{ env.DB_HOST }} --locale=en_DB - - - name: Install WordPress - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli core install --url=127.0.0.1 --title=ConvertKit --admin_user=admin --admin_password=password --admin_email=wordpress@convertkit.local - - # env.INSTALL_PLUGINS_URLS is a list of Plugin URLs, space separated, to install specific versions of third party Plugins. - - name: Install Free Third Party WordPress Specific Version Plugins - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli plugin install ${{ env.INSTALL_PLUGINS_URLS }} - - # Move Plugin - - name: Move Plugin - run: mv /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit ${{ env.PLUGIN_DIR }} - - # WP_DEBUG = true is required so all PHP errors are output and caught by tests (E_ALL). - - name: Enable WP_DEBUG - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set WP_DEBUG true --raw - - # FS_METHOD = direct is required for WP_Filesystem to operate without suppressed PHP fopen() errors that trip up tests. - - name: Enable FS_METHOD - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set FS_METHOD direct - - # DISALLOW_FILE_MODS = true is required to disable the block directory's "Available to Install" suggestions, which trips up - # tests that search and wait for block results. - - name: Enable DISALLOW_FILE_MODS - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set DISALLOW_FILE_MODS true --raw - - # Prevent WordPress auto updating to the latest version, as we specifically want to use a specific version of WordPress. - - name: Disable WP_AUTO_UPDATE_CORE - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set WP_AUTO_UPDATE_CORE false --raw - - # This step is deliberately after WordPress installation and configuration, as enabling PHP 8.x before using WP-CLI results - # in the workflow failing due to incompatibilities between WP-CLI and PHP 8.x. - # By installing PHP at this stage, we can still run our tests against e.g. PHP 8.x. - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - coverage: xdebug - - # Workaround for PHP 8.3 (and potentially other versions): the GitHub - # Actions ubuntu-latest runner ships with a pre-installed PHP that leaves - # an empty regular file at /run/php/php-fpm.sock. setup-php - # installs a different patch version on top, but PHP-FPM never replaces - # the stale file with a real Unix socket, so nginx returns 502. - # Removing the stale file and restarting PHP-FPM forces a clean socket. - - name: Restart PHP-FPM - run: | - sudo rm -f /run/php/php${{ matrix.php-versions }}-fpm.sock - sudo systemctl restart php${{ matrix.php-versions }}-fpm - sleep 1 - ls -la /run/php/ - - # Configure nginx to use the PHP version and WOrdPress installation at /var/www/html - - name: Configure nginx site - run: | - sudo rm -f /etc/nginx/sites-enabled/default - sudo tee /etc/nginx/sites-available/default > /dev/null << 'EOF' - - server { - listen 80 default_server; - listen [::]:80 default_server; - - root /var/www/html; - index index.php; - - server_name localhost; - - location / { - try_files $uri $uri/ /index.php?$args; - } - - location ~ \.php$ { - include snippets/fastcgi-php.conf; - fastcgi_pass unix:/run/php/php${{ matrix.php-versions }}-fpm.sock; - - # Prevent 502 Bad Gateway error "upstream sent too big header while reading response header from upstream" - fastcgi_buffers 16 16k; - fastcgi_buffer_size 32k; - fastcgi_busy_buffers_size 64k; - } - - location ~ /\.ht { - deny all; - } - } - EOF - - sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default || true - - - name: Test nginx - run: sudo nginx -t - - - name: Start nginx - run: sudo systemctl start nginx.service - - # Start chromedriver - - name: Start chromedriver - run: | - export DISPLAY=:99 - chromedriver --port=9515 --url-base=/wd/hub & - sudo Xvfb -ac :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & # optional - - # Exchange API Keys and Secrets for OAuth Tokens. - - name: Exchange API Key and Secret for OAuth Tokens - id: get-oauth-tokens - run: | - response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY }}&api_secret=${{ env.CONVERTKIT_API_SECRET }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}") - access_token=$(echo "$response" | jq -r '.oauth.access_token') - refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') - echo "CONVERTKIT_OAUTH_ACCESS_TOKEN=$access_token" >> $GITHUB_ENV - echo "CONVERTKIT_OAUTH_REFRESH_TOKEN=$refresh_token" >> $GITHUB_ENV - response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY_NO_DATA }}&api_secret=${{ env.CONVERTKIT_API_SECRET_NO_DATA }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}") - access_token=$(echo "$response" | jq -r '.oauth.access_token') - refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') - echo "CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=$access_token" >> $GITHUB_ENV - echo "CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=$refresh_token" >> $GITHUB_ENV - - # Write any secrets, such as API keys, to the .env.testing file now. - - name: Define GitHub Secrets in .env.dist.testing - uses: DamianReeves/write-file-action@v1.3 - with: - path: ${{ env.PLUGIN_DIR }}/.env.testing - contents: | - WORDPRESS_V3_BLOCK_EDITOR_ENABLED=${{ env.WORDPRESS_V3_BLOCK_EDITOR_ENABLED }} - WORDPRESS_DB_SQL_DUMP_FILE=${{ env.WORDPRESS_DB_SQL_DUMP_FILE }} - CONVERTKIT_API_KEY=${{ env.CONVERTKIT_API_KEY }} - CONVERTKIT_API_SECRET=${{ env.CONVERTKIT_API_SECRET }} - CONVERTKIT_API_KEY_NO_DATA=${{ env.CONVERTKIT_API_KEY_NO_DATA }} - CONVERTKIT_API_SECRET_NO_DATA=${{ env.CONVERTKIT_API_SECRET_NO_DATA }} - CONVERTKIT_OAUTH_ACCESS_TOKEN=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN }} - CONVERTKIT_OAUTH_REFRESH_TOKEN=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN }} - CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA }} - CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA }} - CONVERTKIT_OAUTH_CLIENT_ID=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }} - CONVERTKIT_OAUTH_REDIRECT_URI=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }} - KIT_OAUTH_REDIRECT_URI=${{ env.KIT_OAUTH_REDIRECT_URI }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} - CONVERTKIT_API_RECAPTCHA_SITE_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} - CONVERTKIT_API_RECAPTCHA_SECRET_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} - - write-mode: overwrite - - # Installs wp-browser, Codeception, PHP CodeSniffer and anything else needed to run tests. - - name: Run Composer - working-directory: ${{ env.PLUGIN_DIR }} - run: composer update - - # Build the frontend CSS and JS assets - - name: Run npm - working-directory: ${{ env.PLUGIN_DIR }} - run: | - npm install - npm run build - - # Confirm that expected files exist - # if e.g. `composer install` or `npm run build` fails - - name: Check Kit WordPress Libraries and Assets Exists - working-directory: ${{ env.PLUGIN_DIR }} - run: | - set -e - - files=( - "resources/frontend/css/frontend.css" - "resources/frontend/js/dist/frontend.min.asset.php" - "resources/frontend/js/dist/frontend.min.js" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-traits.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-log.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-resource-v4.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-review-request.php" - ) - - for file in "${files[@]}"; do - echo "Checking: $file" - test -f "$file" || { echo "❌ Missing required file: $file"; exit 1; } - done - - echo "✅ All required files exist." - - - name: Build PHP Autoloader - working-directory: ${{ env.PLUGIN_DIR }} - run: composer dump-autoload - - # This ensures that applicable files and folders can be written to by WordPress and cache Plugins. - - name: Set File and Folder Permissions - run: | - sudo chmod 767 ${{ env.ROOT_DIR }} - sudo chown www-data:www-data ${{ env.ROOT_DIR }} - - sudo chmod 767 ${{ env.ROOT_DIR }}/wp-config.php - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-config.php - - sudo chmod 767 ${{ env.ROOT_DIR }}/wp-content - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content - - sudo chmod -R 767 ${{ env.ROOT_DIR }}/wp-content/uploads - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content/uploads - - # This ensures the Plugin's log file can be written to. - # We don't recursively do this, as it'll prevent Codeception from writing to the /tests/_output directory. - - name: Set Permissions for Plugin Directory - run: | - sudo chmod g+w ${{ env.PLUGIN_DIR }} - sudo chown www-data:www-data ${{ env.PLUGIN_DIR }} - - # Build Codeception Tests. - - name: Build Tests - working-directory: ${{ env.PLUGIN_DIR }} - run: php vendor/bin/codecept build - - # Run Codeception Tests. - - name: Run tests/${{ matrix.test-groups }} - working-directory: ${{ env.PLUGIN_DIR }} - run: php vendor/bin/codecept run tests/${{ matrix.test-groups }} --env headless --fail-fast - - # Artifacts are data generated by this workflow that we want to access, such as log files, screenshots, HTML output. - # The if: failure() directive means that this will run when the workflow fails e.g. if a test fails, which is needed - # because we want to see why a test failed. - - name: Upload nginx Error Log to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: nginx-error-log-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}.log - path: /var/log/nginx/error.log - - - name: Upload Test Results to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: test-results-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }} - path: ${{ env.PLUGIN_DIR }}/tests/_output/ - - - name: Upload Plugin Log File to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: log-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}.txt - path: ${{ env.PLUGIN_DIR }}/log/log.txt \ No newline at end of file + wp-version: [ '6.2.8' ] + php-version: [ '8.1' ] + test-group: + - 'EndToEnd/broadcasts/blocks-shortcodes/PageBlockBroadcastsCest' + - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest' + - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormCest' + - 'EndToEnd/forms/blocks-shortcodes/PageBlockFormTriggerCest' + - 'EndToEnd/products/PageBlockFormatterProductLinkCest' + - 'EndToEnd/products/PageBlockProductCest' + + uses: ./.github/workflows/_run-tests.yml + secrets: inherit + with: + wp-version: ${{ matrix.wp-version }} + php-version: ${{ matrix.php-version }} + test-group: ${{ matrix.test-group }} + block-editor-v3-enabled: 'false' + db-sql-dump-file: 'tests/Support/Data/dump-6.2.8.sql' + install-plugins-urls: 'https://downloads.wordpress.org/plugin/block-visibility.3.3.0.zip' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 146c2d248..84bdef4bd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,37 +12,12 @@ on: jobs: dependabot-metadata: - # Name. - name: Dependabot Metadata - - # Virtual Environment to use. - # @see: https://github.com/actions/virtual-environments - runs-on: ubuntu-latest - - # Don't run if the PR is not from Dependabot. - if: github.actor == 'dependabot[bot]' - - # Outputs. - outputs: - package-ecosystem: ${{ steps.metadata.outputs.package-ecosystem }} - - # Steps to fetch Dependabot metadata. - steps: - - name: Fetch Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v3 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" + uses: ./.github/workflows/_dependabot-metadata.yml + secrets: inherit tests: - # Name. - name: ${{ matrix.test-groups }} / WordPress ${{ matrix.wp-versions }} / PHP ${{ matrix.php-versions }} + name: ${{ matrix.test-group }} / WordPress ${{ matrix.wp-version }} / PHP ${{ matrix.php-version }} - # Virtual Environment to use. - # @see: https://github.com/actions/virtual-environments - runs-on: ubuntu-latest - - # Requieres the dependabot-metadata job to have run successfully. needs: [dependabot-metadata] # Always allow non-Dependabot PRs and pushes. @@ -54,388 +29,44 @@ jobs: needs.dependabot-metadata.outputs.package-ecosystem == 'composer' ) - # Environment Variables. - # Accessible by using ${{ env.NAME }} - # Use ${{ secrets.NAME }} to include any GitHub Secrets in ${{ env.NAME }} - env: - ROOT_DIR: /var/www/html - PLUGIN_DIR: /var/www/html/wp-content/plugins/convertkit - CACHE_DIR: /var/www/html/wp-content/plugins/wp-super-cache/ - DB_NAME: test - DB_USER: root - DB_PASS: root - DB_HOST: localhost - WORDPRESS_V3_BLOCK_EDITOR_ENABLED: true - WORDPRESS_DB_SQL_DUMP_FILE: tests/Support/Data/dump.sql - INSTALL_PLUGINS: "admin-menu-editor autoptimize beaver-builder-lite-version block-visibility contact-form-7 classic-editor custom-post-type-ui debloat elementor forminator jetpack-boost mailchimp-for-wp rocket-lazy-load woocommerce wordpress-seo wpforms-lite litespeed-cache wp-crontrol wp-super-cache w3-total-cache wp-fastest-cache wp-optimize sg-cachepress" # Don't include this repository's Plugin here. - INSTALL_PLUGINS_URLS: "https://downloads.wordpress.org/plugin/convertkit-for-woocommerce.1.6.4.zip http://cktestplugins.wpengine.com/wp-content/uploads/2024/01/convertkit-action-filter-tests.zip http://cktestplugins.wpengine.com/wp-content/uploads/2024/11/disable-doing-it-wrong-notices.zip http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode-js_composer.7.8.zip http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode-core.zip" # URLs to specific third party Plugins - INSTALL_THEMES_URLS: "http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode.zip http://cktestplugins.wpengine.com/wp-content/uploads/2026/03/Divi_5.zip http://cktestplugins.wpengine.com/wp-content/uploads/2026/01/impeka.zip" - CONVERTKIT_API_KEY: ${{ secrets.CONVERTKIT_API_KEY }} # ConvertKit API Key, stored in the repository's Settings > Secrets - CONVERTKIT_API_SECRET: ${{ secrets.CONVERTKIT_API_SECRET }} # ConvertKit API Secret, stored in the repository's Settings > Secrets - CONVERTKIT_API_KEY_NO_DATA: ${{ secrets.CONVERTKIT_API_KEY_NO_DATA }} # ConvertKit API Key for ConvertKit account with no data, stored in the repository's Settings > Secrets - CONVERTKIT_API_SECRET_NO_DATA: ${{ secrets.CONVERTKIT_API_SECRET_NO_DATA }} # ConvertKit API Secret for ConvertKit account with no data, stored in the repository's Settings > Secrets - CONVERTKIT_OAUTH_CLIENT_ID: ${{ secrets.CONVERTKIT_OAUTH_CLIENT_ID }} - CONVERTKIT_OAUTH_REDIRECT_URI: ${{ secrets.CONVERTKIT_OAUTH_REDIRECT_URI }} - KIT_OAUTH_REDIRECT_URI: ${{ secrets.KIT_OAUTH_REDIRECT_URI }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} # ConvertKit API Signed Subscriber ID, stored in the repository's Settings > Secrets - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS: ${{ secrets.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} # ConvertKit API Signed Subscriber ID with no access to Products, stored in the repository's Settings > Secrets - CONVERTKIT_API_RECAPTCHA_SITE_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} # Google reCAPTCHA v3 Site Key, stored in the repository's Settings > Secrets - CONVERTKIT_API_RECAPTCHA_SECRET_KEY: ${{ secrets.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} # Google reCAPTCHA v3 Secret Key, stored in the repository's Settings > Secrets - - # Defines the WordPress and PHP Versions matrix to run tests on - # WooCommerce 5.9.0 requires WordPress 5.6 or greater, so we do not test on earlier versions - # If testing older WordPress versions, ensure they are e.g. 5.7.4, 5.6.6 that have the X3 SSL fix: https://core.trac.wordpress.org/ticket/54207 strategy: fail-fast: false matrix: - wp-versions: [ 'latest' ] #[ '6.1.1', 'latest' ] - php-versions: [ '8.1', '8.2', '8.3', '8.4' ] #[ '7.4', '8.0', '8.1' ] - - # Folder names within the 'tests' folder to run tests in parallel. - test-groups: [ - 'EndToEnd/broadcasts/blocks-shortcodes', - 'EndToEnd/broadcasts/import-export', - 'EndToEnd/forms/blocks-shortcodes', - 'EndToEnd/forms/general', - 'EndToEnd/forms/post-types', - 'EndToEnd/general/other', - 'EndToEnd/general/uninstall', - 'EndToEnd/general/plugin-screens', - 'EndToEnd/integrations/divi-builder', - 'EndToEnd/integrations/divi-theme', - 'EndToEnd/integrations/elementor', - 'EndToEnd/integrations/other', - 'EndToEnd/integrations/wlm', - 'EndToEnd/integrations/woocommerce', - 'EndToEnd/landing-pages', - 'EndToEnd/products', - 'EndToEnd/restrict-content/general', - 'EndToEnd/restrict-content/post-types', - 'EndToEnd/tags', - 'Integration' - ] - - # Steps to install, configure and run tests - steps: - - name: Define Test Group Name - id: test-group - uses: mad9000/actions-find-and-replace-string@5 - with: - source: ${{ matrix.test-groups }} - find: '/' - replace: '-' - replaceAll: true - - # Checkout Plugin to /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit - # We cannot checkout to ${{ env.PLUGIN_DIR }} as GitHub Actions require it be first placed in /home/runner/work/repo/repo - - name: Checkout Plugin - uses: actions/checkout@v6 - with: - path: /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit - - - name: Start MySQL - run: sudo systemctl start mysql.service - - - name: Create MySQL Database - run: | - mysql -e 'CREATE DATABASE test;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - mysql -e 'SHOW DATABASES;' -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # WordPress won't be able to connect to the DB if we don't perform this step. - - name: Permit MySQL Password Auth for MySQL 8.0 - run: mysql -e "ALTER USER '${{ env.DB_USER }}'@'${{ env.DB_HOST }}' IDENTIFIED WITH mysql_native_password BY '${{ env.DB_PASS }}';" -u${{ env.DB_USER }} -p${{ env.DB_PASS }} - - # Some workflows checkout WordPress from GitHub, but that seems to bring a bunch of uncompiled files with it. - # Instead download from wordpress.org stable. - - name: Download and Extract WordPress - run: | - sudo chown -R runner:docker /var/www/html - ls -la /var/www/html - cd /var/www/html - wget https://wordpress.org/wordpress-${{ matrix.wp-versions }}.tar.gz - tar xfz wordpress-${{ matrix.wp-versions }}.tar.gz - mv wordpress/* . - rm -rf wordpress wordpress-${{ matrix.wp-versions }}.tar.gz - - # We install WP-CLI, as it provides useful commands to setup and install WordPress through the command line. - - name: Install WP-CLI - run: | - curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar - chmod +x wp-cli.phar - sudo mv wp-cli.phar /usr/local/bin/wp-cli - - - name: Setup wp-config.php - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli config create --dbname=${{ env.DB_NAME }} --dbuser=${{ env.DB_USER }} --dbpass=${{ env.DB_PASS }} --dbhost=${{ env.DB_HOST }} --locale=en_DB - - - name: Install WordPress - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli core install --url=127.0.0.1 --title=ConvertKit --admin_user=admin --admin_password=password --admin_email=wordpress@convertkit.local - - # env.INSTALL_PLUGINS is a list of Plugin slugs, space separated e.g. contact-form-7 woocommerce. - - name: Install Free Third Party WordPress Plugins - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli plugin install ${{ env.INSTALL_PLUGINS }} - - # env.INSTALL_PLUGINS_URLS is a list of Plugin URLs, space separated, to install specific versions of third party Plugins. - - name: Install Free Third Party WordPress Specific Version Plugins - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli plugin install ${{ env.INSTALL_PLUGINS_URLS }} - - # env.INSTALL_THEMES_URLS is a list of Theme URLs, space separated, to install specific versions of third party Themes. - - name: Install Free Third Party WordPress Specific Version Themes - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli theme install ${{ env.INSTALL_THEMES_URLS }} - - # These should be stored as a separated list of URLs in the repository Settings > Secrets > Repository Secret > CONVERTKIT_PAID_PLUGIN_URLS. - # We cannot include the URLs in this file, as they're not Plugins we are permitted to distribute. - - name: Install Paid Third Party WordPress Plugins - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli plugin install ${{ secrets.CONVERTKIT_PAID_PLUGIN_URLS }} - - # Install 2021 Theme, which provides support for widgets. - # 2021 Theme isn't included in WordPress 6.4 or higher, but is required for our widget tests. - - name: Install WordPress Themes - working-directory: ${{ env.ROOT_DIR }} - run: wp-cli theme install twentytwentyone - - # Move Plugin - - name: Move Plugin - run: mv /home/runner/work/convertkit-wordpress/convertkit-wordpress/convertkit ${{ env.PLUGIN_DIR }} - - # WP_DEBUG = true is required so all PHP errors are output and caught by tests (E_ALL). - # WP_DEBUG = false for other integraiton tests, as Elementor in PHP 8.4 throws a deprecation notice - - name: Enable WP_DEBUG - if: ${{ matrix.test-groups != 'EndToEnd/integrations/other' && matrix.php-versions != '8.4' }} - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set WP_DEBUG true --raw - - # FS_METHOD = direct is required for WP_Filesystem to operate without suppressed PHP fopen() errors that trip up tests. - - name: Enable FS_METHOD - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set FS_METHOD direct - - # DISALLOW_FILE_MODS = true is required to disable the block directory's "Available to Install" suggestions, which trips up - # tests that search and wait for block results. - # We don't enable DISALLOW_FILE_MODS for the UninstallCest test, as tests will perform a Plugin deletion - # which requires DISALLOW_FILE_MODS to not be true. - - name: Enable DISALLOW_FILE_MODS - if: ${{ matrix.test-groups != 'EndToEnd/general/uninstall' }} - working-directory: ${{ env.ROOT_DIR }} - run: | - wp-cli config set DISALLOW_FILE_MODS true --raw - - # This step is deliberately after WordPress installation and configuration, as enabling PHP 8.x before using WP-CLI results - # in the workflow failing due to incompatibilities between WP-CLI and PHP 8.x. - # By installing PHP at this stage, we can still run our tests against e.g. PHP 8.x. - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - coverage: xdebug - - # Workaround for PHP 8.3 (and potentially other versions): the GitHub - # Actions ubuntu-latest runner ships with a pre-installed PHP that leaves - # an empty regular file at /run/php/php-fpm.sock. setup-php - # installs a different patch version on top, but PHP-FPM never replaces - # the stale file with a real Unix socket, so nginx returns 502. - # Removing the stale file and restarting PHP-FPM forces a clean socket. - - name: Restart PHP-FPM - run: | - sudo rm -f /run/php/php${{ matrix.php-versions }}-fpm.sock - sudo systemctl restart php${{ matrix.php-versions }}-fpm - sleep 1 - ls -la /run/php/ - - # Configure nginx to use the PHP version and WOrdPress installation at /var/www/html - - name: Configure nginx site - run: | - sudo rm -f /etc/nginx/sites-enabled/default - sudo tee /etc/nginx/sites-available/default > /dev/null << 'EOF' - - server { - listen 80 default_server; - listen [::]:80 default_server; - - root /var/www/html; - index index.php; - - server_name localhost; - - location / { - try_files $uri $uri/ /index.php?$args; - } - - location ~ \.php$ { - include snippets/fastcgi-php.conf; - fastcgi_pass unix:/run/php/php${{ matrix.php-versions }}-fpm.sock; - - # Prevent 502 Bad Gateway error "upstream sent too big header while reading response header from upstream" - fastcgi_buffers 16 16k; - fastcgi_buffer_size 32k; - fastcgi_busy_buffers_size 64k; - } - - location ~ /\.ht { - deny all; - } - } - EOF - - sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default || true - - - name: Test nginx - run: sudo nginx -t - - - name: Start nginx - run: sudo systemctl start nginx.service - - # Start chromedriver - - name: Start chromedriver - run: | - export DISPLAY=:99 - chromedriver --port=9515 --url-base=/wd/hub & - sudo Xvfb -ac :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & # optional - - # Exchange API Keys and Secrets for OAuth Tokens. - - name: Exchange API Key and Secret for OAuth Tokens - id: get-oauth-tokens - run: | - response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY }}&api_secret=${{ env.CONVERTKIT_API_SECRET }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}") - access_token=$(echo "$response" | jq -r '.oauth.access_token') - refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') - echo "CONVERTKIT_OAUTH_ACCESS_TOKEN=$access_token" >> $GITHUB_ENV - echo "CONVERTKIT_OAUTH_REFRESH_TOKEN=$refresh_token" >> $GITHUB_ENV - response=$(curl -s -X POST "${{ secrets.CONVERTKIT_EXCHANGE_API_KEYS_ENDPOINT }}?api_key=${{ env.CONVERTKIT_API_KEY_NO_DATA }}&api_secret=${{ env.CONVERTKIT_API_SECRET_NO_DATA }}&client_id=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }}&redirect_uri=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }}&tenant_name=github-actions-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}") - access_token=$(echo "$response" | jq -r '.oauth.access_token') - refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') - echo "CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=$access_token" >> $GITHUB_ENV - echo "CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=$refresh_token" >> $GITHUB_ENV - - # Write any secrets, such as API keys, to the .env.testing file now. - - name: Define GitHub Secrets in .env.dist.testing - uses: DamianReeves/write-file-action@v1.3 - with: - path: ${{ env.PLUGIN_DIR }}/.env.testing - contents: | - WORDPRESS_V3_BLOCK_EDITOR_ENABLED=${{ env.WORDPRESS_V3_BLOCK_EDITOR_ENABLED }} - WORDPRESS_DB_SQL_DUMP_FILE=${{ env.WORDPRESS_DB_SQL_DUMP_FILE }} - CONVERTKIT_API_KEY=${{ env.CONVERTKIT_API_KEY }} - CONVERTKIT_API_SECRET=${{ env.CONVERTKIT_API_SECRET }} - CONVERTKIT_API_KEY_NO_DATA=${{ env.CONVERTKIT_API_KEY_NO_DATA }} - CONVERTKIT_API_SECRET_NO_DATA=${{ env.CONVERTKIT_API_SECRET_NO_DATA }} - CONVERTKIT_OAUTH_ACCESS_TOKEN=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN }} - CONVERTKIT_OAUTH_REFRESH_TOKEN=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN }} - CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_ACCESS_TOKEN_NO_DATA }} - CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA=${{ env.CONVERTKIT_OAUTH_REFRESH_TOKEN_NO_DATA }} - CONVERTKIT_OAUTH_CLIENT_ID=${{ env.CONVERTKIT_OAUTH_CLIENT_ID }} - CONVERTKIT_OAUTH_REDIRECT_URI=${{ env.CONVERTKIT_OAUTH_REDIRECT_URI }} - KIT_OAUTH_REDIRECT_URI=${{ env.KIT_OAUTH_REDIRECT_URI }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID }} - CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS=${{ env.CONVERTKIT_API_SIGNED_SUBSCRIBER_ID_NO_ACCESS }} - CONVERTKIT_API_RECAPTCHA_SITE_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SITE_KEY }} - CONVERTKIT_API_RECAPTCHA_SECRET_KEY=${{ env.CONVERTKIT_API_RECAPTCHA_SECRET_KEY }} - - write-mode: overwrite - - # Installs wp-browser, Codeception, PHP CodeSniffer and anything else needed to run tests. - - name: Run Composer - working-directory: ${{ env.PLUGIN_DIR }} - run: composer update - - # Build the frontend CSS and JS assets - - name: Run npm - working-directory: ${{ env.PLUGIN_DIR }} - run: | - npm install - npm run build - - # Confirm that expected files exist - # if e.g. `composer install` or `npm run build` fails - - name: Check Kit WordPress Libraries and Assets Exists - working-directory: ${{ env.PLUGIN_DIR }} - run: | - set -e - - files=( - "resources/frontend/css/frontend.css" - "resources/frontend/js/dist/frontend.min.asset.php" - "resources/frontend/js/dist/frontend.min.js" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-traits.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-api-v4.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-log.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-resource-v4.php" - "vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-review-request.php" - ) - - for file in "${files[@]}"; do - echo "Checking: $file" - test -f "$file" || { echo "❌ Missing required file: $file"; exit 1; } - done - - echo "✅ All required files exist." - - - name: Build PHP Autoloader - working-directory: ${{ env.PLUGIN_DIR }} - run: composer dump-autoload - - # This ensures that applicable files and folders can be written to by WordPress and cache Plugins. - - name: Set File and Folder Permissions - run: | - sudo chmod 767 ${{ env.ROOT_DIR }} - sudo chown www-data:www-data ${{ env.ROOT_DIR }} - - sudo chmod 767 ${{ env.ROOT_DIR }}/wp-config.php - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-config.php - - sudo chmod 767 ${{ env.ROOT_DIR }}/wp-content - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content - - sudo chmod -R 767 ${{ env.ROOT_DIR }}/wp-content/uploads - sudo chown www-data:www-data ${{ env.ROOT_DIR }}/wp-content/uploads - - # This ensures the Plugin's log file can be written to. - # We don't recursively do this, as it'll prevent Codeception from writing to the /tests/_output directory. - - name: Set Permissions for Plugin Directory - run: | - sudo chmod g+w ${{ env.PLUGIN_DIR }} - sudo chown www-data:www-data ${{ env.PLUGIN_DIR }} - - # Build Codeception Tests. - - name: Build Tests - working-directory: ${{ env.PLUGIN_DIR }} - run: php vendor/bin/codecept build - - # Run Codeception Tests. - - name: Run tests/${{ matrix.test-groups }} - working-directory: ${{ env.PLUGIN_DIR }} - run: php vendor/bin/codecept run tests/${{ matrix.test-groups }} --env headless --fail-fast - - # Artifacts are data generated by this workflow that we want to access, such as log files, screenshots, HTML output. - # The if: failure() directive means that this will run when the workflow fails e.g. if a test fails, which is needed - # because we want to see why a test failed. - - name: Upload nginx Error Log to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: nginx-error-log-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}.log - path: /var/log/nginx/error.log - - - name: Upload Test Results to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: test-results-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }} - path: ${{ env.PLUGIN_DIR }}/tests/_output/ - - - name: Upload Plugin Log File to Artifact - if: failure() - uses: actions/upload-artifact@v7 - with: - name: log-${{ steps.test-group.outputs.value }}-${{ matrix.php-versions }}.txt - path: ${{ env.PLUGIN_DIR }}/log/log.txt + wp-version: [ 'latest' ] + php-version: [ '8.1', '8.2', '8.3', '8.4' ] + test-group: + - 'EndToEnd/broadcasts/blocks-shortcodes' + - 'EndToEnd/broadcasts/import-export' + - 'EndToEnd/forms/blocks-shortcodes' + - 'EndToEnd/forms/general' + - 'EndToEnd/forms/post-types' + - 'EndToEnd/general/other' + - 'EndToEnd/general/uninstall' + - 'EndToEnd/general/plugin-screens' + - 'EndToEnd/integrations/divi-builder' + - 'EndToEnd/integrations/divi-theme' + - 'EndToEnd/integrations/elementor' + - 'EndToEnd/integrations/other' + - 'EndToEnd/integrations/wlm' + - 'EndToEnd/integrations/woocommerce' + - 'EndToEnd/landing-pages' + - 'EndToEnd/products' + - 'EndToEnd/restrict-content/general' + - 'EndToEnd/restrict-content/post-types' + - 'EndToEnd/tags' + - 'Integration' + + uses: ./.github/workflows/_run-tests.yml + secrets: inherit + with: + wp-version: ${{ matrix.wp-version }} + php-version: ${{ matrix.php-version }} + test-group: ${{ matrix.test-group }} + block-editor-v3-enabled: 'true' + db-sql-dump-file: 'tests/Support/Data/dump.sql' + install-plugins: "admin-menu-editor autoptimize beaver-builder-lite-version block-visibility contact-form-7 classic-editor custom-post-type-ui debloat elementor forminator jetpack-boost mailchimp-for-wp rocket-lazy-load woocommerce wordpress-seo wpforms-lite litespeed-cache wp-crontrol wp-super-cache w3-total-cache wp-fastest-cache wp-optimize sg-cachepress" + install-plugins-urls: "https://downloads.wordpress.org/plugin/convertkit-for-woocommerce.1.6.4.zip http://cktestplugins.wpengine.com/wp-content/uploads/2024/01/convertkit-action-filter-tests.zip http://cktestplugins.wpengine.com/wp-content/uploads/2024/11/disable-doing-it-wrong-notices.zip http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode-js_composer.7.8.zip http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode-core.zip" + install-themes-urls: "http://cktestplugins.wpengine.com/wp-content/uploads/2025/03/uncode.zip http://cktestplugins.wpengine.com/wp-content/uploads/2026/03/Divi_5.zip http://cktestplugins.wpengine.com/wp-content/uploads/2026/01/impeka.zip" build-and-deploy: name: WordPress Playground @@ -475,7 +106,7 @@ jobs: CONVERTKIT_API_SECRET: ${{ secrets.CONVERTKIT_API_SECRET }} # ConvertKit API Secret, stored in the repository's Settings > Secrets CONVERTKIT_OAUTH_CLIENT_ID: ${{ secrets.CONVERTKIT_OAUTH_CLIENT_ID }} CONVERTKIT_OAUTH_REDIRECT_URI: ${{ secrets.CONVERTKIT_OAUTH_REDIRECT_URI }} - + # Steps to build and provide the Playground URL steps: # Checkout (copy) this repository's Plugin to this VM. @@ -515,7 +146,7 @@ jobs: refresh_token=$(echo "$response" | jq -r '.oauth.refresh_token') echo "CONVERTKIT_OAUTH_ACCESS_TOKEN=$access_token" >> $GITHUB_ENV echo "CONVERTKIT_OAUTH_REFRESH_TOKEN=$refresh_token" >> $GITHUB_ENV - + # Create base64 encoded version of blueprint JSON for Playground URL. # See: https://wordpress.github.io/wordpress-playground/blueprints/using-blueprints#base64-encoded-blueprints - name: Create Blueprint JSON, Base64 Encoded @@ -540,8 +171,8 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: `## WordPress Playground - + :rocket: Your PR has been built and is ready for testing in WordPress Playground! - + [Click here to test your changes in WordPress Playground](https://playground.wordpress.net/#${{ steps.blueprint.outputs.blueprint_json_base64 }})` - }) \ No newline at end of file + })