WordPress.org

Make WordPress Core

Changeset 50441


Ignore:
Timestamp:
02/26/2021 02:07:53 PM (2 months ago)
Author:
johnbillion
Message:

Build/Test Tools: Switch back to running the PHPUnit test suite against the src directory instead of build.

Some PHPUnit tests were concerned with the state of files in the build directory. In order to allow the tests to run without requiring a build to be run first, these have been moved into assertions that run after the build step (and therefore cause it to fail if they do not pass), or into QUnit tests as necessary.

Various other PHPUnit tests implictly depend on built JavaScript files being present. These files are now touched during the test setup to avoid PHP warnings if the build files are not present.

The wp-tests-config-sample.php file and the GitHub Actions configuration have also been changed so ABSPATH uses src instead of build, therefore allowing the PHPUnit tests to be run without a build having to be run first. This means all new local installations of WordPress will use src for PHPUnit testing. If you would like to switch your existing installation over then change the location of ABSPATH in wp-tests-config.php to point to src instead of build.

Props peterwilsoncc, iandunn, gziolo, desroj, johnbillion

Fixes #51734
See #45863

Location:
trunk
Files:
1 added
2 deleted
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/.github/workflows/phpunit-tests.yml

    r50436 r50441  
    2020
    2121env:
    22   LOCAL_DIR: build
    2322  PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: ${{ true }}
    2423  COMPOSER_INSTALL: ${{ false }}
     
    2827
    2928jobs:
    30   # Sets up WordPress for testing or development use.
     29  # Sets up the workflow for testing.
    3130  #
    3231  # Performs the following steps:
    3332  # - Cancels all previous workflow runs for pull requests that have not completed.
    34   # - Checks out the repository.
    35   # - Logs debug information about the runner container.
    36   # - Installs NodeJS 14.
    37   # - Sets up caching for NPM.
    38   # _ Installs NPM dependencies using install-changed to hash the `package.json` file.
    39   # - Builds WordPress to run from the `build` directory.
    40   # - Creates a ZIP file of compiled WordPress.
    41   # - Uploads ZIP file as an artifact.
    42   setup-wordpress:
    43     name: Setup WordPress
     33  setup-workflow:
     34    name: Setup Workflow
    4435    runs-on: ubuntu-latest
    4536    if: ${{ github.repository == 'WordPress/wordpress-develop' || github.event_name == 'pull_request' }}
     
    5142        with:
    5243          access_token: ${{ github.token }}
    53 
    54       - name: Checkout repository
    55         uses: actions/checkout@v2
    56 
    57       - name: Log debug information
    58         run: |
    59           echo "$GITHUB_REF"
    60           echo "$GITHUB_EVENT_NAME"
    61           npm --version
    62           node --version
    63           curl --version
    64           git --version
    65           svn --version
    66           php --version
    67           php -i
    68           locale -a
    69 
    70       - name: Install NodeJS
    71         uses: actions/setup-node@v2
    72         with:
    73           node-version: 14
    74 
    75       - name: Cache NodeJS modules
    76         uses: actions/cache@v2
    77         env:
    78           cache-name: cache-node-modules
    79         with:
    80           # npm cache files are stored in `~/.npm` on Linux/macOS
    81           path: ~/.npm
    82           key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    83 
    84       - name: Install Dependencies
    85         run: npx install-changed --install-command="npm ci"
    86 
    87       - name: Build WordPress
    88         run: npm run build
    89 
    90       - name: Create ZIP artifact
    91         uses: thedoctor0/zip-release@0.4.1
    92         with:
    93           filename: built-wp-${{ github.sha }}.zip
    94           exclusions: '*.git* /*node_modules/* packagehash.txt'
    95 
    96       - name: Upload build artifact
    97         uses: actions/upload-artifact@v2
    98         with:
    99           name: built-wp-${{ github.sha }}
    100           path: built-wp-${{ github.sha }}.zip
    101           if-no-files-found: error
    10244
    10345  # Runs the PHPUnit tests for WordPress.
     
    10648  # - Set environment variables.
    10749  # - Sets up the environment variables needed for testing with memcached (if desired).
    108   # - Downloads the built WordPress artifact from the previous job.
    109   # - Unzips the artifact.
    11050  # - Installs NodeJS 14.
    11151  # - Sets up caching for NPM.
    112   # _ Installs NPM dependencies using install-changed to hash the `package.json` file.
     52  # - Installs NPM dependencies
    11353  # - Configures caching for Composer.
    114   # _ Installs Composer dependencies (if desired).
     54  # - Installs Composer dependencies (if desired).
    11555  # - Logs Docker debug information (about both the Docker installation within the runner).
    11656  # - Starts the WordPress Docker container.
     
    12868  test-php:
    12969    name: ${{ matrix.php }}${{ matrix.multisite && ' multisite' || '' }}${{ matrix.memcached && ' with memcached' || '' }} on ${{ matrix.os }}
    130     needs: setup-wordpress
    13170    runs-on: ${{ matrix.os }}
    13271    strategy:
     
    164103          echo "PHP_FPM_GID=$(id -g)" >> $GITHUB_ENV
    165104
    166       - name: Download the built WordPress artifact
    167         uses: actions/download-artifact@v2
    168         with:
    169           name: built-wp-${{ github.sha }}
    170 
    171       - name: Unzip built artifact
    172         run: unzip built-wp-${{ github.sha }}.zip
     105      - name: Checkout repository
     106        uses: actions/checkout@v2
    173107
    174108      - name: Install NodeJS
     
    231165        if: ${{ matrix.memcached }}
    232166        run: |
    233           cp tests/phpunit/includes/object-cache.php build/wp-content/object-cache.php
     167          cp tests/phpunit/includes/object-cache.php src/wp-content/object-cache.php
    234168          docker run --name memcached --net $(basename "$PWD")_wpdevnet -d memcached
    235169
  • trunk/.github/workflows/test-coverage.yml

    r50436 r50441  
    77
    88env:
    9   LOCAL_DIR: build
    109  PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: ${{ true }}
    1110  COMPOSER_INSTALL: ${{ false }}
     
    2726  # - Sets up caching for NPM.
    2827  # _ Installs NPM dependencies using install-changed to hash the `package.json` file.
    29   # - Builds WordPress to run from the `build` directory.
    3028  # - Logs Docker debug information (about the Docker installation within the runner).
    3129  # - Starts the WordPress Docker container.
     
    8684        run: npx install-changed --install-command="npm ci"
    8785
    88       - name: Build WordPress
    89         run: npm run build
    90 
    9186      - name: Docker debug information
    9287        run: |
  • trunk/Gruntfile.js

    r50126 r50441  
    88    var path = require('path'),
    99        fs = require( 'fs' ),
     10        glob = require( 'glob' ),
     11        assert = require( 'assert' ).strict,
    1012        spawn = require( 'child_process' ).spawnSync,
    1113        SOURCE_DIR = 'src/',
     
    14321434    ] );
    14331435
     1436    /**
     1437     * Build verification tasks.
     1438     */
     1439    grunt.registerTask( 'verify:build', [
     1440        'verify:wp-embed',
     1441        'verify:old-files',
     1442        'verify:source-maps',
     1443    ] );
     1444
     1445    /**
     1446     * Build assertions for wp-embed.min.js.
     1447     *
     1448     * @ticket 34698
     1449     */
     1450    grunt.registerTask( 'verify:wp-embed', function() {
     1451        const file = `${ BUILD_DIR }/wp-includes/js/wp-embed.min.js`;
     1452
     1453        assert(
     1454            fs.existsSync( file ),
     1455            'The build/wp-includes/js/wp-embed.min.js file does not exist.'
     1456        );
     1457
     1458        const contents = fs.readFileSync( file, {
     1459            encoding: 'utf8',
     1460        } );
     1461
     1462        assert(
     1463            contents.length > 0,
     1464            'The build/wp-includes/js/wp-embed.min.js file must not be empty.'
     1465        );
     1466        assert(
     1467            false === contents.includes( '&' ),
     1468            'The build/wp-includes/js/wp-embed.min.js file must not contain ampersands.'
     1469        );
     1470    } );
     1471
     1472    /**
     1473     * Build assertions to ensure no project files are inside `$_old_files` in the build directory.
     1474     *
     1475     * @ticket 36083
     1476     */
     1477    grunt.registerTask( 'verify:old-files', function() {
     1478        const file = `${ BUILD_DIR }wp-admin/includes/update-core.php`;
     1479
     1480        assert(
     1481            fs.existsSync( file ),
     1482            'The build/wp-admin/includes/update-core.php file does not exist.'
     1483        );
     1484
     1485        const contents = fs.readFileSync( file, {
     1486            encoding: 'utf8',
     1487        } );
     1488
     1489        assert(
     1490            contents.length > 0,
     1491            'The build/wp-admin/includes/update-core.php file must not be empty.'
     1492        );
     1493
     1494        const match = contents.match( /\$_old_files = array\(([^\)]+)\);/ );
     1495
     1496        assert(
     1497            match.length > 0,
     1498            'The build/wp-admin/includes/update-core.php file does not include an `$_old_files` array.'
     1499        );
     1500
     1501        const files = match[1].split( '\n\t' ).filter( function( file ) {
     1502            // Filter out empty lines
     1503            if ( '' === file ) {
     1504                return false;
     1505            }
     1506
     1507            // Filter out commented out lines
     1508            if ( 0 === file.indexOf( '/' ) ) {
     1509                return false;
     1510            }
     1511
     1512            return true;
     1513        } ).map( function( file ) {
     1514            // Strip leading and trailing single quotes and commas
     1515            return file.replace( /^\'|\',$/g, '' );
     1516        } );
     1517
     1518        files.forEach(function( file ){
     1519            const search = `${ BUILD_DIR }${ file }`;
     1520            assert(
     1521                false === fs.existsSync( search ),
     1522                `${ search } should not be present in the $_old_files array.`
     1523            );
     1524        });
     1525    } );
     1526
     1527    /**
     1528     * Build assertions for the lack of source maps in JavaScript files.
     1529     *
     1530     * @ticket 24994
     1531     * @ticket 46218
     1532     */
     1533    grunt.registerTask( 'verify:source-maps', function() {
     1534        const path = `${ BUILD_DIR }**/*.js`;
     1535        const files = glob.sync( path );
     1536
     1537        assert(
     1538            files.length > 0,
     1539            'No JavaScript files found in the build directory.'
     1540        );
     1541
     1542        files.forEach( function( file ) {
     1543            const contents = fs.readFileSync( file, {
     1544                encoding: 'utf8',
     1545            } );
     1546            // `data:` URLs are allowed:
     1547            const match = contents.match( /sourceMappingURL=((?!data:).)/ );
     1548
     1549            assert(
     1550                match === null,
     1551                `The ${ file } file must not contain a sourceMappingURL.`
     1552            );
     1553        } );
     1554    } );
     1555
    14341556    grunt.registerTask( 'build', function() {
    14351557        if ( grunt.option( 'dev' ) ) {
     
    14451567                'includes:emoji',
    14461568                'includes:embed',
    1447                 'replace:emojiBannerText'
     1569                'replace:emojiBannerText',
     1570                'verify:build'
    14481571            ] );
    14491572        }
  • trunk/README.md

    r49957 r50441  
    4646
    4747```
    48 npm run watch
     48npm run dev
    4949```
    5050
  • trunk/tests/phpunit/includes/abstract-testcase.php

    r49916 r50441  
    12941294        );
    12951295    }
     1296
     1297    /**
     1298     * Touches the given file and its directory if it doesn't already exist.
     1299     *
     1300     * This can be used to ensure a file that is implictly relied on in a test exists
     1301     * without it having to be built.
     1302     *
     1303     * @param string $file The file name.
     1304     */
     1305    public static function touch( $file ) {
     1306        if ( file_exists( $file ) ) {
     1307            return;
     1308        }
     1309
     1310        $dir = dirname( $file );
     1311
     1312        if ( ! file_exists( $dir ) ) {
     1313            mkdir( $dir, 0777, true );
     1314        }
     1315
     1316        touch( $file );
     1317    }
    12961318}
  • trunk/tests/phpunit/tests/dependencies/jquery.php

    r50287 r50441  
    3838            $this->assertSame( $jquery_scripts[ $dep ], $o->src );
    3939        }
    40     }
    41 
    42     function test_presence_of_jquery_no_conflict() {
    43         $contents   = trim( file_get_contents( ABSPATH . WPINC . '/js/jquery/jquery.js' ) );
    44         $noconflict = 'jQuery.noConflict();';
    45         $end        = substr( $contents, - strlen( $noconflict ) );
    46         $this->assertSame( $noconflict, $end );
    4740    }
    4841
  • trunk/tests/phpunit/tests/dependencies/scripts.php

    r50408 r50441  
    729729        $expected .= "<script type='text/javascript' src='/wp-includes/js/dist/vendor/wp-polyfill{$suffix}.js' id='wp-polyfill-js'></script>\n";
    730730        $expected .= "<script type='text/javascript' id='wp-polyfill-js-after'>\n";
    731         $expected .= "( 'fetch' in window ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-fetch{$suffix}.js\"></scr' + 'ipt>' );( document.contains ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-node-contains{$suffix}.js\"></scr' + 'ipt>' );( window.DOMRect ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-dom-rect{$suffix}.js\"></scr' + 'ipt>' );( window.URL && window.URL.prototype && window.URLSearchParams ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-url{$suffix}.js\"></scr' + 'ipt>' );( window.FormData && window.FormData.prototype.keys ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-formdata{$suffix}.js\"></scr' + 'ipt>' );( Element.prototype.matches && Element.prototype.closest ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-element-closest{$suffix}.js\"></scr' + 'ipt>' );( 'objectFit' in document.documentElement.style ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-object-fit.min.js\"></scr' + 'ipt>' );\n";
     731        $expected .= "( 'fetch' in window ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-fetch{$suffix}.js\"></scr' + 'ipt>' );( document.contains ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-node-contains{$suffix}.js\"></scr' + 'ipt>' );( window.DOMRect ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-dom-rect{$suffix}.js\"></scr' + 'ipt>' );( window.URL && window.URL.prototype && window.URLSearchParams ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-url{$suffix}.js\"></scr' + 'ipt>' );( window.FormData && window.FormData.prototype.keys ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-formdata{$suffix}.js\"></scr' + 'ipt>' );( Element.prototype.matches && Element.prototype.closest ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-element-closest{$suffix}.js\"></scr' + 'ipt>' );( 'objectFit' in document.documentElement.style ) || document.write( '<script src=\"http://example.org/wp-includes/js/dist/vendor/wp-polyfill-object-fit{$suffix}.js\"></scr' + 'ipt>' );\n";
    732732        $expected .= "</script>\n";
    733733        $expected .= "<script type='text/javascript' src='/wp-includes/js/dist/dom-ready{$suffix}.js' id='wp-dom-ready-js'></script>\n";
     
    14181418    }
    14191419
    1420     function test_no_source_mapping() {
    1421         $all_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( dirname( ABSPATH ) . '/build/' ) );
    1422         $js_files  = new RegexIterator( $all_files, '/\.js$/' );
    1423         foreach ( $js_files as $js_file ) {
    1424             $contents = trim( file_get_contents( $js_file ) );
    1425 
    1426             // We allow data: URLs.
    1427             $found = preg_match( '/sourceMappingURL=((?!data:).)/', $contents );
    1428             $this->assertSame( $found, 0, "sourceMappingURL found in $js_file" );
    1429         }
    1430     }
    1431 
    14321420    /**
    14331421     * @ticket 52534
  • trunk/tests/phpunit/tests/formatting/Emoji.php

    r49239 r50441  
    1414     */
    1515    public function test_unfiltered_emoji_cdns() {
     16        // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present:
     17        self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' );
    1618        $output = get_echo( '_print_emoji_detection_script' );
    1719
     
    3234        add_filter( 'emoji_svg_url', array( $this, '_filtered_emoji_svn_cdn' ) );
    3335
     36        // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present:
     37        self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' );
    3438        $output = get_echo( '_print_emoji_detection_script' );
    3539
     
    5357        add_filter( 'emoji_url', array( $this, '_filtered_emoji_png_cdn' ) );
    5458
     59        // `_print_emoji_detection_script()` assumes `wp-includes/js/wp-emoji-loader.js` is present:
     60        self::touch( ABSPATH . WPINC . '/js/wp-emoji-loader.js' );
    5561        $output = get_echo( '_print_emoji_detection_script' );
    5662
  • trunk/tests/phpunit/tests/oembed/controller.php

    r49603 r50441  
    3535            )
    3636        );
     37
     38        // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present:
     39        self::touch( ABSPATH . WPINC . '/js/wp-embed.js' );
    3740    }
    3841
  • trunk/tests/phpunit/tests/oembed/getResponseData.php

    r50401 r50441  
    66 */
    77class Tests_oEmbed_Response_Data extends WP_UnitTestCase {
     8    public function setUp() {
     9        parent::setUp();
     10
     11        // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present:
     12        self::touch( ABSPATH . WPINC . '/js/wp-embed.js' );
     13    }
     14
    815    function test_get_oembed_response_data_non_existent_post() {
    916        $this->assertFalse( get_oembed_response_data( 0, 100 ) );
  • trunk/tests/phpunit/tests/oembed/template.php

    r48937 r50441  
    2323
    2424        $this->assertQueryTrue( 'is_single', 'is_singular', 'is_embed' );
     25
     26        // `print_embed_scripts()` assumes `wp-includes/js/wp-embed-template.js` is present:
     27        self::touch( ABSPATH . WPINC . '/js/wp-embed-template.js' );
    2528
    2629        ob_start();
     
    291294
    292295    /**
     296     * Confirms that no ampersands exist in src/wp-includes/js/wp-embed.js.
     297     *
     298     * See also the `verify:wp-embed` Grunt task for verifying the built file.
     299     *
    293300     * @ticket 34698
    294301     */
     
    296303        $this->assertNotContains( '&', file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' ) );
    297304    }
    298 
    299     /**
    300      * @ticket 34698
    301      *
    302      * @depends test_js_no_ampersands
    303      *
    304      * The previous test confirms that no ampersands exist in src/wp-includes/js/wp-embed.js.
    305      * However, we must also confirm that UglifyJS does not add ampersands during its
    306      * optimizations (which we tweak to avoid, but indirectly -- understandably, there's
    307      * no "don't add ampersands to my JavaScript file" option).
    308      *
    309      * So this test checks for ampersands in build/wp-includes/js/wp-embed.min.js.
    310      * In many cases, this file will not exist; in those cases, we simply skip the test.
    311      *
    312      * So when would it be run? We have Travis CI run `npm run test` which then runs, in order,
    313      * `qunit:compiled` (which runs the build) and then `phpunit`. Thus, this test will at least be
    314      * run during continuous integration.
    315      *
    316      * However, we need to verify that `qunit:compiled` runs before `phpunit`. So this test also
    317      * does a cheap check for a registered Grunt task called `test` that contains both
    318      * `qunit:compiled` and `phpunit`, in that order.
    319      *
    320      * One final failsafe: The Gruntfile.js assertion takes place before checking for the existence
    321      * of wp-embed.min.js. If the Grunt tasks are significantly refactored later, it could indicate
    322      * that wp-embed.min.js doesn't exist anymore. We wouldn't want the test to silently become one
    323      * that is always skipped, and thus useless.
    324      */
    325     function test_js_no_ampersands_in_compiled() {
    326         $gruntfile = file_get_contents( dirname( ABSPATH ) . '/Gruntfile.js' );
    327 
    328         // Confirm this file *should* exist, otherwise this test will always be skipped.
    329         $test = '/grunt.registerTask\(\s*\'test\',.*\'qunit:compiled\'.*\'phpunit\'/';
    330         $this->assertTrue( (bool) preg_match( $test, $gruntfile ) );
    331 
    332         $file = dirname( ABSPATH ) . '/build/' . WPINC . '/js/wp-embed.min.js';
    333         if ( ! file_exists( $file ) ) {
    334             return;
    335         }
    336         $this->assertNotContains( '&', file_get_contents( $file ) );
    337     }
    338 
    339305}
  • trunk/tests/phpunit/tests/oembed/wpOembed.php

    r48937 r50441  
    1919
    2020        $this->pre_oembed_result_filtered = false;
     21
     22        // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present:
     23        self::touch( ABSPATH . WPINC . '/js/wp-embed.js' );
    2124    }
    2225
  • trunk/tests/phpunit/tests/shortcode.php

    r49117 r50441  
    747747    function test_php_and_js_shortcode_attribute_regexes_match() {
    748748
    749         $file    = file_get_contents( ABSPATH . WPINC . '/js/shortcode.js' );
     749        $file    = file_get_contents( ABSPATH . 'js/_enqueues/wp/shortcode.js' );
    750750        $matched = preg_match( '|\s+pattern = (\/.+\/)g;|', $file, $matches );
    751751        $php     = get_shortcode_atts_regex();
  • trunk/tests/qunit/index.html

    r50137 r50441  
    153153        <script src="wp-includes/js/shortcode.js"></script>
    154154        <script src="wp-includes/js/api-request.js"></script>
     155        <script src="wp-includes/js/jquery.js"></script>
    155156        <script src="wp-includes/js/wp-api.js"></script>
    156157        <script src="wp-admin/js/customize-controls.js"></script>
  • trunk/wp-tests-config-sample.php

    r47201 r50441  
    22
    33/* Path to the WordPress codebase you'd like to test. Add a forward slash in the end. */
    4 if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
    5     define( 'ABSPATH', dirname( __FILE__ ) . '/build/' );
    6 } else {
    7     define( 'ABSPATH', dirname( __FILE__ ) . '/src/' );
    8 }
     4define( 'ABSPATH', dirname( __FILE__ ) . '/src/' );
    95
    106/*
Note: See TracChangeset for help on using the changeset viewer.