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..6d75fd766 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. @@ -42,7 +23,7 @@ jobs: # @see: https://github.com/actions/virtual-environments runs-on: ubuntu-latest - # Requieres the dependabot-metadata job to have run successfully. + # Requires the dependabot-metadata job to have run successfully. needs: [dependabot-metadata] # Always allow non-Dependabot PRs and pushes. @@ -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 97e7a5170..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,333 +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 - - # 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 b72cd3642..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,375 +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 - - # 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 @@ -462,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. @@ -502,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 @@ -527,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 + }) diff --git a/includes/blocks/class-convertkit-block-content.php b/includes/blocks/class-convertkit-block-content.php index 45c2bc4f6..73a6fa01d 100644 --- a/includes/blocks/class-convertkit-block-content.php +++ b/includes/blocks/class-convertkit-block-content.php @@ -256,7 +256,7 @@ public function render( $atts, $content = '' ) { // Get the subscriber's tags, to see if they subscribed to this tag. $tags = $api->get_subscriber_tags( $subscriber_id ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $tags ) ) { if ( $settings->debug_enabled() ) { return ''; diff --git a/includes/blocks/class-convertkit-block-form-builder.php b/includes/blocks/class-convertkit-block-form-builder.php index fe1a97b20..16833a0fd 100644 --- a/includes/blocks/class-convertkit-block-form-builder.php +++ b/includes/blocks/class-convertkit-block-form-builder.php @@ -151,7 +151,7 @@ public function maybe_subscribe() { $custom_fields ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { // Store entry and return. if ( $form_data['store_entries'] ) { diff --git a/includes/blocks/class-convertkit-block-form-trigger.php b/includes/blocks/class-convertkit-block-form-trigger.php index f9a264329..4bec4aa50 100644 --- a/includes/blocks/class-convertkit-block-form-trigger.php +++ b/includes/blocks/class-convertkit-block-form-trigger.php @@ -362,7 +362,7 @@ public function render( $atts ) { $this->is_block_editor_request() ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $html ) ) { if ( $settings->debug_enabled() ) { return ''; diff --git a/includes/blocks/class-convertkit-block-form.php b/includes/blocks/class-convertkit-block-form.php index 964dcf485..f5e121ba8 100644 --- a/includes/blocks/class-convertkit-block-form.php +++ b/includes/blocks/class-convertkit-block-form.php @@ -382,14 +382,14 @@ public function render( $atts ) { $forms = new ConvertKit_Resource_Forms( 'output_form' ); $form = $forms->get_html( $form_id, $post_id ); - // If an error occured, it might be that we're requesting a Form ID that exists in ConvertKit + // If an error occurred, it might be that we're requesting a Form ID that exists in ConvertKit // but does not yet exist in the Plugin's Form Resources. // If so, refresh the Form Resources and try again. if ( is_wp_error( $form ) && $form->get_error_data() === 404 ) { // Refresh Forms from the API. $result = $forms->refresh(); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { if ( $settings->debug_enabled() ) { return ' '; @@ -403,7 +403,7 @@ public function render( $atts ) { $form = $forms->get_html( $form_id, $post_id ); } - // If an error still occured, the shortcode might be from the ConvertKit App for a Legacy Form ID + // If an error still occurred, the shortcode might be from the ConvertKit App for a Legacy Form ID // These ConvertKit App shortcodes, for some reason, use a different Form ID than the one presented // to us in the API. // For example, a Legacy Form ID might be 470099, but the ConvertKit app says to use the shortcode [convertkit form=5281783]). diff --git a/includes/blocks/class-convertkit-block-product.php b/includes/blocks/class-convertkit-block-product.php index a6f1362a0..bea8b12a5 100644 --- a/includes/blocks/class-convertkit-block-product.php +++ b/includes/blocks/class-convertkit-block-product.php @@ -418,7 +418,7 @@ public function render( $atts ) { ) ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $html ) ) { if ( $settings->debug_enabled() ) { return ''; diff --git a/includes/class-convertkit-broadcasts-exporter.php b/includes/class-convertkit-broadcasts-exporter.php index 6acc24a86..48cb05fa5 100644 --- a/includes/class-convertkit-broadcasts-exporter.php +++ b/includes/class-convertkit-broadcasts-exporter.php @@ -112,7 +112,7 @@ public function run_row_action() { // Export Post to a draft ConvertKit Broadcast. $result = $this->export_post_to_broadcast( $post_id ); - // If an error occured, display an error message. + // If an error occurred, display an error message. if ( is_wp_error( $result ) ) { wp_die( esc_html( $result->get_error_message() ) ); } @@ -237,7 +237,7 @@ public function export_post_to_broadcast( $post_id ) { $post->post_excerpt ); - // If an error occured, return it now. + // If an error occurred, return it now. if ( is_wp_error( $result ) ) { return $result; } diff --git a/includes/class-convertkit-broadcasts-importer.php b/includes/class-convertkit-broadcasts-importer.php index 7b918c0b3..008d90ca2 100644 --- a/includes/class-convertkit-broadcasts-importer.php +++ b/includes/class-convertkit-broadcasts-importer.php @@ -171,7 +171,7 @@ public function import_broadcast( $broadcast_id, $post_status = 'publish', $auth // Unset API class. unset( $api ); - // Bail if an error occured fetching the Broadcast. + // Bail if an error occurred fetching the Broadcast. if ( is_wp_error( $broadcast ) ) { $this->maybe_log( 'ConvertKit_Broadcasts_Importer::refresh(): Broadcast #' . $broadcast_id . '. Error fetching from API: ' . $broadcast->get_error_message() ); return $broadcast; @@ -190,7 +190,7 @@ public function import_broadcast( $broadcast_id, $post_status = 'publish', $auth true ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $post_id ) ) { $this->maybe_log( 'ConvertKit_Broadcasts_Importer::refresh(): Broadcast #' . $broadcast_id . '. Error on wp_insert_post(): ' . $post_id->get_error_message() ); return $post_id; @@ -211,7 +211,7 @@ public function import_broadcast( $broadcast_id, $post_status = 'publish', $auth true ); - // Bail if an error occured updating the Post. + // Bail if an error occurred updating the Post. if ( is_wp_error( $post_id ) ) { $this->maybe_log( 'ConvertKit_Broadcasts_Importer::refresh(): Broadcast #' . $broadcast_id . '. Error on wp_update_post() when adding Broadcast content: ' . $post_id->get_error_message() ); return $post_id; @@ -251,7 +251,7 @@ public function import_broadcast( $broadcast_id, $post_status = 'publish', $auth true ); - // Maybe log if an error occured updating the Post to the publish status. + // Maybe log if an error occurred updating the Post to the publish status. if ( is_wp_error( $post_id ) ) { $this->maybe_log( 'ConvertKit_Broadcasts_Importer::refresh(): Broadcast #' . $broadcast_id . '. Error on wp_update_post() when transitioning post status from draft to publish: ' . $post_id->get_error_message() ); return $post_id; @@ -643,7 +643,7 @@ private function add_broadcast_image_to_post( $broadcast, $post_id ) { $broadcast['thumbnail_alt'] ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $image_id ) ) { return $image_id; } diff --git a/includes/class-convertkit-output-restrict-content.php b/includes/class-convertkit-output-restrict-content.php index 93b6930e8..6809455e6 100644 --- a/includes/class-convertkit-output-restrict-content.php +++ b/includes/class-convertkit-output-restrict-content.php @@ -195,7 +195,7 @@ public function register_routes() { $post_id ); - // If an error occured, build the email form view with the error message. + // If an error occurred, build the email form view with the error message. if ( is_wp_error( $result ) ) { // Set error to display on screen. $output_restrict_content->error = $result; @@ -287,7 +287,7 @@ public function register_routes() { // Run subscriber authentication. $result = $output_restrict_content->subscriber_authentication_verify( $post_id, $token, $subscriber_code ); - // If an error occured, build the code form view with the error message. + // If an error occurred, build the code form view with the error message. if ( is_wp_error( $result ) ) { // Set error to display on screen. $output_restrict_content->error = $result; @@ -408,7 +408,7 @@ public function maybe_run_subscriber_authentication() { // Tag subscriber. $result = $this->api->tag_subscribe( $this->resource_id, $email ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { $this->error = $result; return; @@ -436,7 +436,7 @@ public function maybe_run_subscriber_authentication() { // Run subscriber authentication. $result = $this->subscriber_authentication_send_code( $email, $this->post_id ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { $this->error = $result; return; @@ -497,7 +497,7 @@ public function maybe_run_subscriber_verification() { // Run subscriber verification. $subscriber_id = $this->subscriber_authentication_verify( $this->post_id, sanitize_text_field( wp_unslash( $_REQUEST['token'] ) ), sanitize_text_field( wp_unslash( $_REQUEST['subscriber_code'] ) ) ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $subscriber_id ) ) { $this->error = $subscriber_id; return; @@ -526,7 +526,7 @@ public function subscriber_authentication_send_code( $email, $post_id ) { $this->get_url( $post_id ) ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $token ) ) { return $token; } @@ -558,7 +558,7 @@ public function subscriber_authentication_verify( $post_id, $token, $subscriber_ // Verify the token and subscriber code. $subscriber_id = $this->api->subscriber_authentication_verify( $token, $subscriber_code ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $subscriber_id ) ) { return $subscriber_id; } @@ -1127,7 +1127,7 @@ private function subscriber_has_access_to_product_by_signed_subscriber_id( $sign // Get products that the subscriber has access to. $result = $this->api->profile( $signed_subscriber_id ); - // If an error occured, the subscriber ID is invalid. + // If an error occurred, the subscriber ID is invalid. if ( is_wp_error( $result ) ) { return false; } @@ -1157,7 +1157,7 @@ private function subscriber_has_access_to_form_by_signed_subscriber_id( $signed_ // Get products that the subscriber has access to. $result = $this->api->profile( $signed_subscriber_id ); - // If an error occured, the subscriber ID is invalid. + // If an error occurred, the subscriber ID is invalid. if ( is_wp_error( $result ) ) { return false; } @@ -1187,7 +1187,7 @@ private function subscriber_has_access_to_tag_by_signed_subscriber_id( $signed_s // Get products that the subscriber has access to. $result = $this->api->profile( $signed_subscriber_id ); - // If an error occured, the subscriber ID is invalid. + // If an error occurred, the subscriber ID is invalid. if ( is_wp_error( $result ) ) { return false; } @@ -1217,7 +1217,7 @@ private function subscriber_has_access_to_tag_by_subscriber_id( $subscriber_id, // Get tags that the subscriber has been assigned. $tags = $this->api->get_subscriber_tags( $subscriber_id ); - // If an error occured, the subscriber ID is invalid. + // If an error occurred, the subscriber ID is invalid. if ( is_wp_error( $tags ) ) { return false; } @@ -1253,7 +1253,7 @@ public function get_subscriber_id_from_request() { $subscriber = new ConvertKit_Subscriber(); $subscriber_id = $subscriber->get_subscriber_id(); - // If an error occured, the subscriber ID in the request/cookie is not a valid subscriber. + // If an error occurred, the subscriber ID in the request/cookie is not a valid subscriber. if ( is_wp_error( $subscriber_id ) ) { return 0; } diff --git a/includes/class-convertkit-output.php b/includes/class-convertkit-output.php index f1325cb31..93a830528 100644 --- a/includes/class-convertkit-output.php +++ b/includes/class-convertkit-output.php @@ -115,7 +115,7 @@ public function register_routes() { $subscriber = new ConvertKit_Subscriber(); $subscriber_id = $subscriber->validate_and_store_subscriber_email( $email ); - // Bail if an error occured i.e. API hasn't been configured. + // Bail if an error occurred i.e. API hasn't been configured. if ( is_wp_error( $subscriber_id ) ) { return rest_ensure_response( $subscriber_id ); } @@ -202,7 +202,7 @@ public function maybe_tag_subscriber() { if ( ! is_numeric( $this->subscriber_id ) ) { $result = $api->profile( $this->subscriber_id ); - // If an error occured, the subscriber ID is invalid. + // If an error occurred, the subscriber ID is invalid. if ( is_wp_error( $result ) ) { return; } @@ -292,7 +292,7 @@ public function page_takeover() { // Get Landing Page. $landing_page = $this->landing_pages->get_html( $this->post_settings->get_landing_page() ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $landing_page ) ) { return; } @@ -372,7 +372,7 @@ public function append_form_to_content( $content ) { // Get Form HTML. $form = $this->forms->get_html( $form_id, $post_id ); - // If an error occured, it could be because the specified Form ID for the Post either: + // If an error occurred, it could be because the specified Form ID for the Post either: // - belongs to another ConvertKit account (i.e. API credentials were changed in the Plugin, but this Post's specified Form was not changed), or // - the form was deleted from the ConvertKit account. // Attempt to fallback to the default form for this Post Type. @@ -396,7 +396,7 @@ public function append_form_to_content( $content ) { // Get Form HTML. $form = $this->forms->get_html( $form_id, $post_id ); - // If an error occured again, the default form doesn't exist in this ConvertKit account. + // If an error occurred again, the default form doesn't exist in this ConvertKit account. // Just return the Post Content, unedited. if ( is_wp_error( $form ) ) { if ( $this->settings->debug_enabled() ) { @@ -840,7 +840,7 @@ public function get_subscriber_id_from_request() { $subscriber = new ConvertKit_Subscriber(); $subscriber_id = $subscriber->get_subscriber_id(); - // If an error occured, the subscriber ID in the request/cookie is not a valid subscriber. + // If an error occurred, the subscriber ID in the request/cookie is not a valid subscriber. if ( is_wp_error( $subscriber_id ) ) { return; } diff --git a/includes/class-convertkit-recaptcha.php b/includes/class-convertkit-recaptcha.php index feb7a4fba..0156a047e 100644 --- a/includes/class-convertkit-recaptcha.php +++ b/includes/class-convertkit-recaptcha.php @@ -91,7 +91,7 @@ public function verify_recaptcha( $recaptcha_response, $plugin_action ) { ) ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $response ) ) { return $response; } diff --git a/includes/class-convertkit-resource-creator-network-recommendations.php b/includes/class-convertkit-resource-creator-network-recommendations.php index 42845adaa..89af80dc9 100644 --- a/includes/class-convertkit-resource-creator-network-recommendations.php +++ b/includes/class-convertkit-resource-creator-network-recommendations.php @@ -76,7 +76,7 @@ public function enabled() { // Get script from API. $result = $this->api->recommendations_script(); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { delete_option( $this->settings_name ); return false; diff --git a/includes/class-convertkit-resource-forms.php b/includes/class-convertkit-resource-forms.php index 2a475adc6..966361277 100644 --- a/includes/class-convertkit-resource-forms.php +++ b/includes/class-convertkit-resource-forms.php @@ -71,7 +71,7 @@ public function refresh() { // Call parent refresh method. $result = parent::refresh(); - // If an error occured, maybe delete credentials from the Plugin's settings + // If an error occurred, maybe delete credentials from the Plugin's settings // if the error is a 401 unauthorized. if ( is_wp_error( $result ) ) { convertkit_maybe_delete_credentials( $result, CONVERTKIT_OAUTH_CLIENT_ID ); diff --git a/includes/class-convertkit-setup.php b/includes/class-convertkit-setup.php index 6d7ccbe97..4c65f94c5 100644 --- a/includes/class-convertkit-setup.php +++ b/includes/class-convertkit-setup.php @@ -621,7 +621,7 @@ private function maybe_get_access_token_by_api_key_and_secret() { get_site_url() ); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { return; } diff --git a/includes/class-convertkit-subscriber.php b/includes/class-convertkit-subscriber.php index bf170dae9..5f851000f 100644 --- a/includes/class-convertkit-subscriber.php +++ b/includes/class-convertkit-subscriber.php @@ -83,7 +83,7 @@ public function validate_and_store_subscriber_email( $subscriber_email ) { // Get subscriber by email, to ensure they exist. $subscriber_id = $api->get_subscriber_id( $subscriber_email ); - // Bail if no subscriber exists with the given subscriber ID, or an error occured. + // Bail if no subscriber exists with the given subscriber ID, or an error occurred. if ( is_wp_error( $subscriber_id ) ) { // Delete the cookie. $this->forget(); diff --git a/includes/cron-functions.php b/includes/cron-functions.php index f27e4d72b..c707d5615 100644 --- a/includes/cron-functions.php +++ b/includes/cron-functions.php @@ -37,7 +37,7 @@ function convertkit_refresh_token() { // Refresh the token. $result = $api->refresh_token(); - // If an error occured, don't save the new tokens. + // If an error occurred, don't save the new tokens. // Logging is handled by the ConvertKit_API_V4 class. if ( is_wp_error( $result ) ) { return; @@ -86,7 +86,7 @@ function convertkit_resource_refresh_posts() { $posts = new ConvertKit_Resource_Posts( 'cron' ); $result = $posts->refresh(); - // Bail if an error occured. + // Bail if an error occurred. if ( is_wp_error( $result ) ) { // Delete credentials if the error is a 401. convertkit_maybe_delete_credentials( $result, CONVERTKIT_OAUTH_CLIENT_ID ); diff --git a/includes/integrations/contactform7/class-convertkit-contactform7.php b/includes/integrations/contactform7/class-convertkit-contactform7.php index 7474ccd00..8fa87775a 100644 --- a/includes/integrations/contactform7/class-convertkit-contactform7.php +++ b/includes/integrations/contactform7/class-convertkit-contactform7.php @@ -151,7 +151,7 @@ public function handle_wpcf7_submit( $contact_form, $result ) { // Subscribe with inactive state. $subscriber = $api->create_subscriber( $email, $first_name, 'inactive' ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { return; } @@ -172,7 +172,7 @@ public function handle_wpcf7_submit( $contact_form, $result ) { // Subscribe. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { return; } @@ -187,7 +187,7 @@ public function handle_wpcf7_submit( $contact_form, $result ) { // Subscribe. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { return; } diff --git a/includes/integrations/forminator/class-convertkit-forminator.php b/includes/integrations/forminator/class-convertkit-forminator.php index 90156a8df..fcb84d557 100644 --- a/includes/integrations/forminator/class-convertkit-forminator.php +++ b/includes/integrations/forminator/class-convertkit-forminator.php @@ -170,7 +170,7 @@ public function maybe_subscribe( $entry, $form_id, $form_data_array ) { // Subscribe. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { return; } @@ -185,7 +185,7 @@ public function maybe_subscribe( $entry, $form_id, $form_data_array ) { // Subscribe. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { return; } diff --git a/includes/integrations/wishlist/class-convertkit-wishlist.php b/includes/integrations/wishlist/class-convertkit-wishlist.php index 4771495e6..7abc50d7c 100644 --- a/includes/integrations/wishlist/class-convertkit-wishlist.php +++ b/includes/integrations/wishlist/class-convertkit-wishlist.php @@ -141,7 +141,7 @@ private function manage_member( $member_id, $levels, $wlm_action = 'add' ) { // Get subscriber ID. $subscriber_id = $api->get_subscriber_id( $email ); - // Bail if an error occured e.g. no subscriber exists. + // Bail if an error occurred e.g. no subscriber exists. if ( is_wp_error( $subscriber_id ) ) { return $subscriber_id; } @@ -173,7 +173,7 @@ private function manage_member( $member_id, $levels, $wlm_action = 'add' ) { // Subscribe with inactive state. $subscriber = $api->create_subscriber( $email, $first_name, 'inactive' ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { break; } @@ -196,7 +196,7 @@ private function manage_member( $member_id, $levels, $wlm_action = 'add' ) { // Subscribe. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { break; } @@ -212,7 +212,7 @@ private function manage_member( $member_id, $levels, $wlm_action = 'add' ) { // Subscribe with inactive state. $subscriber = $api->create_subscriber( $email, $first_name ); - // If an error occured, don't attempt to add the subscriber to the Form, as it won't work. + // If an error occurred, don't attempt to add the subscriber to the Form, as it won't work. if ( is_wp_error( $subscriber ) ) { break; } @@ -237,7 +237,7 @@ public function get_member( $id ) { // Get WishList Member. $wlm_get_member = wlmapi_get_member( $id ); - // Bail if an error occured. + // Bail if an error occurred. if ( 0 === $wlm_get_member['success'] ) { return false; } diff --git a/wp-convertkit.php b/wp-convertkit.php index 2a69ac039..f3715ccbb 100644 --- a/wp-convertkit.php +++ b/wp-convertkit.php @@ -17,7 +17,7 @@ * License URI: https://www.gnu.org/licenses/gpl-3.0.html */ -// Bail if Kit is alread loaded. +// Bail if Kit is already loaded. if ( class_exists( 'WP_ConvertKit' ) ) { return; }