Make WordPress Core


Ignore:
Timestamp:
06/26/2023 01:40:31 PM (11 months ago)
Author:
joemcgill
Message:

Script Loader: Add support for HTML 5 "async" and "defer" attributes.

This allows developers to register scripts with an intended loading strategy by changing the $in_footer parameter of wp_register_script and wp_enqueue_script to an array that accepts both an in_footer and strategy argument. If present, the loading strategy attribute will be added to the script tag when that script is printed to the page as long as it is not a dependency of any blocking scripts, including any inline scripts attached to the script or any of its dependents.

Props 10upsimon, thekt12, westonruter, costdev, flixos90, spacedmonkey, adamsilverstein, azaozz, mukeshpanchal27, mor10, scep, wpnook, vanaf1979, Otto42.
Fixes #12009.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/dependencies/scripts.php

    r55822 r56033  
    1111 */
    1212class Tests_Dependencies_Scripts extends WP_UnitTestCase {
     13
     14    /**
     15     * @var WP_Scripts
     16     */
    1317    protected $old_wp_scripts;
    1418
     19    /**
     20     * @var WP_Styles
     21     */
     22    protected $old_wp_styles;
     23
    1524    protected $wp_scripts_print_translations_output;
     25
     26    /**
     27     * Stores a string reference to a default scripts directory name, utilised by certain tests.
     28     *
     29     * @var string
     30     */
     31    protected $default_scripts_dir = '/directory/';
    1632
    1733    public function set_up() {
    1834        parent::set_up();
    1935        $this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null;
     36        $this->old_wp_styles  = isset( $GLOBALS['wp_styles'] ) ? $GLOBALS['wp_styles'] : null;
    2037        remove_action( 'wp_default_scripts', 'wp_default_scripts' );
    2138        remove_action( 'wp_default_scripts', 'wp_default_packages' );
    2239        $GLOBALS['wp_scripts']                  = new WP_Scripts();
    2340        $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
     41        $GLOBALS['wp_styles']                   = new WP_Styles();
    2442
    2543        $this->wp_scripts_print_translations_output  = <<<JS
     
    3755    public function tear_down() {
    3856        $GLOBALS['wp_scripts'] = $this->old_wp_scripts;
     57        $GLOBALS['wp_styles']  = $this->old_wp_styles;
    3958        add_action( 'wp_default_scripts', 'wp_default_scripts' );
    4059        parent::tear_down();
     
    4766     */
    4867    public function test_wp_enqueue_script() {
     68        global $wp_version;
     69
    4970        wp_enqueue_script( 'no-deps-no-version', 'example.com', array() );
    5071        wp_enqueue_script( 'empty-deps-no-version', 'example.com' );
     
    5273        wp_enqueue_script( 'empty-deps-null-version', 'example.com', array(), null );
    5374
    54         $ver       = get_bloginfo( 'version' );
    55         $expected  = "<script type='text/javascript' src='http://example.com?ver=$ver' id='no-deps-no-version-js'></script>\n";
    56         $expected .= "<script type='text/javascript' src='http://example.com?ver=$ver' id='empty-deps-no-version-js'></script>\n";
     75        $expected  = "<script type='text/javascript' src='http://example.com?ver={$wp_version}' id='no-deps-no-version-js'></script>\n";
     76        $expected .= "<script type='text/javascript' src='http://example.com?ver={$wp_version}' id='empty-deps-no-version-js'></script>\n";
    5777        $expected .= "<script type='text/javascript' src='http://example.com?ver=1.2' id='empty-deps-version-js'></script>\n";
    5878        $expected .= "<script type='text/javascript' src='http://example.com' id='empty-deps-null-version-js'></script>\n";
     
    6585
    6686    /**
     87     * Gets delayed strategies as a data provider.
     88     *
     89     * @return array[] Delayed strategies.
     90     */
     91    public function data_provider_delayed_strategies() {
     92        return array(
     93            'defer' => array( 'defer' ),
     94            'async' => array( 'async' ),
     95        );
     96    }
     97
     98    /**
     99     * Tests that inline scripts in the `after` position, attached to delayed main scripts, remain unaffected.
     100     *
     101     * If the main script with delayed loading strategy has an `after` inline script,
     102     * the inline script should not be affected.
     103     *
     104     * @ticket 12009
     105     *
     106     * @covers WP_Scripts::do_item
     107     * @covers WP_Scripts::get_inline_script_tag
     108     * @covers ::wp_add_inline_script
     109     * @covers ::wp_enqueue_script
     110     *
     111     * @dataProvider data_provider_delayed_strategies
     112     *
     113     * @param string $strategy Strategy.
     114     */
     115    public function test_after_inline_script_with_delayed_main_script( $strategy ) {
     116        wp_enqueue_script( 'ms-isa-1', 'http://example.org/ms-isa-1.js', array(), null, compact( 'strategy' ) );
     117        wp_add_inline_script( 'ms-isa-1', 'console.log("after one");', 'after' );
     118        $output    = get_echo( 'wp_print_scripts' );
     119        $expected  = "<script type='text/javascript' src='http://example.org/ms-isa-1.js' id='ms-isa-1-js' data-wp-strategy='{$strategy}'></script>\n";
     120        $expected .= wp_get_inline_script_tag(
     121            "console.log(\"after one\");\n",
     122            array(
     123                'id' => 'ms-isa-1-js-after',
     124            )
     125        );
     126        $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a deferred main script, are failing to print/execute.' );
     127    }
     128
     129    /**
     130     * Tests that inline scripts in the `after` position, attached to a blocking main script, are rendered as javascript.
     131     *
     132     * If a main script with a `blocking` strategy has an `after` inline script,
     133     * the inline script should be rendered as type='text/javascript'.
     134     *
     135     * @ticket 12009
     136     *
     137     * @covers WP_Scripts::do_item
     138     * @covers WP_Scripts::get_inline_script_tag
     139     * @covers ::wp_add_inline_script
     140     * @covers ::wp_enqueue_script
     141     */
     142    public function test_after_inline_script_with_blocking_main_script() {
     143        wp_enqueue_script( 'ms-insa-3', 'http://example.org/ms-insa-3.js', array(), null );
     144        wp_add_inline_script( 'ms-insa-3', 'console.log("after one");', 'after' );
     145        $output = get_echo( 'wp_print_scripts' );
     146
     147        $expected  = "<script type='text/javascript' src='http://example.org/ms-insa-3.js' id='ms-insa-3-js'></script>\n";
     148        $expected .= wp_get_inline_script_tag(
     149            "console.log(\"after one\");\n",
     150            array(
     151                'id' => 'ms-insa-3-js-after',
     152            )
     153        );
     154
     155        $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a blocking main script, are failing to print/execute.' );
     156    }
     157
     158    /**
     159     * Tests that inline scripts in the `before` position, attached to a delayed inline main script, results in all
     160     * dependents being delayed.
     161     *
     162     * @ticket 12009
     163     *
     164     * @covers WP_Scripts::do_item
     165     * @covers WP_Scripts::get_inline_script_tag
     166     * @covers ::wp_add_inline_script
     167     * @covers ::wp_enqueue_script
     168     *
     169     * @dataProvider data_provider_delayed_strategies
     170     *
     171     * @param string $strategy
     172     */
     173    public function test_before_inline_scripts_with_delayed_main_script( $strategy ) {
     174        wp_enqueue_script( 'ds-i1-1', 'http://example.org/ds-i1-1.js', array(), null, compact( 'strategy' ) );
     175        wp_add_inline_script( 'ds-i1-1', 'console.log("before first");', 'before' );
     176        wp_enqueue_script( 'ds-i1-2', 'http://example.org/ds-i1-2.js', array(), null, compact( 'strategy' ) );
     177        wp_enqueue_script( 'ds-i1-3', 'http://example.org/ds-i1-3.js', array(), null, compact( 'strategy' ) );
     178        wp_enqueue_script( 'ms-i1-1', 'http://example.org/ms-i1-1.js', array( 'ds-i1-1', 'ds-i1-2', 'ds-i1-3' ), null, compact( 'strategy' ) );
     179        wp_add_inline_script( 'ms-i1-1', 'console.log("before last");', 'before' );
     180        $output = get_echo( 'wp_print_scripts' );
     181
     182        $expected  = wp_get_inline_script_tag(
     183            "console.log(\"before first\");\n",
     184            array(
     185                'id' => 'ds-i1-1-js-before',
     186            )
     187        );
     188        $expected .= "<script type='text/javascript' src='http://example.org/ds-i1-1.js' id='ds-i1-1-js' $strategy data-wp-strategy='{$strategy}'></script>\n";
     189        $expected .= "<script type='text/javascript' src='http://example.org/ds-i1-2.js' id='ds-i1-2-js' $strategy data-wp-strategy='{$strategy}'></script>\n";
     190        $expected .= "<script type='text/javascript' src='http://example.org/ds-i1-3.js' id='ds-i1-3-js' $strategy data-wp-strategy='{$strategy}'></script>\n";
     191        $expected .= wp_get_inline_script_tag(
     192            "console.log(\"before last\");\n",
     193            array(
     194                'id'   => 'ms-i1-1-js-before',
     195                'type' => 'text/javascript',
     196            )
     197        );
     198        $expected .= "<script type='text/javascript' src='http://example.org/ms-i1-1.js' id='ms-i1-1-js' {$strategy} data-wp-strategy='{$strategy}'></script>\n";
     199
     200        $this->assertSame( $expected, $output, 'Inline scripts in the "before" position, that are attached to a deferred main script, are failing to print/execute.' );
     201    }
     202
     203    /**
     204     * Tests that scripts registered with an async strategy print with the async attribute.
     205     *
     206     * @ticket 12009
     207     *
     208     * @covers WP_Scripts::do_item
     209     * @covers WP_Scripts::get_eligible_loading_strategy
     210     * @covers WP_Scripts::filter_eligible_strategies
     211     * @covers ::wp_enqueue_script
     212     */
     213    public function test_loading_strategy_with_valid_async_registration() {
     214        // No dependents, No dependencies then async.
     215        wp_enqueue_script( 'main-script-a1', '/main-script-a1.js', array(), null, array( 'strategy' => 'async' ) );
     216        $output   = get_echo( 'wp_print_scripts' );
     217        $expected = "<script type='text/javascript' src='/main-script-a1.js' id='main-script-a1-js' async data-wp-strategy='async'></script>\n";
     218        $this->assertSame( $expected, $output, 'Scripts enqueued with an async loading strategy are failing to have the async attribute applied to the script handle when being printed.' );
     219    }
     220
     221    /**
     222     * Tests that dependents of a blocking dependency script are free to contain any strategy.
     223     *
     224     * @ticket 12009
     225     *
     226     * @covers WP_Scripts::do_item
     227     * @covers WP_Scripts::get_eligible_loading_strategy
     228     * @covers WP_Scripts::filter_eligible_strategies
     229     * @covers ::wp_enqueue_script
     230     *
     231     * @dataProvider data_provider_delayed_strategies
     232     *
     233     * @param string $strategy Strategy.
     234     */
     235    public function test_delayed_dependent_with_blocking_dependency( $strategy ) {
     236        wp_enqueue_script( 'dependency-script-a2', '/dependency-script-a2.js', array(), null );
     237        wp_enqueue_script( 'main-script-a2', '/main-script-a2.js', array( 'dependency-script-a2' ), null, compact( 'strategy' ) );
     238        $output   = get_echo( 'wp_print_scripts' );
     239        $expected = "<script type='text/javascript' src='/main-script-a2.js' id='main-script-a2-js' {$strategy} data-wp-strategy='{$strategy}'></script>";
     240        $this->assertStringContainsString( $expected, $output, 'Dependents of a blocking dependency are free to have any strategy.' );
     241    }
     242
     243    /**
     244     * Tests that blocking dependents force delayed dependencies to become blocking.
     245     *
     246     * @ticket 12009
     247     *
     248     * @covers WP_Scripts::do_item
     249     * @covers WP_Scripts::get_eligible_loading_strategy
     250     * @covers WP_Scripts::filter_eligible_strategies
     251     * @covers ::wp_enqueue_script
     252     *
     253     * @dataProvider data_provider_delayed_strategies
     254     * @param string $strategy Strategy.
     255     */
     256    public function test_blocking_dependent_with_delayed_dependency( $strategy ) {
     257        wp_enqueue_script( 'main-script-a3', '/main-script-a3.js', array(), null, compact( 'strategy' ) );
     258        wp_enqueue_script( 'dependent-script-a3', '/dependent-script-a3.js', array( 'main-script-a3' ), null );
     259        $output   = get_echo( 'wp_print_scripts' );
     260        $expected = "<script type='text/javascript' src='/main-script-a3.js' id='main-script-a3-js' data-wp-strategy='{$strategy}'></script>";
     261        $this->assertStringContainsString( $expected, $output, 'Blocking dependents must force delayed dependencies to become blocking.' );
     262    }
     263
     264    /**
     265     * Tests that only enqueued dependents effect the eligible loading strategy.
     266     *
     267     * @ticket 12009
     268     *
     269     * @covers WP_Scripts::do_item
     270     * @covers WP_Scripts::get_eligible_loading_strategy
     271     * @covers WP_Scripts::filter_eligible_strategies
     272     * @covers ::wp_enqueue_script
     273     *
     274     * @dataProvider data_provider_delayed_strategies
     275     * @param string $strategy Strategy.
     276     */
     277    public function test_delayed_dependent_with_blocking_dependency_not_enqueued( $strategy ) {
     278        wp_enqueue_script( 'main-script-a4', '/main-script-a4.js', array(), null, compact( 'strategy' ) );
     279        // This dependent is registered but not enqueued, so it should not factor into the eligible loading strategy.
     280        wp_register_script( 'dependent-script-a4', '/dependent-script-a4.js', array( 'main-script-a4' ), null );
     281        $output   = get_echo( 'wp_print_scripts' );
     282        $expected = "<script type='text/javascript' src='/main-script-a4.js' id='main-script-a4-js' {$strategy} data-wp-strategy='{$strategy}'></script>";
     283        $this->assertStringContainsString( $expected, $output, 'Only enqueued dependents should affect the eligible strategy.' );
     284    }
     285
     286    /**
     287     * Data provider for test_filter_eligible_strategies.
     288     *
     289     * @return array
     290     */
     291    public function get_data_to_filter_eligible_strategies() {
     292        return array(
     293            'no_dependents'                       => array(
     294                'set_up'   => static function () {
     295                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     296                    return 'foo';
     297                },
     298                'expected' => array( 'defer' ),
     299            ),
     300            'one_delayed_dependent'               => array(
     301                'set_up'   => static function () {
     302                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     303                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     304                    return 'foo';
     305                },
     306                'expected' => array( 'defer' ),
     307            ),
     308            'one_blocking_dependent'              => array(
     309                'set_up'   => static function () {
     310                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     311                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
     312                    return 'foo';
     313                },
     314                'expected' => array(),
     315            ),
     316            'one_blocking_dependent_not_enqueued' => array(
     317                'set_up'   => static function () {
     318                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     319                    wp_register_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
     320                    return 'foo';
     321                },
     322                'expected' => array( 'defer' ), // Because bar was not enqueued, only foo was.
     323            ),
     324            'two_delayed_dependents'              => array(
     325                'set_up'   => static function () {
     326                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     327                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     328                    wp_enqueue_script( 'baz', 'https://example.com/baz.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     329                    return 'foo';
     330                },
     331                'expected' => array( 'defer' ),
     332            ),
     333            'recursion_not_delayed'               => array(
     334                'set_up'   => static function () {
     335                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null );
     336                    return 'foo';
     337                },
     338                'expected' => array(),
     339            ),
     340            'recursion_yes_delayed'               => array(
     341                'set_up'   => static function () {
     342                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     343                    return 'foo';
     344                },
     345                'expected' => array( 'defer' ),
     346            ),
     347            'recursion_triple_level'              => array(
     348                'set_up'   => static function () {
     349                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'baz' ), null, array( 'strategy' => 'defer' ) );
     350                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     351                    wp_enqueue_script( 'baz', 'https://example.com/bar.js', array( 'bar' ), null, array( 'strategy' => 'defer' ) );
     352                    return 'foo';
     353                },
     354                'expected' => array( 'defer' ),
     355            ),
     356            'async_only_with_async_dependency'    => array(
     357                'set_up'   => static function () {
     358                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
     359                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'async' ) );
     360                    return 'foo';
     361                },
     362                'expected' => array( 'defer', 'async' ),
     363            ),
     364            'async_only_with_defer_dependency'    => array(
     365                'set_up'   => static function () {
     366                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
     367                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
     368                    return 'foo';
     369                },
     370                'expected' => array( 'defer' ),
     371            ),
     372            'async_only_with_blocking_dependency' => array(
     373                'set_up'   => static function () {
     374                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
     375                    wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
     376                    return 'foo';
     377                },
     378                'expected' => array(),
     379            ),
     380            'defer_with_inline_after_script'      => array(
     381                'set_up'   => static function () {
     382                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     383                    wp_add_inline_script( 'foo', 'console.log("foo")', 'after' );
     384                    return 'foo';
     385                },
     386                'expected' => array(),
     387            ),
     388            'defer_with_inline_before_script'     => array(
     389                'set_up'   => static function () {
     390                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
     391                    wp_add_inline_script( 'foo', 'console.log("foo")', 'before' );
     392                    return 'foo';
     393                },
     394                'expected' => array( 'defer' ),
     395            ),
     396            'async_with_inline_after_script'      => array(
     397                'set_up'   => static function () {
     398                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
     399                    wp_add_inline_script( 'foo', 'console.log("foo")', 'after' );
     400                    return 'foo';
     401                },
     402                'expected' => array(),
     403            ),
     404            'async_with_inline_before_script'     => array(
     405                'set_up'   => static function () {
     406                    wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
     407                    wp_add_inline_script( 'foo', 'console.log("foo")', 'before' );
     408                    return 'foo';
     409                },
     410                'expected' => array( 'defer', 'async' ),
     411            ),
     412        );
     413    }
     414
     415    /**
     416     * Tests that the filter_eligible_strategies method works as expected and returns the correct value.
     417     *
     418     * @ticket 12009
     419     *
     420     * @covers WP_Scripts::filter_eligible_strategies
     421     *
     422     * @dataProvider get_data_to_filter_eligible_strategies
     423     *
     424     * @param callable $set_up     Set up.
     425     * @param bool     $async_only Async only.
     426     * @param bool     $expected   Expected return value.
     427     */
     428    public function test_filter_eligible_strategies( $set_up, $expected ) {
     429        $handle = $set_up();
     430
     431        $wp_scripts_reflection      = new ReflectionClass( WP_Scripts::class );
     432        $filter_eligible_strategies = $wp_scripts_reflection->getMethod( 'filter_eligible_strategies' );
     433        $filter_eligible_strategies->setAccessible( true );
     434        $this->assertSame( $expected, $filter_eligible_strategies->invokeArgs( wp_scripts(), array( $handle ) ), 'Expected return value of WP_Scripts::filter_eligible_strategies to match.' );
     435    }
     436
     437    /**
     438     * Register test script.
     439     *
     440     * @param string   $handle    Dependency handle to enqueue.
     441     * @param string   $strategy  Strategy to use for dependency.
     442     * @param string[] $deps      Dependencies for the script.
     443     * @param bool     $in_footer Whether to print the script in the footer.
     444     */
     445    protected function register_test_script( $handle, $strategy, $deps = array(), $in_footer = false ) {
     446        wp_register_script(
     447            $handle,
     448            add_query_arg(
     449                array(
     450                    'script_event_log' => "$handle: script",
     451                ),
     452                'https://example.com/external.js'
     453            ),
     454            $deps,
     455            null
     456        );
     457        if ( 'blocking' !== $strategy ) {
     458            wp_script_add_data( $handle, 'strategy', $strategy );
     459        }
     460    }
     461
     462    /**
     463     * Enqueue test script.
     464     *
     465     * @param string   $handle    Dependency handle to enqueue.
     466     * @param string   $strategy  Strategy to use for dependency.
     467     * @param string[] $deps      Dependencies for the script.
     468     * @param bool     $in_footer Whether to print the script in the footer.
     469     */
     470    protected function enqueue_test_script( $handle, $strategy, $deps = array(), $in_footer = false ) {
     471        $this->register_test_script( $handle, $strategy, $deps, $in_footer );
     472        wp_enqueue_script( $handle );
     473    }
     474
     475    /**
     476     * Adds test inline script.
     477     *
     478     * @param string $handle   Dependency handle to enqueue.
     479     * @param string $position Position.
     480     */
     481    protected function add_test_inline_script( $handle, $position ) {
     482        wp_add_inline_script( $handle, sprintf( 'scriptEventLog.push( %s )', wp_json_encode( "{$handle}: {$position} inline" ) ), $position );
     483    }
     484
     485    /**
     486     * Data provider to test various strategy dependency chains.
     487     *
     488     * @return array[]
     489     */
     490    public function data_provider_to_test_various_strategy_dependency_chains() {
     491        return array(
     492            'async-dependent-with-one-blocking-dependency' => array(
     493                'set_up'          => function () {
     494                    $handle1 = 'blocking-not-async-without-dependency';
     495                    $handle2 = 'async-with-blocking-dependency';
     496                    $this->enqueue_test_script( $handle1, 'blocking', array() );
     497                    $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
     498                    foreach ( array( $handle1, $handle2 ) as $handle ) {
     499                        $this->add_test_inline_script( $handle, 'before' );
     500                        $this->add_test_inline_script( $handle, 'after' );
     501                    }
     502                },
     503                'expected_markup' => <<<HTML
     504<script id="blocking-not-async-without-dependency-js-before" type="text/javascript">
     505scriptEventLog.push( "blocking-not-async-without-dependency: before inline" )
     506</script>
     507<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-not-async-without-dependency:%20script' id='blocking-not-async-without-dependency-js'></script>
     508<script id="blocking-not-async-without-dependency-js-after" type="text/javascript">
     509scriptEventLog.push( "blocking-not-async-without-dependency: after inline" )
     510</script>
     511<script id="async-with-blocking-dependency-js-before" type="text/javascript">
     512scriptEventLog.push( "async-with-blocking-dependency: before inline" )
     513</script>
     514<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-with-blocking-dependency:%20script' id='async-with-blocking-dependency-js' data-wp-strategy='async'></script>
     515<script id="async-with-blocking-dependency-js-after" type="text/javascript">
     516scriptEventLog.push( "async-with-blocking-dependency: after inline" )
     517</script>
     518HTML
     519                ,
     520                /*
     521                 * Note: The above comma must be on its own line in PHP<7.3 and not after the `HTML` identifier
     522                 * terminating the heredoc. Otherwise, a syntax error is raised with the line number being wildly wrong:
     523                 *
     524                 * PHP Parse error:  syntax error, unexpected '' (T_ENCAPSED_AND_WHITESPACE), expecting '-' or identifier (T_STRING) or variable (T_VARIABLE) or number (T_NUM_STRING)
     525                 */
     526            ),
     527            'async-with-async-dependencies'                => array(
     528                'set_up'          => function () {
     529                    $handle1 = 'async-no-dependency';
     530                    $handle2 = 'async-one-async-dependency';
     531                    $handle3 = 'async-two-async-dependencies';
     532                    $this->enqueue_test_script( $handle1, 'async', array() );
     533                    $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
     534                    $this->enqueue_test_script( $handle3, 'async', array( $handle1, $handle2 ) );
     535                    foreach ( array( $handle1, $handle2, $handle3 ) as $handle ) {
     536                        $this->add_test_inline_script( $handle, 'before' );
     537                        $this->add_test_inline_script( $handle, 'after' );
     538                    }
     539                },
     540                'expected_markup' => <<<HTML
     541<script id="async-no-dependency-js-before" type="text/javascript">
     542scriptEventLog.push( "async-no-dependency: before inline" )
     543</script>
     544<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-no-dependency:%20script' id='async-no-dependency-js' data-wp-strategy='async'></script>
     545<script id="async-no-dependency-js-after" type="text/javascript">
     546scriptEventLog.push( "async-no-dependency: after inline" )
     547</script>
     548<script id="async-one-async-dependency-js-before" type="text/javascript">
     549scriptEventLog.push( "async-one-async-dependency: before inline" )
     550</script>
     551<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-one-async-dependency:%20script' id='async-one-async-dependency-js' data-wp-strategy='async'></script>
     552<script id="async-one-async-dependency-js-after" type="text/javascript">
     553scriptEventLog.push( "async-one-async-dependency: after inline" )
     554</script>
     555<script id="async-two-async-dependencies-js-before" type="text/javascript">
     556scriptEventLog.push( "async-two-async-dependencies: before inline" )
     557</script>
     558<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-two-async-dependencies:%20script' id='async-two-async-dependencies-js' data-wp-strategy='async'></script>
     559<script id="async-two-async-dependencies-js-after" type="text/javascript">
     560scriptEventLog.push( "async-two-async-dependencies: after inline" )
     561</script>
     562HTML
     563                ,
     564            ),
     565            'async-with-blocking-dependency'               => array(
     566                'set_up'          => function () {
     567                    $handle1 = 'async-with-blocking-dependent';
     568                    $handle2 = 'blocking-dependent-of-async';
     569                    $this->enqueue_test_script( $handle1, 'async', array() );
     570                    $this->enqueue_test_script( $handle2, 'blocking', array( $handle1 ) );
     571                    foreach ( array( $handle1, $handle2 ) as $handle ) {
     572                        $this->add_test_inline_script( $handle, 'before' );
     573                        $this->add_test_inline_script( $handle, 'after' );
     574                    }
     575                },
     576                'expected_markup' => <<<HTML
     577<script id="async-with-blocking-dependent-js-before" type="text/javascript">
     578scriptEventLog.push( "async-with-blocking-dependent: before inline" )
     579</script>
     580<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-with-blocking-dependent:%20script' id='async-with-blocking-dependent-js' data-wp-strategy='async'></script>
     581<script id="async-with-blocking-dependent-js-after" type="text/javascript">
     582scriptEventLog.push( "async-with-blocking-dependent: after inline" )
     583</script>
     584<script id="blocking-dependent-of-async-js-before" type="text/javascript">
     585scriptEventLog.push( "blocking-dependent-of-async: before inline" )
     586</script>
     587<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-dependent-of-async:%20script' id='blocking-dependent-of-async-js'></script>
     588<script id="blocking-dependent-of-async-js-after" type="text/javascript">
     589scriptEventLog.push( "blocking-dependent-of-async: after inline" )
     590</script>
     591HTML
     592                ,
     593            ),
     594            'defer-with-async-dependency'                  => array(
     595                'set_up'          => function () {
     596                    $handle1 = 'async-with-defer-dependent';
     597                    $handle2 = 'defer-dependent-of-async';
     598                    $this->enqueue_test_script( $handle1, 'async', array() );
     599                    $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
     600                    foreach ( array( $handle1, $handle2 ) as $handle ) {
     601                        $this->add_test_inline_script( $handle, 'before' );
     602                        $this->add_test_inline_script( $handle, 'after' );
     603                    }
     604                },
     605                'expected_markup' => <<<HTML
     606<script id="async-with-defer-dependent-js-before" type="text/javascript">
     607scriptEventLog.push( "async-with-defer-dependent: before inline" )
     608</script>
     609<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-with-defer-dependent:%20script' id='async-with-defer-dependent-js' data-wp-strategy='async'></script>
     610<script id="async-with-defer-dependent-js-after" type="text/javascript">
     611scriptEventLog.push( "async-with-defer-dependent: after inline" )
     612</script>
     613<script id="defer-dependent-of-async-js-before" type="text/javascript">
     614scriptEventLog.push( "defer-dependent-of-async: before inline" )
     615</script>
     616<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-async:%20script' id='defer-dependent-of-async-js' data-wp-strategy='defer'></script>
     617<script id="defer-dependent-of-async-js-after" type="text/javascript">
     618scriptEventLog.push( "defer-dependent-of-async: after inline" )
     619</script>
     620HTML
     621                ,
     622            ),
     623            'blocking-bundle-of-none-with-inline-scripts-and-defer-dependent' => array(
     624                'set_up'          => function () {
     625                    $handle1 = 'blocking-bundle-of-none';
     626                    $handle2 = 'defer-dependent-of-blocking-bundle-of-none';
     627
     628                    wp_register_script( $handle1, false, array(), null );
     629                    $this->add_test_inline_script( $handle1, 'before' );
     630                    $this->add_test_inline_script( $handle1, 'after' );
     631
     632                    // Note: the before script for this will be blocking because the dependency is blocking.
     633                    $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
     634                    $this->add_test_inline_script( $handle2, 'before' );
     635                    $this->add_test_inline_script( $handle2, 'after' );
     636                },
     637                'expected_markup' => <<<HTML
     638<script id="blocking-bundle-of-none-js-before" type="text/javascript">
     639scriptEventLog.push( "blocking-bundle-of-none: before inline" )
     640</script>
     641<script id="blocking-bundle-of-none-js-after" type="text/javascript">
     642scriptEventLog.push( "blocking-bundle-of-none: after inline" )
     643</script>
     644<script id="defer-dependent-of-blocking-bundle-of-none-js-before" type="text/javascript">
     645scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-none: before inline" )
     646</script>
     647<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-none:%20script' id='defer-dependent-of-blocking-bundle-of-none-js' data-wp-strategy='defer'></script>
     648<script id="defer-dependent-of-blocking-bundle-of-none-js-after" type="text/javascript">
     649scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-none: after inline" )
     650</script>
     651HTML
     652                ,
     653            ),
     654            'blocking-bundle-of-two-with-defer-dependent'  => array(
     655                'set_up'          => function () {
     656                    $handle1 = 'blocking-bundle-of-two';
     657                    $handle2 = 'blocking-bundle-member-one';
     658                    $handle3 = 'blocking-bundle-member-two';
     659                    $handle4 = 'defer-dependent-of-blocking-bundle-of-two';
     660
     661                    wp_register_script( $handle1, false, array( $handle2, $handle3 ), null );
     662                    $this->enqueue_test_script( $handle2, 'blocking' );
     663                    $this->enqueue_test_script( $handle3, 'blocking' );
     664                    $this->enqueue_test_script( $handle4, 'defer', array( $handle1 ) );
     665
     666                    foreach ( array( $handle2, $handle3, $handle4 ) as $handle ) {
     667                        $this->add_test_inline_script( $handle, 'before' );
     668                        $this->add_test_inline_script( $handle, 'after' );
     669                    }
     670                },
     671                'expected_markup' => <<<HTML
     672<script id="blocking-bundle-member-one-js-before" type="text/javascript">
     673scriptEventLog.push( "blocking-bundle-member-one: before inline" )
     674</script>
     675<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-bundle-member-one:%20script' id='blocking-bundle-member-one-js'></script>
     676<script id="blocking-bundle-member-one-js-after" type="text/javascript">
     677scriptEventLog.push( "blocking-bundle-member-one: after inline" )
     678</script>
     679<script id="blocking-bundle-member-two-js-before" type="text/javascript">
     680scriptEventLog.push( "blocking-bundle-member-two: before inline" )
     681</script>
     682<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-bundle-member-two:%20script' id='blocking-bundle-member-two-js'></script>
     683<script id="blocking-bundle-member-two-js-after" type="text/javascript">
     684scriptEventLog.push( "blocking-bundle-member-two: after inline" )
     685</script>
     686<script id="defer-dependent-of-blocking-bundle-of-two-js-before" type="text/javascript">
     687scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-two: before inline" )
     688</script>
     689<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-two:%20script' id='defer-dependent-of-blocking-bundle-of-two-js' data-wp-strategy='defer'></script>
     690<script id="defer-dependent-of-blocking-bundle-of-two-js-after" type="text/javascript">
     691scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-two: after inline" )
     692</script>
     693HTML
     694                ,
     695            ),
     696            'defer-bundle-of-none-with-inline-scripts-and-defer-dependents' => array(
     697                'set_up'          => function () {
     698                    $handle1 = 'defer-bundle-of-none';
     699                    $handle2 = 'defer-dependent-of-defer-bundle-of-none';
     700
     701                    // The eligible loading strategy for this will be forced to be blocking when rendered since $src = false.
     702                    wp_register_script( $handle1, false, array(), null );
     703                    wp_scripts()->registered[ $handle1 ]->extra['strategy'] = 'defer'; // Bypass wp_script_add_data() which should no-op with _doing_it_wrong() because of $src=false.
     704                    $this->add_test_inline_script( $handle1, 'before' );
     705                    $this->add_test_inline_script( $handle1, 'after' );
     706
     707                    // Note: the before script for this will be blocking because the dependency is blocking.
     708                    $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
     709                    $this->add_test_inline_script( $handle2, 'before' );
     710                    $this->add_test_inline_script( $handle2, 'after' );
     711                },
     712                'expected_markup' => <<<HTML
     713<script id="defer-bundle-of-none-js-before" type="text/javascript">
     714scriptEventLog.push( "defer-bundle-of-none: before inline" )
     715</script>
     716<script id="defer-bundle-of-none-js-after" type="text/javascript">
     717scriptEventLog.push( "defer-bundle-of-none: after inline" )
     718</script>
     719<script id="defer-dependent-of-defer-bundle-of-none-js-before" type="text/javascript">
     720scriptEventLog.push( "defer-dependent-of-defer-bundle-of-none: before inline" )
     721</script>
     722<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-bundle-of-none:%20script' id='defer-dependent-of-defer-bundle-of-none-js' data-wp-strategy='defer'></script>
     723<script id="defer-dependent-of-defer-bundle-of-none-js-after" type="text/javascript">
     724scriptEventLog.push( "defer-dependent-of-defer-bundle-of-none: after inline" )
     725</script>
     726HTML
     727                ,
     728            ),
     729            'defer-dependent-with-blocking-and-defer-dependencies' => array(
     730                'set_up'          => function () {
     731                    $handle1 = 'blocking-dependency-with-defer-following-dependency';
     732                    $handle2 = 'defer-dependency-with-blocking-preceding-dependency';
     733                    $handle3 = 'defer-dependent-of-blocking-and-defer-dependencies';
     734                    $this->enqueue_test_script( $handle1, 'blocking', array() );
     735                    $this->enqueue_test_script( $handle2, 'defer', array() );
     736                    $this->enqueue_test_script( $handle3, 'defer', array( $handle1, $handle2 ) );
     737
     738                    foreach ( array( $handle1, $handle2, $handle3 ) as $dep ) {
     739                        $this->add_test_inline_script( $dep, 'before' );
     740                        $this->add_test_inline_script( $dep, 'after' );
     741                    }
     742                },
     743                'expected_markup' => <<<HTML
     744<script id="blocking-dependency-with-defer-following-dependency-js-before" type="text/javascript">
     745scriptEventLog.push( "blocking-dependency-with-defer-following-dependency: before inline" )
     746</script>
     747<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-following-dependency:%20script' id='blocking-dependency-with-defer-following-dependency-js'></script>
     748<script id="blocking-dependency-with-defer-following-dependency-js-after" type="text/javascript">
     749scriptEventLog.push( "blocking-dependency-with-defer-following-dependency: after inline" )
     750</script>
     751<script id="defer-dependency-with-blocking-preceding-dependency-js-before" type="text/javascript">
     752scriptEventLog.push( "defer-dependency-with-blocking-preceding-dependency: before inline" )
     753</script>
     754<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-preceding-dependency:%20script' id='defer-dependency-with-blocking-preceding-dependency-js' data-wp-strategy='defer'></script>
     755<script id="defer-dependency-with-blocking-preceding-dependency-js-after" type="text/javascript">
     756scriptEventLog.push( "defer-dependency-with-blocking-preceding-dependency: after inline" )
     757</script>
     758<script id="defer-dependent-of-blocking-and-defer-dependencies-js-before" type="text/javascript">
     759scriptEventLog.push( "defer-dependent-of-blocking-and-defer-dependencies: before inline" )
     760</script>
     761<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-and-defer-dependencies:%20script' id='defer-dependent-of-blocking-and-defer-dependencies-js' data-wp-strategy='defer'></script>
     762<script id="defer-dependent-of-blocking-and-defer-dependencies-js-after" type="text/javascript">
     763scriptEventLog.push( "defer-dependent-of-blocking-and-defer-dependencies: after inline" )
     764</script>
     765HTML
     766                ,
     767            ),
     768            'defer-dependent-with-defer-and-blocking-dependencies' => array(
     769                'set_up'          => function () {
     770                    $handle1 = 'defer-dependency-with-blocking-following-dependency';
     771                    $handle2 = 'blocking-dependency-with-defer-preceding-dependency';
     772                    $handle3 = 'defer-dependent-of-defer-and-blocking-dependencies';
     773                    $this->enqueue_test_script( $handle1, 'defer', array() );
     774                    $this->enqueue_test_script( $handle2, 'blocking', array() );
     775                    $this->enqueue_test_script( $handle3, 'defer', array( $handle1, $handle2 ) );
     776
     777                    foreach ( array( $handle1, $handle2, $handle3 ) as $dep ) {
     778                        $this->add_test_inline_script( $dep, 'before' );
     779                        $this->add_test_inline_script( $dep, 'after' );
     780                    }
     781                },
     782                'expected_markup' => <<<HTML
     783<script id="defer-dependency-with-blocking-following-dependency-js-before" type="text/javascript">
     784scriptEventLog.push( "defer-dependency-with-blocking-following-dependency: before inline" )
     785</script>
     786<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-following-dependency:%20script' id='defer-dependency-with-blocking-following-dependency-js' data-wp-strategy='defer'></script>
     787<script id="defer-dependency-with-blocking-following-dependency-js-after" type="text/javascript">
     788scriptEventLog.push( "defer-dependency-with-blocking-following-dependency: after inline" )
     789</script>
     790<script id="blocking-dependency-with-defer-preceding-dependency-js-before" type="text/javascript">
     791scriptEventLog.push( "blocking-dependency-with-defer-preceding-dependency: before inline" )
     792</script>
     793<script type='text/javascript' src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-preceding-dependency:%20script' id='blocking-dependency-with-defer-preceding-dependency-js'></script>
     794<script id="blocking-dependency-with-defer-preceding-dependency-js-after" type="text/javascript">
     795scriptEventLog.push( "blocking-dependency-with-defer-preceding-dependency: after inline" )
     796</script>
     797<script id="defer-dependent-of-defer-and-blocking-dependencies-js-before" type="text/javascript">
     798scriptEventLog.push( "defer-dependent-of-defer-and-blocking-dependencies: before inline" )
     799</script>
     800<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-and-blocking-dependencies:%20script' id='defer-dependent-of-defer-and-blocking-dependencies-js' data-wp-strategy='defer'></script>
     801<script id="defer-dependent-of-defer-and-blocking-dependencies-js-after" type="text/javascript">
     802scriptEventLog.push( "defer-dependent-of-defer-and-blocking-dependencies: after inline" )
     803</script>
     804HTML
     805                ,
     806            ),
     807            'async-with-defer-dependency'                  => array(
     808                'set_up'          => function () {
     809                    $handle1 = 'defer-with-async-dependent';
     810                    $handle2 = 'async-dependent-of-defer';
     811                    $this->enqueue_test_script( $handle1, 'defer', array() );
     812                    $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
     813                    foreach ( array( $handle1, $handle2 ) as $handle ) {
     814                        $this->add_test_inline_script( $handle, 'before' );
     815                        $this->add_test_inline_script( $handle, 'after' );
     816                    }
     817                },
     818                'expected_markup' => <<<HTML
     819<script id="defer-with-async-dependent-js-before" type="text/javascript">
     820scriptEventLog.push( "defer-with-async-dependent: before inline" )
     821</script>
     822<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-with-async-dependent:%20script' id='defer-with-async-dependent-js' data-wp-strategy='defer'></script>
     823<script id="defer-with-async-dependent-js-after" type="text/javascript">
     824scriptEventLog.push( "defer-with-async-dependent: after inline" )
     825</script>
     826<script id="async-dependent-of-defer-js-before" type="text/javascript">
     827scriptEventLog.push( "async-dependent-of-defer: before inline" )
     828</script>
     829<script type='text/javascript' src='https://example.com/external.js?script_event_log=async-dependent-of-defer:%20script' id='async-dependent-of-defer-js' data-wp-strategy='async'></script>
     830<script id="async-dependent-of-defer-js-after" type="text/javascript">
     831scriptEventLog.push( "async-dependent-of-defer: after inline" )
     832</script>
     833HTML
     834                ,
     835            ),
     836            'defer-with-before-inline-script'              => array(
     837                'set_up'          => function () {
     838                    // Note this should NOT result in no delayed-inline-script-loader script being added.
     839                    $handle = 'defer-with-before-inline';
     840                    $this->enqueue_test_script( $handle, 'defer', array() );
     841                    $this->add_test_inline_script( $handle, 'before' );
     842                },
     843                'expected_markup' => <<<HTML
     844<script id="defer-with-before-inline-js-before" type="text/javascript">
     845scriptEventLog.push( "defer-with-before-inline: before inline" )
     846</script>
     847<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-with-before-inline:%20script' id='defer-with-before-inline-js' defer data-wp-strategy='defer'></script>
     848HTML
     849                ,
     850            ),
     851            'defer-with-after-inline-script'               => array(
     852                'set_up'          => function () {
     853                    // Note this SHOULD result in delayed-inline-script-loader script being added.
     854                    $handle = 'defer-with-after-inline';
     855                    $this->enqueue_test_script( $handle, 'defer', array() );
     856                    $this->add_test_inline_script( $handle, 'after' );
     857                },
     858                'expected_markup' => <<<HTML
     859<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-with-after-inline:%20script' id='defer-with-after-inline-js' data-wp-strategy='defer'></script>
     860<script id="defer-with-after-inline-js-after" type="text/javascript">
     861scriptEventLog.push( "defer-with-after-inline: after inline" )
     862</script>
     863HTML
     864                ,
     865            ),
     866            'jquery-deferred'                              => array(
     867                'set_up'          => function () {
     868                    $wp_scripts = wp_scripts();
     869                    wp_default_scripts( $wp_scripts );
     870                    foreach ( $wp_scripts->registered['jquery']->deps as $jquery_dep ) {
     871                        $wp_scripts->registered[ $jquery_dep ]->add_data( 'strategy', 'defer' );
     872                        $wp_scripts->registered[ $jquery_dep ]->ver = null; // Just to avoid markup changes in the test when jQuery is upgraded.
     873                    }
     874                    wp_enqueue_script( 'theme-functions', 'https://example.com/theme-functions.js', array( 'jquery' ), null, array( 'strategy' => 'defer' ) );
     875                },
     876                'expected_markup' => <<<HTML
     877<script type='text/javascript' src='http://example.org/wp-includes/js/jquery/jquery.js' id='jquery-core-js' defer data-wp-strategy='defer'></script>
     878<script type='text/javascript' src='http://example.org/wp-includes/js/jquery/jquery-migrate.js' id='jquery-migrate-js' defer data-wp-strategy='defer'></script>
     879<script type='text/javascript' src='https://example.com/theme-functions.js' id='theme-functions-js' defer data-wp-strategy='defer'></script>
     880HTML
     881                ,
     882            ),
     883            'nested-aliases'                               => array(
     884                'set_up'          => function () {
     885                    $outer_alias_handle = 'outer-bundle-of-two';
     886                    $inner_alias_handle = 'inner-bundle-of-two';
     887
     888                    // The outer alias contains a blocking member, as well as a nested alias that contains defer scripts.
     889                    wp_register_script( $outer_alias_handle, false, array( $inner_alias_handle, 'outer-bundle-leaf-member' ), null );
     890                    $this->register_test_script( 'outer-bundle-leaf-member', 'blocking', array() );
     891
     892                    // Inner alias only contains delay scripts.
     893                    wp_register_script( $inner_alias_handle, false, array( 'inner-bundle-member-one', 'inner-bundle-member-two' ), null );
     894                    $this->register_test_script( 'inner-bundle-member-one', 'defer', array() );
     895                    $this->register_test_script( 'inner-bundle-member-two', 'defer', array() );
     896
     897                    $this->enqueue_test_script( 'defer-dependent-of-nested-aliases', 'defer', array( $outer_alias_handle ) );
     898                    $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'before' );
     899                    $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'after' );
     900                },
     901                'expected_markup' => <<<HTML
     902<script type='text/javascript' src='https://example.com/external.js?script_event_log=inner-bundle-member-one:%20script' id='inner-bundle-member-one-js' data-wp-strategy='defer'></script>
     903<script type='text/javascript' src='https://example.com/external.js?script_event_log=inner-bundle-member-two:%20script' id='inner-bundle-member-two-js' data-wp-strategy='defer'></script>
     904<script type='text/javascript' src='https://example.com/external.js?script_event_log=outer-bundle-leaf-member:%20script' id='outer-bundle-leaf-member-js'></script>
     905<script id="defer-dependent-of-nested-aliases-js-before" type="text/javascript">
     906scriptEventLog.push( "defer-dependent-of-nested-aliases: before inline" )
     907</script>
     908<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-nested-aliases:%20script' id='defer-dependent-of-nested-aliases-js' data-wp-strategy='defer'></script>
     909<script id="defer-dependent-of-nested-aliases-js-after" type="text/javascript">
     910scriptEventLog.push( "defer-dependent-of-nested-aliases: after inline" )
     911</script>
     912HTML
     913                ,
     914            ),
     915
     916            'async-alias-members-with-defer-dependency'    => array(
     917                'set_up'          => function () {
     918                    $alias_handle = 'async-alias';
     919                    $async_handle1 = 'async1';
     920                    $async_handle2 = 'async2';
     921
     922                    wp_register_script( $alias_handle, false, array( $async_handle1, $async_handle2 ), null );
     923                    $this->register_test_script( $async_handle1, 'async', array() );
     924                    $this->register_test_script( $async_handle2, 'async', array() );
     925
     926                    $this->enqueue_test_script( 'defer-dependent-of-async-aliases', 'defer', array( $alias_handle ) );
     927                },
     928                'expected_markup' => <<<HTML
     929<script type='text/javascript' src='https://example.com/external.js?script_event_log=async1:%20script' id='async1-js' defer data-wp-strategy='async'></script>
     930<script type='text/javascript' src='https://example.com/external.js?script_event_log=async2:%20script' id='async2-js' defer data-wp-strategy='async'></script>
     931<script type='text/javascript' src='https://example.com/external.js?script_event_log=defer-dependent-of-async-aliases:%20script' id='defer-dependent-of-async-aliases-js' defer data-wp-strategy='defer'></script>
     932HTML
     933                ,
     934            ),
     935        );
     936    }
     937
     938    /**
     939     * Tests that various loading strategy dependency chains function as expected.
     940     *
     941     * @covers ::wp_enqueue_script()
     942     * @covers ::wp_add_inline_script()
     943     * @covers ::wp_print_scripts()
     944     * @covers WP_Scripts::get_inline_script_tag
     945     *
     946     * @dataProvider data_provider_to_test_various_strategy_dependency_chains
     947     *
     948     * @param callable $set_up          Set up.
     949     * @param string   $expected_markup Expected markup.
     950     */
     951    public function test_various_strategy_dependency_chains( $set_up, $expected_markup ) {
     952        $set_up();
     953        $actual_markup = get_echo( 'wp_print_scripts' );
     954        $this->assertEqualMarkup( trim( $expected_markup ), trim( $actual_markup ), "Actual markup:\n{$actual_markup}" );
     955    }
     956
     957    /**
     958     * Tests that defer is the final strategy when registering a script using defer, that has no dependents/dependencies.
     959     *
     960     * @ticket 12009
     961     *
     962     * @covers WP_Scripts::do_item
     963     * @covers WP_Scripts::get_eligible_loading_strategy
     964     * @covers ::wp_enqueue_script
     965     */
     966    public function test_loading_strategy_with_defer_having_no_dependents_nor_dependencies() {
     967        wp_enqueue_script( 'main-script-d1', 'http://example.com/main-script-d1.js', array(), null, array( 'strategy' => 'defer' ) );
     968        $output   = get_echo( 'wp_print_scripts' );
     969        $expected = "<script type='text/javascript' src='http://example.com/main-script-d1.js' id='main-script-d1-js' defer data-wp-strategy='defer'></script>\n";
     970        $this->assertStringContainsString( $expected, $output, 'Expected defer, as there is no dependent or dependency' );
     971    }
     972
     973    /**
     974     * Tests that a script registered with defer remains deferred when all dependencies are either deferred or blocking.
     975     *
     976     * @ticket 12009
     977     *
     978     * @covers WP_Scripts::do_item
     979     * @covers WP_Scripts::get_eligible_loading_strategy
     980     * @covers ::wp_enqueue_script
     981     */
     982    public function test_loading_strategy_with_defer_dependent_and_varied_dependencies() {
     983        wp_enqueue_script( 'dependency-script-d2-1', 'http://example.com/dependency-script-d2-1.js', array(), null, array( 'strategy' => 'defer' ) );
     984        wp_enqueue_script( 'dependency-script-d2-2', 'http://example.com/dependency-script-d2-2.js', array(), null );
     985        wp_enqueue_script( 'dependency-script-d2-3', 'http://example.com/dependency-script-d2-3.js', array( 'dependency-script-d2-2' ), null, array( 'strategy' => 'defer' ) );
     986        wp_enqueue_script( 'main-script-d2', 'http://example.com/main-script-d2.js', array( 'dependency-script-d2-1', 'dependency-script-d2-3' ), null, array( 'strategy' => 'defer' ) );
     987        $output   = get_echo( 'wp_print_scripts' );
     988        $expected = "<script type='text/javascript' src='http://example.com/main-script-d2.js' id='main-script-d2-js' defer data-wp-strategy='defer'></script>\n";
     989        $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependencies are either deferred or blocking' );
     990    }
     991
     992    /**
     993     * Tests that scripts registered with defer remain deferred when all dependents are also deferred.
     994     *
     995     * @ticket 12009
     996     *
     997     * @covers WP_Scripts::do_item
     998     * @covers WP_Scripts::get_eligible_loading_strategy
     999     * @covers ::wp_enqueue_script
     1000     */
     1001    public function test_loading_strategy_with_all_defer_dependencies() {
     1002        wp_enqueue_script( 'main-script-d3', 'http://example.com/main-script-d3.js', array(), null, array( 'strategy' => 'defer' ) );
     1003        wp_enqueue_script( 'dependent-script-d3-1', 'http://example.com/dependent-script-d3-1.js', array( 'main-script-d3' ), null, array( 'strategy' => 'defer' ) );
     1004        wp_enqueue_script( 'dependent-script-d3-2', 'http://example.com/dependent-script-d3-2.js', array( 'dependent-script-d3-1' ), null, array( 'strategy' => 'defer' ) );
     1005        wp_enqueue_script( 'dependent-script-d3-3', 'http://example.com/dependent-script-d3-3.js', array( 'dependent-script-d3-2' ), null, array( 'strategy' => 'defer' ) );
     1006        $output   = get_echo( 'wp_print_scripts' );
     1007        $expected = "<script type='text/javascript' src='http://example.com/main-script-d3.js' id='main-script-d3-js' defer data-wp-strategy='defer'></script>\n";
     1008        $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependents have defer loading strategy' );
     1009    }
     1010
     1011    /**
     1012     * Tests that dependents that are async but attached to a deferred main script, print with defer as opposed to async.
     1013     *
     1014     * @ticket 12009
     1015     *
     1016     * @covers WP_Scripts::do_item
     1017     * @covers WP_Scripts::get_eligible_loading_strategy
     1018     * @covers ::wp_enqueue_script
     1019     */
     1020    public function test_defer_with_async_dependent() {
     1021        // case with one async dependent.
     1022        wp_enqueue_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
     1023        wp_enqueue_script( 'dependent-script-d4-1', '/dependent-script-d4-1.js', array( 'main-script-d4' ), null, array( 'strategy' => 'defer' ) );
     1024        wp_enqueue_script( 'dependent-script-d4-2', '/dependent-script-d4-2.js', array( 'dependent-script-d4-1' ), null, array( 'strategy' => 'async' ) );
     1025        wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
     1026        $output    = get_echo( 'wp_print_scripts' );
     1027        $expected  = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' defer data-wp-strategy='defer'></script>\n";
     1028        $expected .= "<script type='text/javascript' src='/dependent-script-d4-1.js' id='dependent-script-d4-1-js' defer data-wp-strategy='defer'></script>\n";
     1029        $expected .= "<script type='text/javascript' src='/dependent-script-d4-2.js' id='dependent-script-d4-2-js' defer data-wp-strategy='async'></script>\n";
     1030        $expected .= "<script type='text/javascript' src='/dependent-script-d4-3.js' id='dependent-script-d4-3-js' defer data-wp-strategy='defer'></script>\n";
     1031
     1032        $this->assertSame( $expected, $output, 'Scripts registered as defer but that have dependents that are async are expected to have said dependents deferred.' );
     1033    }
     1034
     1035    /**
     1036     * Tests that scripts registered as defer become blocking when their dependents chain are all blocking.
     1037     *
     1038     * @ticket 12009
     1039     *
     1040     * @covers WP_Scripts::do_item
     1041     * @covers WP_Scripts::get_eligible_loading_strategy
     1042     * @covers WP_Scripts::filter_eligible_strategies
     1043     * @covers ::wp_enqueue_script
     1044     */
     1045    public function test_loading_strategy_with_invalid_defer_registration() {
     1046        // Main script is defer and all dependent are not defer. Then main script will have blocking(or no) strategy.
     1047        wp_enqueue_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
     1048        wp_enqueue_script( 'dependent-script-d4-1', '/dependent-script-d4-1.js', array( 'main-script-d4' ), null, array( 'strategy' => 'defer' ) );
     1049        wp_enqueue_script( 'dependent-script-d4-2', '/dependent-script-d4-2.js', array( 'dependent-script-d4-1' ), null );
     1050        wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
     1051        $output   = get_echo( 'wp_print_scripts' );
     1052        $expected = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' data-wp-strategy='defer'></script>\n";
     1053        $this->assertStringContainsString( $expected, $output, 'Scripts registered as defer but that have all dependents with no strategy, should become blocking (no strategy).' );
     1054    }
     1055
     1056    /**
     1057     * Tests that scripts registered as default/blocking remain as such when they have no dependencies.
     1058     *
     1059     * @ticket 12009
     1060     *
     1061     * @covers WP_Scripts::do_item
     1062     * @covers WP_Scripts::get_eligible_loading_strategy
     1063     * @covers WP_Scripts::filter_eligible_strategies
     1064     * @covers ::wp_enqueue_script
     1065     */
     1066    public function test_loading_strategy_with_valid_blocking_registration() {
     1067        wp_enqueue_script( 'main-script-b1', '/main-script-b1.js', array(), null );
     1068        $output   = get_echo( 'wp_print_scripts' );
     1069        $expected = "<script type='text/javascript' src='/main-script-b1.js' id='main-script-b1-js'></script>\n";
     1070        $this->assertSame( $expected, $output, 'Scripts registered with a "blocking" strategy, and who have no dependencies, should have no loading strategy attributes printed.' );
     1071
     1072        // strategy args not set.
     1073        wp_enqueue_script( 'main-script-b2', '/main-script-b2.js', array(), null, array() );
     1074        $output   = get_echo( 'wp_print_scripts' );
     1075        $expected = "<script type='text/javascript' src='/main-script-b2.js' id='main-script-b2-js'></script>\n";
     1076        $this->assertSame( $expected, $output, 'Scripts registered with no strategy assigned, and who have no dependencies, should have no loading strategy attributes printed.' );
     1077    }
     1078
     1079    /**
     1080     * Tests that scripts registered for the head do indeed end up there.
     1081     *
     1082     * @ticket 12009
     1083     *
     1084     * @covers WP_Scripts::do_item
     1085     * @covers ::wp_enqueue_script
     1086     * @covers ::wp_register_script
     1087     */
     1088    public function test_scripts_targeting_head() {
     1089        wp_register_script( 'header-old', '/header-old.js', array(), null, false );
     1090        wp_register_script( 'header-new', '/header-new.js', array( 'header-old' ), null, array( 'in_footer' => false ) );
     1091        wp_enqueue_script( 'enqueue-header-old', '/enqueue-header-old.js', array( 'header-new' ), null, false );
     1092        wp_enqueue_script( 'enqueue-header-new', '/enqueue-header-new.js', array( 'enqueue-header-old' ), null, array( 'in_footer' => false ) );
     1093
     1094        $actual_header = get_echo( 'wp_print_head_scripts' );
     1095        $actual_footer = get_echo( 'wp_print_scripts' );
     1096
     1097        $expected_header  = "<script type='text/javascript' src='/header-old.js' id='header-old-js'></script>\n";
     1098        $expected_header .= "<script type='text/javascript' src='/header-new.js' id='header-new-js'></script>\n";
     1099        $expected_header .= "<script type='text/javascript' src='/enqueue-header-old.js' id='enqueue-header-old-js'></script>\n";
     1100        $expected_header .= "<script type='text/javascript' src='/enqueue-header-new.js' id='enqueue-header-new-js'></script>\n";
     1101
     1102        $this->assertSame( $expected_header, $actual_header, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
     1103        $this->assertEmpty( $actual_footer, 'Expected footer to be empty since all scripts were for head.' );
     1104    }
     1105
     1106    /**
     1107     * Test that scripts registered for the footer do indeed end up there.
     1108     *
     1109     * @ticket 12009
     1110     *
     1111     * @covers WP_Scripts::do_item
     1112     * @covers ::wp_enqueue_script
     1113     * @covers ::wp_register_script
     1114     */
     1115    public function test_scripts_targeting_footer() {
     1116        wp_register_script( 'footer-old', '/footer-old.js', array(), null, true );
     1117        wp_register_script( 'footer-new', '/footer-new.js', array( 'footer-old' ), null, array( 'in_footer' => true ) );
     1118        wp_enqueue_script( 'enqueue-footer-old', '/enqueue-footer-old.js', array( 'footer-new' ), null, true );
     1119        wp_enqueue_script( 'enqueue-footer-new', '/enqueue-footer-new.js', array( 'enqueue-footer-old' ), null, array( 'in_footer' => true ) );
     1120
     1121        $actual_header = get_echo( 'wp_print_head_scripts' );
     1122        $actual_footer = get_echo( 'wp_print_scripts' );
     1123
     1124        $expected_footer  = "<script type='text/javascript' src='/footer-old.js' id='footer-old-js'></script>\n";
     1125        $expected_footer .= "<script type='text/javascript' src='/footer-new.js' id='footer-new-js'></script>\n";
     1126        $expected_footer .= "<script type='text/javascript' src='/enqueue-footer-old.js' id='enqueue-footer-old-js'></script>\n";
     1127        $expected_footer .= "<script type='text/javascript' src='/enqueue-footer-new.js' id='enqueue-footer-new-js'></script>\n";
     1128
     1129        $this->assertEmpty( $actual_header, 'Expected header to be empty since all scripts targeted footer.' );
     1130        $this->assertSame( $expected_footer, $actual_footer, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
     1131    }
     1132
     1133    /**
     1134     * Data provider for test_setting_in_footer_and_strategy.
     1135     *
     1136     * @return array[]
     1137     */
     1138    public function get_data_for_test_setting_in_footer_and_strategy() {
     1139        return array(
     1140            // Passing in_footer and strategy via args array.
     1141            'async_footer_in_args_array'    => array(
     1142                'set_up'   => static function ( $handle ) {
     1143                    $args = array(
     1144                        'in_footer' => true,
     1145                        'strategy'  => 'async',
     1146                    );
     1147                    wp_enqueue_script( $handle, '/footer-async.js', array(), null, $args );
     1148                },
     1149                'group'    => 1,
     1150                'strategy' => 'async',
     1151            ),
     1152
     1153            // Passing in_footer=true but no strategy.
     1154            'blocking_footer_in_args_array' => array(
     1155                'set_up'   => static function ( $handle ) {
     1156                    wp_register_script( $handle, '/defaults.js', array(), null, array( 'in_footer' => true ) );
     1157                },
     1158                'group'    => 1,
     1159                'strategy' => false,
     1160            ),
     1161
     1162            // Passing async strategy in script args array.
     1163            'async_in_args_array'           => array(
     1164                'set_up'   => static function ( $handle ) {
     1165                    wp_register_script( $handle, '/defaults.js', array(), null, array( 'strategy' => 'async' ) );
     1166                },
     1167                'group'    => false,
     1168                'strategy' => 'async',
     1169            ),
     1170
     1171            // Passing empty array as 5th arg.
     1172            'empty_args_array'              => array(
     1173                'set_up'   => static function ( $handle ) {
     1174                    wp_register_script( $handle, '/defaults.js', array(), null, array() );
     1175                },
     1176                'group'    => false,
     1177                'strategy' => false,
     1178            ),
     1179
     1180            // Passing no value as 5th arg.
     1181            'undefined_args_param'          => array(
     1182                'set_up'   => static function ( $handle ) {
     1183                    wp_register_script( $handle, '/defaults.js', array(), null );
     1184                },
     1185                'group'    => false,
     1186                'strategy' => false,
     1187            ),
     1188
     1189            // Test backward compatibility, passing $in_footer=true as 5th arg.
     1190            'passing_bool_as_args_param'    => array(
     1191                'set_up'   => static function ( $handle ) {
     1192                    wp_enqueue_script( $handle, '/footer-async.js', array(), null, true );
     1193                },
     1194                'group'    => 1,
     1195                'strategy' => false,
     1196            ),
     1197
     1198            // Test backward compatibility, passing $in_footer=true as 5th arg and setting strategy via wp_script_add_data().
     1199            'bool_as_args_and_add_data'     => array(
     1200                'set_up'   => static function ( $handle ) {
     1201                    wp_register_script( $handle, '/footer-async.js', array(), null, true );
     1202                    wp_script_add_data( $handle, 'strategy', 'defer' );
     1203                },
     1204                'group'    => 1,
     1205                'strategy' => 'defer',
     1206            ),
     1207        );
     1208    }
     1209
     1210    /**
     1211     * Tests that scripts print in the correct group (head/footer) when using in_footer and assigning a strategy.
     1212     *
     1213     * @ticket 12009
     1214     *
     1215     * @covers ::wp_register_script
     1216     * @covers ::wp_enqueue_script
     1217     * @covers ::wp_script_add_data
     1218     *
     1219     * @dataProvider get_data_for_test_setting_in_footer_and_strategy
     1220     *
     1221     * @param callable     $set_up            Set up.
     1222     * @param int|false    $expected_group    Expected group.
     1223     * @param string|false $expected_strategy Expected strategy.
     1224     */
     1225    public function test_setting_in_footer_and_strategy( $set_up, $expected_group, $expected_strategy ) {
     1226        $handle = 'foo';
     1227        $set_up( $handle );
     1228        $this->assertSame( $expected_group, wp_scripts()->get_data( $handle, 'group' ) );
     1229        $this->assertSame( $expected_strategy, wp_scripts()->get_data( $handle, 'strategy' ) );
     1230    }
     1231
     1232    /**
     1233     * Tests that scripts print with no strategy when an incorrect strategy is passed during wp_register_script.
     1234     *
     1235     * For an invalid strategy defined during script registration, default to a blocking strategy.
     1236     *
     1237     * @ticket 12009
     1238     *
     1239     * @covers WP_Scripts::add_data
     1240     * @covers ::wp_register_script
     1241     * @covers ::wp_enqueue_script
     1242     *
     1243     * @expectedIncorrectUsage WP_Scripts::add_data
     1244     */
     1245    public function test_script_strategy_doing_it_wrong_via_register() {
     1246        wp_register_script( 'invalid-strategy', '/defaults.js', array(), null, array( 'strategy' => 'random-strategy' ) );
     1247        wp_enqueue_script( 'invalid-strategy' );
     1248
     1249        $this->assertSame(
     1250            "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
     1251            get_echo( 'wp_print_scripts' )
     1252        );
     1253    }
     1254
     1255    /**
     1256     * Tests that scripts print with no strategy when an incorrect strategy is passed via wp_script_add_data().
     1257     *
     1258     * For an invalid strategy defined during script registration, default to a blocking strategy.
     1259     *
     1260     * @ticket 12009
     1261     *
     1262     * @covers WP_Scripts::add_data
     1263     * @covers ::wp_script_add_data
     1264     * @covers ::wp_register_script
     1265     * @covers ::wp_enqueue_script
     1266     *
     1267     * @expectedIncorrectUsage WP_Scripts::add_data
     1268     */
     1269    public function test_script_strategy_doing_it_wrong_via_add_data() {
     1270        wp_register_script( 'invalid-strategy', '/defaults.js', array(), null );
     1271        wp_script_add_data( 'invalid-strategy', 'strategy', 'random-strategy' );
     1272        wp_enqueue_script( 'invalid-strategy' );
     1273
     1274        $this->assertSame(
     1275            "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
     1276            get_echo( 'wp_print_scripts' )
     1277        );
     1278    }
     1279
     1280    /**
     1281     * Tests that scripts print with no strategy when an incorrect strategy is passed during wp_enqueue_script.
     1282     *
     1283     * For an invalid strategy defined during script registration, default to a blocking strategy.
     1284     *
     1285     * @ticket 12009
     1286     *
     1287     * @covers WP_Scripts::add_data
     1288     * @covers ::wp_enqueue_script
     1289     *
     1290     * @expectedIncorrectUsage WP_Scripts::add_data
     1291     */
     1292    public function test_script_strategy_doing_it_wrong_via_enqueue() {
     1293        wp_enqueue_script( 'invalid-strategy', '/defaults.js', array(), null, array( 'strategy' => 'random-strategy' ) );
     1294
     1295        $this->assertSame(
     1296            "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
     1297            get_echo( 'wp_print_scripts' )
     1298        );
     1299    }
     1300
     1301    /**
     1302     * Tests that scripts registered with a deferred strategy are not included in the script concat loading query.
     1303     *
     1304     * @ticket 12009
     1305     *
     1306     * @covers WP_Scripts::do_item
     1307     * @covers ::wp_enqueue_script
     1308     * @covers ::wp_register_script
     1309     */
     1310    public function test_concatenate_with_defer_strategy() {
     1311        global $wp_scripts, $concatenate_scripts, $wp_version;
     1312
     1313        $old_value           = $concatenate_scripts;
     1314        $concatenate_scripts = true;
     1315
     1316        $wp_scripts->do_concat    = true;
     1317        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1318
     1319        wp_register_script( 'one-concat-dep', $this->default_scripts_dir . 'script.js' );
     1320        wp_register_script( 'two-concat-dep', $this->default_scripts_dir . 'script.js' );
     1321        wp_register_script( 'three-concat-dep', $this->default_scripts_dir . 'script.js' );
     1322        wp_enqueue_script( 'main-defer-script', '/main-script.js', array( 'one-concat-dep', 'two-concat-dep', 'three-concat-dep' ), null, array( 'strategy' => 'defer' ) );
     1323
     1324        wp_print_scripts();
     1325        $print_scripts = get_echo( '_print_scripts' );
     1326
     1327        // Reset global before asserting.
     1328        $concatenate_scripts = $old_value;
     1329
     1330        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one-concat-dep,two-concat-dep,three-concat-dep&amp;ver={$wp_version}'></script>\n";
     1331        $expected .= "<script type='text/javascript' src='/main-script.js' id='main-defer-script-js' defer data-wp-strategy='defer'></script>\n";
     1332
     1333        $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with a "defer" loading strategy. Deferred scripts should not be part of the script concat loading query.' );
     1334    }
     1335
     1336    /**
     1337     * Test script concatenation with `async` main script.
     1338     *
     1339     * @ticket 12009
     1340     *
     1341     * @covers WP_Scripts::do_item
     1342     * @covers ::wp_enqueue_script
     1343     * @covers ::wp_register_script
     1344     */
     1345    public function test_concatenate_with_async_strategy() {
     1346        global $wp_scripts, $concatenate_scripts, $wp_version;
     1347
     1348        $old_value           = $concatenate_scripts;
     1349        $concatenate_scripts = true;
     1350
     1351        $wp_scripts->do_concat    = true;
     1352        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1353
     1354        wp_enqueue_script( 'one-concat-dep-1', $this->default_scripts_dir . 'script.js' );
     1355        wp_enqueue_script( 'two-concat-dep-1', $this->default_scripts_dir . 'script.js' );
     1356        wp_enqueue_script( 'three-concat-dep-1', $this->default_scripts_dir . 'script.js' );
     1357        wp_enqueue_script( 'main-async-script-1', '/main-script.js', array(), null, array( 'strategy' => 'async' ) );
     1358
     1359        wp_print_scripts();
     1360        $print_scripts = get_echo( '_print_scripts' );
     1361
     1362        // Reset global before asserting.
     1363        $concatenate_scripts = $old_value;
     1364
     1365        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one-concat-dep-1,two-concat-dep-1,three-concat-dep-1&amp;ver={$wp_version}'></script>\n";
     1366        $expected .= "<script type='text/javascript' src='/main-script.js' id='main-async-script-1-js' async data-wp-strategy='async'></script>\n";
     1367
     1368        $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with an "async" loading strategy. Async scripts should not be part of the script concat loading query.' );
     1369    }
     1370
     1371    /**
     1372     * Tests that script concatenation remains correct when a main script is registered as deferred after other blocking
     1373     * scripts are registered.
     1374     *
     1375     * @ticket 12009
     1376     *
     1377     * @covers WP_Scripts::do_item
     1378     * @covers ::wp_enqueue_script
     1379     * @covers ::wp_register_script
     1380     */
     1381    public function test_concatenate_with_blocking_script_before_and_after_script_with_defer_strategy() {
     1382        global $wp_scripts, $concatenate_scripts, $wp_version;
     1383
     1384        $old_value           = $concatenate_scripts;
     1385        $concatenate_scripts = true;
     1386
     1387        $wp_scripts->do_concat    = true;
     1388        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1389
     1390        wp_enqueue_script( 'one-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1391        wp_enqueue_script( 'two-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1392        wp_enqueue_script( 'three-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1393        wp_enqueue_script( 'deferred-script-2', '/main-script.js', array(), null, array( 'strategy' => 'defer' ) );
     1394        wp_enqueue_script( 'four-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1395        wp_enqueue_script( 'five-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1396        wp_enqueue_script( 'six-concat-dep-2', $this->default_scripts_dir . 'script.js' );
     1397
     1398        wp_print_scripts();
     1399        $print_scripts = get_echo( '_print_scripts' );
     1400
     1401        // Reset global before asserting.
     1402        $concatenate_scripts = $old_value;
     1403
     1404        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one-concat-dep-2,two-concat-dep-2,three-concat-dep-2,four-concat-dep-2,five-concat-dep-2,six-concat-dep-2&amp;ver={$wp_version}'></script>\n";
     1405        $expected .= "<script type='text/javascript' src='/main-script.js' id='deferred-script-2-js' defer data-wp-strategy='defer'></script>\n";
     1406
     1407        $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered as deferred after other blocking scripts are registered. Deferred scripts should not be part of the script concat loader query string. ' );
     1408    }
     1409
     1410    /**
    671411     * @ticket 42804
    681412     */
    691413    public function test_wp_enqueue_script_with_html5_support_does_not_contain_type_attribute() {
     1414        global $wp_version;
    701415        add_theme_support( 'html5', array( 'script' ) );
    711416
     
    751420        wp_enqueue_script( 'empty-deps-no-version', 'example.com' );
    761421
    77         $ver      = get_bloginfo( 'version' );
    78         $expected = "<script src='http://example.com?ver=$ver' id='empty-deps-no-version-js'></script>\n";
     1422        $expected = "<script src='http://example.com?ver={$wp_version}' id='empty-deps-no-version-js'></script>\n";
    791423
    801424        $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     
    841428     * Test the different protocol references in wp_enqueue_script
    851429     *
     1430     * @ticket 16560
     1431     *
    861432     * @global WP_Scripts $wp_scripts
    87      * @ticket 16560
    881433     */
    891434    public function test_protocols() {
    901435        // Init.
    91         global $wp_scripts;
     1436        global $wp_scripts, $wp_version;
    921437        $base_url_backup      = $wp_scripts->base_url;
    931438        $wp_scripts->base_url = 'http://example.com/wordpress';
    941439        $expected             = '';
    95         $ver                  = get_bloginfo( 'version' );
    961440
    971441        // Try with an HTTP reference.
    981442        wp_enqueue_script( 'jquery-http', 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
    99         $expected .= "<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver=$ver' id='jquery-http-js'></script>\n";
     1443        $expected .= "<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver={$wp_version}' id='jquery-http-js'></script>\n";
    1001444
    1011445        // Try with an HTTPS reference.
    1021446        wp_enqueue_script( 'jquery-https', 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
    103         $expected .= "<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver=$ver' id='jquery-https-js'></script>\n";
     1447        $expected .= "<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver={$wp_version}' id='jquery-https-js'></script>\n";
    1041448
    1051449        // Try with an automatic protocol reference (//).
    1061450        wp_enqueue_script( 'jquery-doubleslash', '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
    107         $expected .= "<script type='text/javascript' src='//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver=$ver' id='jquery-doubleslash-js'></script>\n";
     1451        $expected .= "<script type='text/javascript' src='//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver={$wp_version}' id='jquery-doubleslash-js'></script>\n";
    1081452
    1091453        // Try with a local resource and an automatic protocol reference (//).
    1101454        $url = '//my_plugin/script.js';
    1111455        wp_enqueue_script( 'plugin-script', $url );
    112         $expected .= "<script type='text/javascript' src='$url?ver=$ver' id='plugin-script-js'></script>\n";
     1456        $expected .= "<script type='text/javascript' src='$url?ver={$wp_version}' id='plugin-script-js'></script>\n";
    1131457
    1141458        // Try with a bad protocol.
    1151459        wp_enqueue_script( 'jquery-ftp', 'ftp://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
    116         $expected .= "<script type='text/javascript' src='{$wp_scripts->base_url}ftp://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver=$ver' id='jquery-ftp-js'></script>\n";
     1460        $expected .= "<script type='text/javascript' src='{$wp_scripts->base_url}ftp://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver={$wp_version}' id='jquery-ftp-js'></script>\n";
    1171461
    1181462        // Go!
     
    1301474     */
    1311475    public function test_script_concatenation() {
    132         global $wp_scripts;
     1476        global $wp_scripts, $wp_version;
    1331477
    1341478        $wp_scripts->do_concat    = true;
    135         $wp_scripts->default_dirs = array( '/directory/' );
    136 
    137         wp_enqueue_script( 'one', '/directory/script.js' );
    138         wp_enqueue_script( 'two', '/directory/script.js' );
    139         wp_enqueue_script( 'three', '/directory/script.js' );
     1479        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1480
     1481        wp_enqueue_script( 'one', $this->default_scripts_dir . 'script.js' );
     1482        wp_enqueue_script( 'two', $this->default_scripts_dir . 'script.js' );
     1483        wp_enqueue_script( 'three', $this->default_scripts_dir . 'script.js' );
    1401484
    1411485        wp_print_scripts();
    1421486        $print_scripts = get_echo( '_print_scripts' );
    1431487
    144         $ver      = get_bloginfo( 'version' );
    145         $expected = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one,two,three&amp;ver={$ver}'></script>\n";
     1488        $expected = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one,two,three&amp;ver={$wp_version}'></script>\n";
    1461489
    1471490        $this->assertSame( $expected, $print_scripts );
     
    2061549
    2071550    /**
    208      * Testing `wp_script_add_data` with an anvalid key.
     1551     * Testing `wp_script_add_data` with an invalid key.
    2091552     *
    2101553     * @ticket 16024
     
    3341677        $expected_footer  = "<script type='text/javascript' src='/parent.js' id='parent-js'></script>\n";
    3351678
    336         $this->assertSame( $expected_header, $header );
    337         $this->assertSame( $expected_footer, $footer );
     1679        $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
     1680        $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
    3381681    }
    3391682
     
    3551698        $expected_footer .= "<script type='text/javascript' src='/parent.js' id='parent-js'></script>\n";
    3561699
    357         $this->assertSame( $expected_header, $header );
    358         $this->assertSame( $expected_footer, $footer );
     1700        $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
     1701        $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
    3591702    }
    3601703
     
    3861729        $expected_footer .= "<script type='text/javascript' src='/parent-footer.js' id='parent-footer-js'></script>\n";
    3871730
    388         $this->assertSame( $expected_header, $header );
    389         $this->assertSame( $expected_footer, $footer );
     1731        $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
     1732        $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
    3901733    }
    3911734
     
    4171760        $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
    4181761
    419         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1762        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4201763    }
    4211764
     
    4301773        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    4311774
    432         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1775        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4331776    }
    4341777
     
    4451788        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    4461789
    447         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1790        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4481791    }
    4491792
     
    4581801        $expected = "<script type='text/javascript' id='test-example-js-before'>\nconsole.log(\"before\");\n</script>\n";
    4591802
    460         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1803        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4611804    }
    4621805
     
    4711814        $expected = "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    4721815
    473         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1816        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4741817    }
    4751818
     
    4861829        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    4871830
    488         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1831        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    4891832    }
    4901833
     
    5031846        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\nconsole.log(\"after\");\n</script>\n";
    5041847
    505         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1848        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    5061849    }
    5071850
     
    5201863        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    5211864
    522         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1865        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    5231866    }
    5241867
     
    5271870     */
    5281871    public function test_wp_add_inline_script_before_with_concat() {
    529         global $wp_scripts;
     1872        global $wp_scripts, $wp_version;
    5301873
    5311874        $wp_scripts->do_concat    = true;
    532         $wp_scripts->default_dirs = array( '/directory/' );
    533 
    534         wp_enqueue_script( 'one', '/directory/one.js' );
    535         wp_enqueue_script( 'two', '/directory/two.js' );
    536         wp_enqueue_script( 'three', '/directory/three.js' );
     1875        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1876
     1877        wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
     1878        wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
     1879        wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
    5371880
    5381881        wp_add_inline_script( 'one', 'console.log("before one");', 'before' );
    5391882        wp_add_inline_script( 'two', 'console.log("before two");', 'before' );
    5401883
    541         $ver       = get_bloginfo( 'version' );
    5421884        $expected  = "<script type='text/javascript' id='one-js-before'>\nconsole.log(\"before one\");\n</script>\n";
    543         $expected .= "<script type='text/javascript' src='/directory/one.js?ver={$ver}' id='one-js'></script>\n";
     1885        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}one.js?ver={$wp_version}' id='one-js'></script>\n";
    5441886        $expected .= "<script type='text/javascript' id='two-js-before'>\nconsole.log(\"before two\");\n</script>\n";
    545         $expected .= "<script type='text/javascript' src='/directory/two.js?ver={$ver}' id='two-js'></script>\n";
    546         $expected .= "<script type='text/javascript' src='/directory/three.js?ver={$ver}' id='three-js'></script>\n";
    547 
    548         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1887        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}two.js?ver={$wp_version}' id='two-js'></script>\n";
     1888        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}three.js?ver={$wp_version}' id='three-js'></script>\n";
     1889
     1890        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    5491891    }
    5501892
     
    5531895     */
    5541896    public function test_wp_add_inline_script_before_with_concat2() {
    555         global $wp_scripts;
     1897        global $wp_scripts, $wp_version;
    5561898
    5571899        $wp_scripts->do_concat    = true;
    558         $wp_scripts->default_dirs = array( '/directory/' );
    559 
    560         wp_enqueue_script( 'one', '/directory/one.js' );
    561         wp_enqueue_script( 'two', '/directory/two.js' );
    562         wp_enqueue_script( 'three', '/directory/three.js' );
     1900        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1901
     1902        wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
     1903        wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
     1904        wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
    5631905
    5641906        wp_add_inline_script( 'one', 'console.log("before one");', 'before' );
    5651907
    566         $ver       = get_bloginfo( 'version' );
    5671908        $expected  = "<script type='text/javascript' id='one-js-before'>\nconsole.log(\"before one\");\n</script>\n";
    568         $expected .= "<script type='text/javascript' src='/directory/one.js?ver={$ver}' id='one-js'></script>\n";
    569         $expected .= "<script type='text/javascript' src='/directory/two.js?ver={$ver}' id='two-js'></script>\n";
    570         $expected .= "<script type='text/javascript' src='/directory/three.js?ver={$ver}' id='three-js'></script>\n";
    571 
    572         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1909        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}one.js?ver={$wp_version}' id='one-js'></script>\n";
     1910        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}two.js?ver={$wp_version}' id='two-js'></script>\n";
     1911        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}three.js?ver={$wp_version}' id='three-js'></script>\n";
     1912
     1913        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    5731914    }
    5741915
     
    5771918     */
    5781919    public function test_wp_add_inline_script_after_with_concat() {
    579         global $wp_scripts;
     1920        global $wp_scripts, $wp_version;
    5801921
    5811922        $wp_scripts->do_concat    = true;
    582         $wp_scripts->default_dirs = array( '/directory/' );
    583 
    584         wp_enqueue_script( 'one', '/directory/one.js' );
    585         wp_enqueue_script( 'two', '/directory/two.js' );
    586         wp_enqueue_script( 'three', '/directory/three.js' );
    587         wp_enqueue_script( 'four', '/directory/four.js' );
     1923        $wp_scripts->default_dirs = array( $this->default_scripts_dir );
     1924
     1925        wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
     1926        wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
     1927        wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
     1928        wp_enqueue_script( 'four', $this->default_scripts_dir . 'four.js' );
    5881929
    5891930        wp_add_inline_script( 'two', 'console.log("after two");' );
    5901931        wp_add_inline_script( 'three', 'console.log("after three");' );
    5911932
    592         $ver       = get_bloginfo( 'version' );
    593         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one&amp;ver={$ver}'></script>\n";
    594         $expected .= "<script type='text/javascript' src='/directory/two.js?ver={$ver}' id='two-js'></script>\n";
     1933        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one&amp;ver={$wp_version}'></script>\n";
     1934        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}two.js?ver={$wp_version}' id='two-js'></script>\n";
    5951935        $expected .= "<script type='text/javascript' id='two-js-after'>\nconsole.log(\"after two\");\n</script>\n";
    596         $expected .= "<script type='text/javascript' src='/directory/three.js?ver={$ver}' id='three-js'></script>\n";
     1936        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}three.js?ver={$wp_version}' id='three-js'></script>\n";
    5971937        $expected .= "<script type='text/javascript' id='three-js-after'>\nconsole.log(\"after three\");\n</script>\n";
    598         $expected .= "<script type='text/javascript' src='/directory/four.js?ver={$ver}' id='four-js'></script>\n";
    599 
    600         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1938        $expected .= "<script type='text/javascript' src='{$this->default_scripts_dir}four.js?ver={$wp_version}' id='four-js'></script>\n";
     1939
     1940        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    6011941    }
    6021942
     
    6271967
    6281968        $this->assertSame( $expected_localized, get_echo( 'wp_print_scripts' ) );
    629         $this->assertSame( $expected, $wp_scripts->print_html );
     1969        $this->assertEqualMarkup( $expected, $wp_scripts->print_html );
    6301970        $this->assertTrue( $wp_scripts->do_concat );
    6311971    }
     
    6351975     */
    6361976    public function test_wp_add_inline_script_after_with_concat_and_core_dependency() {
    637         global $wp_scripts;
     1977        global $wp_scripts, $wp_version;
    6381978
    6391979        wp_default_scripts( $wp_scripts );
     
    6421982        $wp_scripts->do_concat = true;
    6431983
    644         $ver       = get_bloginfo( 'version' );
    645         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$ver}'></script>\n";
     1984        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$wp_version}'></script>\n";
    6461985        $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
    6471986        $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
     
    6531992        $print_scripts = get_echo( '_print_scripts' );
    6541993
    655         $this->assertSame( $expected, $print_scripts );
     1994        $this->assertEqualMarkup( $expected, $print_scripts );
    6561995    }
    6571996
     
    6601999     */
    6612000    public function test_wp_add_inline_script_after_with_concat_and_conditional_and_core_dependency() {
    662         global $wp_scripts;
     2001        global $wp_scripts, $wp_version;
    6632002
    6642003        wp_default_scripts( $wp_scripts );
     
    6672006        $wp_scripts->do_concat = true;
    6682007
    669         $ver       = get_bloginfo( 'version' );
    670         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$ver}'></script>\n";
     2008        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$wp_version}'></script>\n";
    6712009        $expected .= "<!--[if gte IE 9]>\n";
    6722010        $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
     
    6812019        $print_scripts = get_echo( '_print_scripts' );
    6822020
    683         $this->assertSame( $expected, $print_scripts );
     2021        $this->assertEqualMarkup( $expected, $print_scripts );
    6842022    }
    6852023
     
    6882026     */
    6892027    public function test_wp_add_inline_script_before_with_concat_and_core_dependency() {
    690         global $wp_scripts;
     2028        global $wp_scripts, $wp_version;
    6912029
    6922030        wp_default_scripts( $wp_scripts );
     
    6962034        $wp_scripts->do_concat = true;
    6972035
    698         $ver       = get_bloginfo( 'version' );
    699         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$ver}'></script>\n";
     2036        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate&amp;ver={$wp_version}'></script>\n";
    7002037        $expected .= "<script type='text/javascript' id='test-example-js-before'>\nconsole.log(\"before\");\n</script>\n";
    7012038        $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
     
    7072044        $print_scripts = get_echo( '_print_scripts' );
    7082045
    709         $this->assertSame( $expected, $print_scripts );
     2046        $this->assertEqualMarkup( $expected, $print_scripts );
    7102047    }
    7112048
     
    7142051     */
    7152052    public function test_wp_add_inline_script_before_after_concat_with_core_dependency() {
    716         global $wp_scripts;
     2053        global $wp_scripts, $wp_version;
    7172054
    7182055        wp_default_scripts( $wp_scripts );
     
    7222059        $wp_scripts->do_concat = true;
    7232060
    724         $ver       = get_bloginfo( 'version' );
    725         $suffix    = wp_scripts_get_suffix();
    726         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate,wp-polyfill-inert,regenerator-runtime,wp-polyfill,wp-dom-ready,wp-hooks&amp;ver={$ver}'></script>\n";
     2061        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core,jquery-migrate,wp-polyfill-inert,regenerator-runtime,wp-polyfill,wp-dom-ready,wp-hooks&amp;ver={$wp_version}'></script>\n";
    7272062        $expected .= "<script type='text/javascript' id='test-example-js-before'>\nconsole.log(\"before\");\n</script>\n";
    7282063        $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
     
    7592094        );
    7602095
    761         $this->assertSameIgnoreEOL( $expected, $print_scripts );
     2096        $this->assertEqualMarkup( $expected, $print_scripts );
    7622097    }
    7632098
     
    7912126
    7922127        $tail = substr( $print_scripts, strrpos( $print_scripts, "<script type='text/javascript' src='/customize-dependency.js' id='customize-dependency-js'>" ) );
    793         $this->assertSame( $expected_tail, $tail );
     2128        $this->assertEqualMarkup( $expected_tail, $tail );
    7942129    }
    7952130
     
    7982133     */
    7992134    public function test_wp_add_inline_script_after_for_core_scripts_with_concat_is_limited_and_falls_back_to_no_concat() {
    800         global $wp_scripts;
     2135        global $wp_scripts, $wp_version;
    8012136
    8022137        $wp_scripts->do_concat    = true;
     
    8092144        wp_enqueue_script( 'four', '/wp-includes/js/script4.js' );
    8102145
    811         $ver       = get_bloginfo( 'version' );
    812         $expected  = "<script type='text/javascript' src='/wp-includes/js/script.js?ver={$ver}' id='one-js'></script>\n";
     2146        $expected  = "<script type='text/javascript' src='/wp-includes/js/script.js?ver={$wp_version}' id='one-js'></script>\n";
    8132147        $expected .= "<script type='text/javascript' id='one-js-after'>\nconsole.log(\"after one\");\n</script>\n";
    814         $expected .= "<script type='text/javascript' src='/wp-includes/js/script2.js?ver={$ver}' id='two-js'></script>\n";
    815         $expected .= "<script type='text/javascript' src='/wp-includes/js/script3.js?ver={$ver}' id='three-js'></script>\n";
    816         $expected .= "<script type='text/javascript' src='/wp-includes/js/script4.js?ver={$ver}' id='four-js'></script>\n";
    817 
    818         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     2148        $expected .= "<script type='text/javascript' src='/wp-includes/js/script2.js?ver={$wp_version}' id='two-js'></script>\n";
     2149        $expected .= "<script type='text/javascript' src='/wp-includes/js/script3.js?ver={$wp_version}' id='three-js'></script>\n";
     2150        $expected .= "<script type='text/javascript' src='/wp-includes/js/script4.js?ver={$wp_version}' id='four-js'></script>\n";
     2151
     2152        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    8192153    }
    8202154
     
    8232157     */
    8242158    public function test_wp_add_inline_script_before_third_core_script_prints_two_concat_scripts() {
    825         global $wp_scripts;
     2159        global $wp_scripts, $wp_version;
    8262160
    8272161        $wp_scripts->do_concat    = true;
     
    8342168        wp_enqueue_script( 'four', '/wp-includes/js/script4.js' );
    8352169
    836         $ver       = get_bloginfo( 'version' );
    837         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one,two&amp;ver={$ver}'></script>\n";
     2170        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=one,two&amp;ver={$wp_version}'></script>\n";
    8382171        $expected .= "<script type='text/javascript' id='three-js-before'>\nconsole.log(\"before three\");\n</script>\n";
    839         $expected .= "<script type='text/javascript' src='/wp-includes/js/script3.js?ver={$ver}' id='three-js'></script>\n";
    840         $expected .= "<script type='text/javascript' src='/wp-includes/js/script4.js?ver={$ver}' id='four-js'></script>\n";
    841 
    842         $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     2172        $expected .= "<script type='text/javascript' src='/wp-includes/js/script3.js?ver={$wp_version}' id='three-js'></script>\n";
     2173        $expected .= "<script type='text/javascript' src='/wp-includes/js/script4.js?ver={$wp_version}' id='four-js'></script>\n";
     2174
     2175        $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
     2176    }
     2177
     2178    /**
     2179     * Data provider to test get_inline_script_data and get_inline_script_tag.
     2180     *
     2181     * @return array[]
     2182     */
     2183    public function data_provider_to_test_get_inline_script() {
     2184        return array(
     2185            'before-blocking' => array(
     2186                'position'       => 'before',
     2187                'inline_scripts' => array(
     2188                    '/*before foo 1*/',
     2189                ),
     2190                'delayed'        => false,
     2191                'expected_data'  => '/*before foo 1*/',
     2192                'expected_tag'   => "<script id='foo-js-before' type='text/javascript'>\n/*before foo 1*/\n</script>\n",
     2193            ),
     2194            'after-blocking'  => array(
     2195                'position'       => 'after',
     2196                'inline_scripts' => array(
     2197                    '/*after foo 1*/',
     2198                    '/*after foo 2*/',
     2199                ),
     2200                'delayed'        => false,
     2201                'expected_data'  => "/*after foo 1*/\n/*after foo 2*/",
     2202                'expected_tag'   => "<script id='foo-js-after' type='text/javascript'>\n/*after foo 1*/\n/*after foo 2*/\n</script>\n",
     2203            ),
     2204            'before-delayed'  => array(
     2205                'position'       => 'before',
     2206                'inline_scripts' => array(
     2207                    '/*before foo 1*/',
     2208                ),
     2209                'delayed'        => true,
     2210                'expected_data'  => '/*before foo 1*/',
     2211                'expected_tag'   => "<script id='foo-js-before' type='text/javascript'>\n/*before foo 1*/\n</script>\n",
     2212            ),
     2213            'after-delayed'   => array(
     2214                'position'       => 'after',
     2215                'inline_scripts' => array(
     2216                    '/*after foo 1*/',
     2217                    '/*after foo 2*/',
     2218                ),
     2219                'delayed'        => true,
     2220                'expected_data'  => "/*after foo 1*/\n/*after foo 2*/",
     2221                'expected_tag'   => "<script id='foo-js-after' type='text/javascript'>\n/*after foo 1*/\n/*after foo 2*/\n</script>\n",
     2222            ),
     2223        );
     2224    }
     2225
     2226    /**
     2227     * Test getting inline scripts.
     2228     *
     2229     * @covers WP_Scripts::get_inline_script_data
     2230     * @covers WP_Scripts::get_inline_script_tag
     2231     * @covers WP_Scripts::print_inline_script
     2232     *
     2233     * @expectedDeprecated WP_Scripts::print_inline_script
     2234     *
     2235     * @dataProvider data_provider_to_test_get_inline_script
     2236     *
     2237     * @param string   $position       Position.
     2238     * @param string[] $inline_scripts Inline scripts.
     2239     * @param bool     $delayed        Delayed.
     2240     * @param string   $expected_data  Expected data.
     2241     * @param string   $expected_tag   Expected tag.
     2242     */
     2243    public function test_get_inline_script( $position, $inline_scripts, $delayed, $expected_data, $expected_tag ) {
     2244        global $wp_scripts;
     2245
     2246        $deps = array();
     2247        if ( $delayed ) {
     2248            $wp_scripts->add( 'dep', 'https://example.com/dependency.js', array(), false ); // TODO: Cannot pass strategy to $args e.g. array( 'strategy' => 'defer' )
     2249            $wp_scripts->add_data( 'dep', 'strategy', 'defer' );
     2250            $deps[] = 'dep';
     2251        }
     2252
     2253        $handle = 'foo';
     2254        $wp_scripts->add( $handle, 'https://example.com/foo.js', $deps );
     2255        if ( $delayed ) {
     2256            $wp_scripts->add_data( $handle, 'strategy', 'defer' );
     2257        }
     2258
     2259        $this->assertSame( '', $wp_scripts->get_inline_script_data( $handle, $position ) );
     2260        $this->assertSame( '', $wp_scripts->get_inline_script_tag( $handle, $position ) );
     2261        $this->assertFalse( $wp_scripts->print_inline_script( $handle, $position, false ) );
     2262        ob_start();
     2263        $output = $wp_scripts->print_inline_script( $handle, $position, true );
     2264        $this->assertSame( '', ob_get_clean() );
     2265        $this->assertFalse( $output );
     2266
     2267        foreach ( $inline_scripts as $inline_script ) {
     2268            $wp_scripts->add_inline_script( $handle, $inline_script, $position );
     2269        }
     2270
     2271        $this->assertSame( $expected_data, $wp_scripts->get_inline_script_data( $handle, $position ) );
     2272        $this->assertSame( $expected_data, $wp_scripts->print_inline_script( $handle, $position, false ) );
     2273        $this->assertEqualMarkup(
     2274            $expected_tag,
     2275            $wp_scripts->get_inline_script_tag( $handle, $position )
     2276        );
     2277        ob_start();
     2278        $output = $wp_scripts->print_inline_script( $handle, $position, true );
     2279        $this->assertEqualMarkup( $expected_tag, ob_get_clean() );
     2280        $this->assertEquals( $expected_data, $output );
    8432281    }
    8442282
     
    10442482     *
    10452483     * @ticket 41871
     2484     *
    10462485     * @covers ::wp_enqueue_code_editor
    10472486     */
     
    11312570     *
    11322571     * @ticket 41871
     2572     *
    11332573     * @covers ::wp_enqueue_code_editor
    11342574     */
     
    12142654     *
    12152655     * @ticket 41871
     2656     *
    12162657     * @covers ::wp_enqueue_code_editor
    12172658     */
     
    13112752     *
    13122753     * @ticket 41871
     2754     *
    13132755     * @covers ::wp_enqueue_code_editor
    13142756     */
     
    14032845    /**
    14042846     * @ticket 52534
     2847     *
    14052848     * @covers ::wp_localize_script
    14062849     *
     
    14572900    /**
    14582901     * @ticket 55628
     2902     *
    14592903     * @covers ::wp_set_script_translations
    14602904     */
    14612905    public function test_wp_external_wp_i18n_print_order() {
    1462         global $wp_scripts;
     2906        global $wp_scripts, $wp_version;
    14632907
    14642908        $wp_scripts->do_concat    = true;
     
    14812925
    14822926        // The non-default script should end concatenation and maintain order.
    1483         $ver       = get_bloginfo( 'version' );
    1484         $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core&amp;ver={$ver}'></script>\n";
     2927        $expected  = "<script type='text/javascript' src='/wp-admin/load-scripts.php?c=0&amp;load%5Bchunk_0%5D=jquery-core&amp;ver={$wp_version}'></script>\n";
    14852928        $expected .= "<script type='text/javascript' src='/plugins/wp-i18n.js' id='wp-i18n-js'></script>\n";
    14862929        $expected .= "<script type='text/javascript' src='/default/common.js' id='common-js'></script>\n";
     
    14882931        $this->assertSame( $expected, $print_scripts );
    14892932    }
     2933
     2934    /**
     2935     * Parse an HTML markup fragment.
     2936     *
     2937     * @param string $markup Markup.
     2938     * @return DOMElement Body element wrapping supplied markup fragment.
     2939     */
     2940    protected function parse_markup_fragment( $markup ) {
     2941        $dom = new DOMDocument();
     2942        $dom->loadHTML(
     2943            "<!DOCTYPE html><html><head><meta charset=utf8></head><body>{$markup}</body></html>"
     2944        );
     2945
     2946        /** @var DOMElement $body */
     2947        $body = $dom->getElementsByTagName( 'body' )->item( 0 );
     2948
     2949        // Trim whitespace nodes added before/after which can be added when parsing.
     2950        foreach ( array( $body->firstChild, $body->lastChild ) as $node ) {
     2951            if ( $node instanceof DOMText && '' === trim( $node->data ) ) {
     2952                $body->removeChild( $node );
     2953            }
     2954        }
     2955
     2956        return $body;
     2957    }
     2958
     2959    /**
     2960     * Assert markup is equal.
     2961     *
     2962     * @param string $expected Expected markup.
     2963     * @param string $actual   Actual markup.
     2964     * @param string $message  Message.
     2965     */
     2966    protected function assertEqualMarkup( $expected, $actual, $message = '' ) {
     2967        $this->assertEquals(
     2968            $this->parse_markup_fragment( $expected ),
     2969            $this->parse_markup_fragment( $actual ),
     2970            $message
     2971        );
     2972    }
    14902973}
Note: See TracChangeset for help on using the changeset viewer.