From c78ab111abd4aa834d0d4a7aeab0a5999dac9dfb Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Sat, 27 Jun 2026 00:20:12 +0200 Subject: [PATCH] Automate HTML API regression harness (CI + composer test) Wire the standalone regression harness into automation so it actually runs on every push and PR. - composer.json: add a `test` script that runs the regression harness then phpcs in one command, stopping (non-zero) on the first failure. Reuses the existing phpcs.xml ruleset, which the plugin source already passes clean. - .github/workflows/ci.yml: run `composer test` on push to main and on pull_request, across PHP 8.2-8.5 via shivammathur/setup-php. WordPress core sources are provided by a shallow checkout of WordPress/wordpress-develop pinned to tag 7.0.0 (bump deliberately). - tests/html-api-integration-regression.php: harden the bootstrap. Check every required HTML API file exists before requiring any of them, and bail with a clear STDERR message + exit(2) if the API class or the plugin integration function is still missing after bootstrap, instead of fataling on the first require. Claude-Session: https://claude.ai/code/session_014CpRWL5jrDEGKzzJkQdNyL --- .github/workflows/ci.yml | 53 +++++++++++++++++ composer.json | 13 +++++ tests/html-api-integration-regression.php | 71 ++++++++++++++++------- 3 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..82c418f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + test: + name: Test (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + + strategy: + # Run every PHP version even if one fails so we see the full matrix. + fail-fast: false + matrix: + # Floor is the lowest PHP still in security support (Requires PHP: 8.2); + # 8.5 is the current latest. Keep in sync with the plugin's Requires PHP. + php: ['8.2', '8.3', '8.4', '8.5'] + + steps: + - name: Checkout plugin + uses: actions/checkout@v4 + + # The regression harness needs WordPress core's HTML API sources. + # Pin to a specific tag for reproducible runs. Bump this DELIBERATELY + # (not via automation): 7.0.0 is the latest stable release as of 2026-06 + # (latest 6.x is 6.9.4). A shallow checkout keeps the clone small. + - name: Checkout WordPress source + uses: actions/checkout@v4 + with: + repository: WordPress/wordpress-develop + ref: '7.0.0' + path: wordpress-develop + fetch-depth: 1 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-progress + + - name: Run tests (regression harness + phpcs) + env: + WP_CORE_DIR: ${{ github.workspace }}/wordpress-develop/src + run: composer test diff --git a/composer.json b/composer.json index 883613d..d392ab5 100644 --- a/composer.json +++ b/composer.json @@ -8,5 +8,18 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } + }, + "scripts": { + "test": [ + "@test:harness", + "@test:phpcs" + ], + "test:harness": "php tests/html-api-integration-regression.php", + "test:phpcs": "phpcs" + }, + "scripts-descriptions": { + "test": "Run the HTML API regression harness and phpcs (requires WP_CORE_DIR for the harness).", + "test:harness": "Run the HTML API integration regression harness (requires WP_CORE_DIR).", + "test:phpcs": "Run phpcs against the plugin source using phpcs.xml." } } diff --git a/tests/html-api-integration-regression.php b/tests/html-api-integration-regression.php index 99dc939..88248b0 100644 --- a/tests/html-api-integration-regression.php +++ b/tests/html-api-integration-regression.php @@ -67,30 +67,61 @@ function do_action( $hook_name, ...$args ) { } if ( ! class_exists( 'WP_HTML_Processor' ) ) { - require "{$include_dir}/class-wp-token-map.php"; + $required_files = array( + "{$include_dir}/class-wp-token-map.php", + "{$include_dir}/html-api/class-wp-html-text-replacement.php", + "{$include_dir}/html-api/class-wp-html-span.php", + "{$include_dir}/html-api/class-wp-html-attribute-token.php", + "{$include_dir}/html-api/class-wp-html-doctype-info.php", + "{$include_dir}/html-api/class-wp-html-unsupported-exception.php", + "{$include_dir}/html-api/class-wp-html-decoder.php", + "{$include_dir}/html-api/class-wp-html-tag-processor.php", + "{$include_dir}/html-api/class-wp-html-token.php", + "{$include_dir}/html-api/class-wp-html-stack-event.php", + "{$include_dir}/html-api/class-wp-html-open-elements.php", + "{$include_dir}/html-api/class-wp-html-active-formatting-elements.php", + "{$include_dir}/html-api/class-wp-html-processor-state.php", + "{$include_dir}/html-api/class-wp-html-processor.php", + ); - foreach ( - array( - 'class-wp-html-text-replacement.php', - 'class-wp-html-span.php', - 'class-wp-html-attribute-token.php', - 'class-wp-html-doctype-info.php', - 'class-wp-html-unsupported-exception.php', - 'class-wp-html-decoder.php', - 'class-wp-html-tag-processor.php', - 'class-wp-html-token.php', - 'class-wp-html-stack-event.php', - 'class-wp-html-open-elements.php', - 'class-wp-html-active-formatting-elements.php', - 'class-wp-html-processor-state.php', - 'class-wp-html-processor.php', - ) as $file - ) { - require "{$include_dir}/html-api/{$file}"; + // Verify every dependency exists before requiring any of them so a moved or + // renamed file produces a clear message instead of a fatal mid-bootstrap. + $missing_files = array(); + foreach ( $required_files as $file ) { + if ( ! file_exists( $file ) ) { + $missing_files[] = $file; + } + } + + if ( $missing_files ) { + fwrite( STDERR, "Cannot bootstrap the HTML API from WP_CORE_DIR; the following required files are missing:\n" ); + fwrite( STDERR, ' - ' . implode( "\n - ", $missing_files ) . "\n" ); + fwrite( STDERR, "This WordPress version may be incompatible with the regression harness.\n" ); + exit( 2 ); + } + + foreach ( $required_files as $file ) { + require $file; } } -require dirname( __DIR__ ) . '/html-api-debugger/html-api-integration.php'; +// Bail clearly if the HTML API still is not available after bootstrap. +if ( ! class_exists( 'WP_HTML_Processor' ) ) { + fwrite( STDERR, "Bootstrap completed but WP_HTML_Processor is undefined; the HTML API may have changed in this WordPress version.\n" ); + exit( 2 ); +} + +$integration_file = dirname( __DIR__ ) . '/html-api-debugger/html-api-integration.php'; +if ( ! file_exists( $integration_file ) ) { + fwrite( STDERR, "Missing plugin integration file: {$integration_file}\n" ); + exit( 2 ); +} +require $integration_file; + +if ( ! function_exists( 'HTML_API_Debugger\HTML_API_Integration\get_tree' ) ) { + fwrite( STDERR, "Plugin integration loaded but HTML_API_Debugger\\HTML_API_Integration\\get_tree() is undefined.\n" ); + exit( 2 ); +} /** * Convert a debugger tree into stable text lines.