diff --git a/.github/workflows/phpunit-tests.yml b/.github/workflows/phpunit-tests.yml index d41c4f9412..c27aaefa3f 100644 --- a/.github/workflows/phpunit-tests.yml +++ b/.github/workflows/phpunit-tests.yml @@ -19,7 +19,6 @@ on: - cron: '0 0 * * 0' env: - LOCAL_DIR: build PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: ${{ true }} COMPOSER_INSTALL: ${{ false }} # Controls which NPM script to use for running PHPUnit tests. Options ar `php` and `php-composer`. @@ -27,20 +26,12 @@ env: LOCAL_PHP_MEMCACHED: ${{ false }} jobs: - # Sets up WordPress for testing or development use. + # Sets up the workflow for testing. # # Performs the following steps: # - Cancels all previous workflow runs for pull requests that have not completed. - # - Checks out the repository. - # - Logs debug information about the runner container. - # - Installs NodeJS 14. - # - Sets up caching for NPM. - # _ Installs NPM dependencies using install-changed to hash the `package.json` file. - # - Builds WordPress to run from the `build` directory. - # - Creates a ZIP file of compiled WordPress. - # - Uploads ZIP file as an artifact. - setup-wordpress: - name: Setup WordPress + setup-workflow: + name: Setup Workflow runs-on: ubuntu-latest if: ${{ github.repository == 'WordPress/wordpress-develop' || github.event_name == 'pull_request' }} @@ -51,67 +42,16 @@ jobs: with: access_token: ${{ github.token }} - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Log debug information - run: | - echo "$GITHUB_REF" - echo "$GITHUB_EVENT_NAME" - npm --version - node --version - curl --version - git --version - svn --version - php --version - php -i - locale -a - - - name: Install NodeJS - uses: actions/setup-node@v2 - with: - node-version: 14 - - - name: Cache NodeJS modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} - - - name: Install Dependencies - run: npx install-changed --install-command="npm ci" - - - name: Build WordPress - run: npm run build - - - name: Create ZIP artifact - uses: thedoctor0/zip-release@0.4.1 - with: - filename: built-wp-${{ github.sha }}.zip - exclusions: '*.git* /*node_modules/* packagehash.txt' - - - name: Upload build artifact - uses: actions/upload-artifact@v2 - with: - name: built-wp-${{ github.sha }} - path: built-wp-${{ github.sha }}.zip - if-no-files-found: error - # Runs the PHPUnit tests for WordPress. # # Performs the following steps: # - Set environment variables. # - Sets up the environment variables needed for testing with memcached (if desired). - # - Downloads the built WordPress artifact from the previous job. - # - Unzips the artifact. # - Installs NodeJS 14. # - Sets up caching for NPM. - # _ Installs NPM dependencies using install-changed to hash the `package.json` file. + # - Installs NPM dependencies # - Configures caching for Composer. - # _ Installs Composer dependencies (if desired). + # - Installs Composer dependencies (if desired). # - Logs Docker debug information (about both the Docker installation within the runner). # - Starts the WordPress Docker container. # - Starts the memcached server after the Docker network has been created (if desired). @@ -127,7 +67,6 @@ jobs: # - todo: Configure Slack notifications for failing tests. test-php: name: ${{ matrix.php }}${{ matrix.multisite && ' multisite' || '' }}${{ matrix.memcached && ' with memcached' || '' }} on ${{ matrix.os }} - needs: setup-wordpress runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -163,13 +102,8 @@ jobs: echo "PHP_FPM_UID=$(id -u)" >> $GITHUB_ENV echo "PHP_FPM_GID=$(id -g)" >> $GITHUB_ENV - - name: Download the built WordPress artifact - uses: actions/download-artifact@v2 - with: - name: built-wp-${{ github.sha }} - - - name: Unzip built artifact - run: unzip built-wp-${{ github.sha }}.zip + - name: Checkout repository + uses: actions/checkout@v2 - name: Install NodeJS uses: actions/setup-node@v2 @@ -230,7 +164,7 @@ jobs: - name: Start the Memcached server. if: ${{ matrix.memcached }} run: | - cp tests/phpunit/includes/object-cache.php build/wp-content/object-cache.php + cp tests/phpunit/includes/object-cache.php src/wp-content/object-cache.php docker run --name memcached --net $(basename "$PWD")_wpdevnet -d memcached - name: General debug information diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml index 8fe9054a49..79c453429f 100644 --- a/.github/workflows/test-coverage.yml +++ b/.github/workflows/test-coverage.yml @@ -6,7 +6,6 @@ on: - cron: '0 0 * * *' env: - LOCAL_DIR: build PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: ${{ true }} COMPOSER_INSTALL: ${{ false }} # Controls which NPM script to use for running PHPUnit tests. Options ar `php` and `php-composer`. @@ -26,7 +25,6 @@ jobs: # - Installs NodeJS 14. # - Sets up caching for NPM. # _ Installs NPM dependencies using install-changed to hash the `package.json` file. - # - Builds WordPress to run from the `build` directory. # - Logs Docker debug information (about the Docker installation within the runner). # - Starts the WordPress Docker container. # - Logs debug general information. @@ -85,9 +83,6 @@ jobs: - name: Install Dependencies run: npx install-changed --install-command="npm ci" - - name: Build WordPress - run: npm run build - - name: Docker debug information run: | docker -v diff --git a/Gruntfile.js b/Gruntfile.js index 06d9b4b298..c7a772e73d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,6 +7,8 @@ var installChanged = require( 'install-changed' ); module.exports = function(grunt) { var path = require('path'), fs = require( 'fs' ), + glob = require( 'glob' ), + assert = require( 'assert' ).strict, spawn = require( 'child_process' ).spawnSync, SOURCE_DIR = 'src/', BUILD_DIR = 'build/', @@ -1431,6 +1433,126 @@ module.exports = function(grunt) { 'copy:version', ] ); + /** + * Build verification tasks. + */ + grunt.registerTask( 'verify:build', [ + 'verify:wp-embed', + 'verify:old-files', + 'verify:source-maps', + ] ); + + /** + * Build assertions for wp-embed.min.js. + * + * @ticket 34698 + */ + grunt.registerTask( 'verify:wp-embed', function() { + const file = `${ BUILD_DIR }/wp-includes/js/wp-embed.min.js`; + + assert( + fs.existsSync( file ), + 'The build/wp-includes/js/wp-embed.min.js file does not exist.' + ); + + const contents = fs.readFileSync( file, { + encoding: 'utf8', + } ); + + assert( + contents.length > 0, + 'The build/wp-includes/js/wp-embed.min.js file must not be empty.' + ); + assert( + false === contents.includes( '&' ), + 'The build/wp-includes/js/wp-embed.min.js file must not contain ampersands.' + ); + } ); + + /** + * Build assertions to ensure no project files are inside `$_old_files` in the build directory. + * + * @ticket 36083 + */ + grunt.registerTask( 'verify:old-files', function() { + const file = `${ BUILD_DIR }wp-admin/includes/update-core.php`; + + assert( + fs.existsSync( file ), + 'The build/wp-admin/includes/update-core.php file does not exist.' + ); + + const contents = fs.readFileSync( file, { + encoding: 'utf8', + } ); + + assert( + contents.length > 0, + 'The build/wp-admin/includes/update-core.php file must not be empty.' + ); + + const match = contents.match( /\$_old_files = array\(([^\)]+)\);/ ); + + assert( + match.length > 0, + 'The build/wp-admin/includes/update-core.php file does not include an `$_old_files` array.' + ); + + const files = match[1].split( '\n\t' ).filter( function( file ) { + // Filter out empty lines + if ( '' === file ) { + return false; + } + + // Filter out commented out lines + if ( 0 === file.indexOf( '/' ) ) { + return false; + } + + return true; + } ).map( function( file ) { + // Strip leading and trailing single quotes and commas + return file.replace( /^\'|\',$/g, '' ); + } ); + + files.forEach(function( file ){ + const search = `${ BUILD_DIR }${ file }`; + assert( + false === fs.existsSync( search ), + `${ search } should not be present in the $_old_files array.` + ); + }); + } ); + + /** + * Build assertions for the lack of source maps in JavaScript files. + * + * @ticket 24994 + * @ticket 46218 + */ + grunt.registerTask( 'verify:source-maps', function() { + const path = `${ BUILD_DIR }**/*.js`; + const files = glob.sync( path ); + + assert( + files.length > 0, + 'No JavaScript files found in the build directory.' + ); + + files.forEach( function( file ) { + const contents = fs.readFileSync( file, { + encoding: 'utf8', + } ); + // `data:` URLs are allowed: + const match = contents.match( /sourceMappingURL=((?!data:).)/ ); + + assert( + match === null, + `The ${ file } file must not contain a sourceMappingURL.` + ); + } ); + } ); + grunt.registerTask( 'build', function() { if ( grunt.option( 'dev' ) ) { grunt.task.run( [ @@ -1444,7 +1566,8 @@ module.exports = function(grunt) { 'build:css', 'includes:emoji', 'includes:embed', - 'replace:emojiBannerText' + 'replace:emojiBannerText', + 'verify:build' ] ); } } ); diff --git a/README.md b/README.md index 48af3be754..952d037d37 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Your WordPress site will accessible at http://localhost:8889. You can see or cha If you're making changes to WordPress core files, you should start the file watcher in order to build or copy the files as necessary: ``` -npm run watch +npm run dev ``` To stop the watcher, press `ctrl+c`. diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 4dbbea959f..1c532b9372 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -1293,4 +1293,26 @@ abstract class WP_UnitTestCase_Base extends PHPUnit\Framework\TestCase { ) ); } + + /** + * Touches the given file and its directory if it doesn't already exist. + * + * This can be used to ensure a file that is implictly relied on in a test exists + * without it having to be built. + * + * @param string $file The file name. + */ + public static function touch( $file ) { + if ( file_exists( $file ) ) { + return; + } + + $dir = dirname( $file ); + + if ( ! file_exists( $dir ) ) { + mkdir( $dir, 0777, true ); + } + + touch( $file ); + } } diff --git a/tests/phpunit/tests/admin/includesUpdateCore.php b/tests/phpunit/tests/admin/includesUpdateCore.php deleted file mode 100644 index 67d750a442..0000000000 --- a/tests/phpunit/tests/admin/includesUpdateCore.php +++ /dev/null @@ -1,34 +0,0 @@ -assertFileNotExists( dirname( ABSPATH ) . '/build/' . $file ); - } -} diff --git a/tests/phpunit/tests/dependencies/jquery.php b/tests/phpunit/tests/dependencies/jquery.php index 33f3b6b9a0..082d0e6bd0 100644 --- a/tests/phpunit/tests/dependencies/jquery.php +++ b/tests/phpunit/tests/dependencies/jquery.php @@ -39,13 +39,6 @@ class Tests_Dependencies_jQuery extends WP_UnitTestCase { } } - function test_presence_of_jquery_no_conflict() { - $contents = trim( file_get_contents( ABSPATH . WPINC . '/js/jquery/jquery.js' ) ); - $noconflict = 'jQuery.noConflict();'; - $end = substr( $contents, - strlen( $noconflict ) ); - $this->assertSame( $noconflict, $end ); - } - /** * @ticket 22896 * diff --git a/tests/phpunit/tests/dependencies/mediaelementjs.php b/tests/phpunit/tests/dependencies/mediaelementjs.php deleted file mode 100644 index 38263f7581..0000000000 --- a/tests/phpunit/tests/dependencies/mediaelementjs.php +++ /dev/null @@ -1,36 +0,0 @@ -assertGreaterThan( 0, count( $js_files ) ); - - $mejs_directory_iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $mejs_folder ) ); - $mejs_swf_iterator = new RegexIterator( $mejs_directory_iterator, '/\.swf$/i', RecursiveRegexIterator::GET_MATCH ); - - // Make sure the Flash files haven't been re-added accidentally. - $this->assertCount( 0, iterator_to_array( $mejs_swf_iterator ) ); - } -} diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 838ac454d3..299c112f5f 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -728,7 +728,7 @@ JS; $expected .= "\n"; $expected .= "\n"; $expected .= "\n"; $expected .= "\n"; $expected .= "\n"; @@ -1417,18 +1417,6 @@ JS; ); } - function test_no_source_mapping() { - $all_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( dirname( ABSPATH ) . '/build/' ) ); - $js_files = new RegexIterator( $all_files, '/\.js$/' ); - foreach ( $js_files as $js_file ) { - $contents = trim( file_get_contents( $js_file ) ); - - // We allow data: URLs. - $found = preg_match( '/sourceMappingURL=((?!data:).)/', $contents ); - $this->assertSame( $found, 0, "sourceMappingURL found in $js_file" ); - } - } - /** * @ticket 52534 * @covers ::wp_localize_script diff --git a/tests/phpunit/tests/formatting/Emoji.php b/tests/phpunit/tests/formatting/Emoji.php index 8ddb5bc05b..6c8c5685f3 100644 --- a/tests/phpunit/tests/formatting/Emoji.php +++ b/tests/phpunit/tests/formatting/Emoji.php @@ -13,6 +13,8 @@ class Tests_Formatting_Emoji extends WP_UnitTestCase { * @ticket 36525 */ public function test_unfiltered_emoji_cdns() { + // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' ); $output = get_echo( '_print_emoji_detection_script' ); $this->assertContains( wp_json_encode( $this->png_cdn ), $output ); @@ -31,6 +33,8 @@ class Tests_Formatting_Emoji extends WP_UnitTestCase { add_filter( 'emoji_svg_url', array( $this, '_filtered_emoji_svn_cdn' ) ); + // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' ); $output = get_echo( '_print_emoji_detection_script' ); $this->assertContains( wp_json_encode( $this->png_cdn ), $output ); @@ -52,6 +56,8 @@ class Tests_Formatting_Emoji extends WP_UnitTestCase { add_filter( 'emoji_url', array( $this, '_filtered_emoji_png_cdn' ) ); + // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' ); $output = get_echo( '_print_emoji_detection_script' ); $this->assertContains( wp_json_encode( $filtered_png_cdn ), $output ); diff --git a/tests/phpunit/tests/oembed/controller.php b/tests/phpunit/tests/oembed/controller.php index 731955f31d..f39c74f5d0 100644 --- a/tests/phpunit/tests/oembed/controller.php +++ b/tests/phpunit/tests/oembed/controller.php @@ -34,6 +34,9 @@ class Test_oEmbed_Controller extends WP_UnitTestCase { 'user_email' => 'administrator@example.com', ) ); + + // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-embed.js' ); } public static function wpTearDownAfterClass() { diff --git a/tests/phpunit/tests/oembed/getResponseData.php b/tests/phpunit/tests/oembed/getResponseData.php index 8315d652a4..1c6547a6b7 100644 --- a/tests/phpunit/tests/oembed/getResponseData.php +++ b/tests/phpunit/tests/oembed/getResponseData.php @@ -5,6 +5,13 @@ * @covers ::get_oembed_response_data */ class Tests_oEmbed_Response_Data extends WP_UnitTestCase { + public function setUp() { + parent::setUp(); + + // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-embed.js' ); + } + function test_get_oembed_response_data_non_existent_post() { $this->assertFalse( get_oembed_response_data( 0, 100 ) ); } diff --git a/tests/phpunit/tests/oembed/template.php b/tests/phpunit/tests/oembed/template.php index 0eefb8f4c0..1577c53609 100644 --- a/tests/phpunit/tests/oembed/template.php +++ b/tests/phpunit/tests/oembed/template.php @@ -23,6 +23,9 @@ class Tests_Embed_Template extends WP_UnitTestCase { $this->assertQueryTrue( 'is_single', 'is_singular', 'is_embed' ); + // `print_embed_scripts()` assumes `wp-includes/js/wp-embed-template.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-embed-template.js' ); + ob_start(); require ABSPATH . WPINC . '/theme-compat/embed.php'; $actual = ob_get_clean(); @@ -290,50 +293,13 @@ class Tests_Embed_Template extends WP_UnitTestCase { } /** + * Confirms that no ampersands exist in src/wp-includes/js/wp-embed.js. + * + * See also the `verify:wp-embed` Grunt task for verifying the built file. + * * @ticket 34698 */ function test_js_no_ampersands() { $this->assertNotContains( '&', file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' ) ); } - - /** - * @ticket 34698 - * - * @depends test_js_no_ampersands - * - * The previous test confirms that no ampersands exist in src/wp-includes/js/wp-embed.js. - * However, we must also confirm that UglifyJS does not add ampersands during its - * optimizations (which we tweak to avoid, but indirectly -- understandably, there's - * no "don't add ampersands to my JavaScript file" option). - * - * So this test checks for ampersands in build/wp-includes/js/wp-embed.min.js. - * In many cases, this file will not exist; in those cases, we simply skip the test. - * - * So when would it be run? We have Travis CI run `npm run test` which then runs, in order, - * `qunit:compiled` (which runs the build) and then `phpunit`. Thus, this test will at least be - * run during continuous integration. - * - * However, we need to verify that `qunit:compiled` runs before `phpunit`. So this test also - * does a cheap check for a registered Grunt task called `test` that contains both - * `qunit:compiled` and `phpunit`, in that order. - * - * One final failsafe: The Gruntfile.js assertion takes place before checking for the existence - * of wp-embed.min.js. If the Grunt tasks are significantly refactored later, it could indicate - * that wp-embed.min.js doesn't exist anymore. We wouldn't want the test to silently become one - * that is always skipped, and thus useless. - */ - function test_js_no_ampersands_in_compiled() { - $gruntfile = file_get_contents( dirname( ABSPATH ) . '/Gruntfile.js' ); - - // Confirm this file *should* exist, otherwise this test will always be skipped. - $test = '/grunt.registerTask\(\s*\'test\',.*\'qunit:compiled\'.*\'phpunit\'/'; - $this->assertTrue( (bool) preg_match( $test, $gruntfile ) ); - - $file = dirname( ABSPATH ) . '/build/' . WPINC . '/js/wp-embed.min.js'; - if ( ! file_exists( $file ) ) { - return; - } - $this->assertNotContains( '&', file_get_contents( $file ) ); - } - } diff --git a/tests/phpunit/tests/oembed/wpOembed.php b/tests/phpunit/tests/oembed/wpOembed.php index 1f15abbad8..a2d767e373 100644 --- a/tests/phpunit/tests/oembed/wpOembed.php +++ b/tests/phpunit/tests/oembed/wpOembed.php @@ -18,6 +18,9 @@ class Tests_WP_oEmbed extends WP_UnitTestCase { $this->oembed = _wp_oembed_get_object(); $this->pre_oembed_result_filtered = false; + + // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present: + self::touch( ABSPATH . WPINC . '/js/wp-embed.js' ); } public function _filter_pre_oembed_result( $result ) { diff --git a/tests/phpunit/tests/shortcode.php b/tests/phpunit/tests/shortcode.php index 764c492764..ea8bb136fc 100644 --- a/tests/phpunit/tests/shortcode.php +++ b/tests/phpunit/tests/shortcode.php @@ -746,7 +746,7 @@ EOF; function test_php_and_js_shortcode_attribute_regexes_match() { - $file = file_get_contents( ABSPATH . WPINC . '/js/shortcode.js' ); + $file = file_get_contents( ABSPATH . 'js/_enqueues/wp/shortcode.js' ); $matched = preg_match( '|\s+pattern = (\/.+\/)g;|', $file, $matches ); $php = get_shortcode_atts_regex(); diff --git a/tests/qunit/index.html b/tests/qunit/index.html index ab6a9c0468..dd3fb68224 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -152,6 +152,7 @@ + diff --git a/tests/qunit/wp-includes/js/jquery.js b/tests/qunit/wp-includes/js/jquery.js new file mode 100644 index 0000000000..6c02e63238 --- /dev/null +++ b/tests/qunit/wp-includes/js/jquery.js @@ -0,0 +1,10 @@ +( function( QUnit ) { + QUnit.module( 'jQuery' ); + + QUnit.test( 'jQuery is run in noConflict mode', function( assert ) { + assert.expect( 1 ); + + assert.ok( 'undefined' === typeof window.$ ); + } ); + +} )( window.QUnit ); diff --git a/wp-tests-config-sample.php b/wp-tests-config-sample.php index 7e87d5f6d3..62636da344 100644 --- a/wp-tests-config-sample.php +++ b/wp-tests-config-sample.php @@ -1,11 +1,7 @@