diff --git a/.jshintrc b/.jshintrc
index 20dadcd1fa..f0e6b02645 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -19,11 +19,13 @@
"globals": {
"_": false,
"Backbone": false,
+ "console": false,
"jQuery": false,
"JSON": false,
"wp": false,
"export": false,
"module": false,
- "require": false
+ "require": false,
+ "Set": false
}
}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 453ef5c794..02e1b657e2 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -68,7 +68,9 @@
+
+
diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php
index ddaa270c6d..857f1e948b 100644
--- a/src/wp-includes/class-wp-scripts.php
+++ b/src/wp-includes/class-wp-scripts.php
@@ -133,6 +133,24 @@ class WP_Scripts extends WP_Dependencies {
*/
private $type_attr = '';
+ /**
+ * Holds a mapping of dependents (as handles) for a given script handle.
+ * Used to optimize recursive dependency tree checks.
+ *
+ * @since 6.3.0
+ * @var array
+ */
+ private $dependents_map = array();
+
+ /**
+ * Holds a reference to the delayed (non-blocking) script loading strategies.
+ * Used by methods that validate loading strategies.
+ *
+ * @since 6.3.0
+ * @var string[]
+ */
+ private $delayed_strategies = array( 'defer', 'async' );
+
/**
* Constructor.
*
@@ -284,29 +302,27 @@ class WP_Scripts extends WP_Dependencies {
$ver = $ver ? $ver . '&' . $this->args[ $handle ] : $this->args[ $handle ];
}
- $src = $obj->src;
- $cond_before = '';
- $cond_after = '';
- $conditional = isset( $obj->extra['conditional'] ) ? $obj->extra['conditional'] : '';
+ $src = $obj->src;
+ $strategy = $this->get_eligible_loading_strategy( $handle );
+ $intended_strategy = (string) $this->get_data( $handle, 'strategy' );
+ $cond_before = '';
+ $cond_after = '';
+ $conditional = isset( $obj->extra['conditional'] ) ? $obj->extra['conditional'] : '';
+
+ if ( ! $this->is_delayed_strategy( $intended_strategy ) ) {
+ $intended_strategy = '';
+ }
if ( $conditional ) {
$cond_before = "\n";
}
- $before_handle = $this->print_inline_script( $handle, 'before', false );
- $after_handle = $this->print_inline_script( $handle, 'after', false );
+ $before_script = $this->get_inline_script_tag( $handle, 'before' );
+ $after_script = $this->get_inline_script_tag( $handle, 'after' );
- if ( $before_handle ) {
- $before_handle = sprintf( "\n", $this->type_attr, esc_attr( $handle ), $before_handle );
- }
-
- if ( $after_handle ) {
- $after_handle = sprintf( "\n", $this->type_attr, esc_attr( $handle ), $after_handle );
- }
-
- if ( $before_handle || $after_handle ) {
- $inline_script_tag = $cond_before . $before_handle . $after_handle . $cond_after;
+ if ( $before_script || $after_script ) {
+ $inline_script_tag = $cond_before . $before_script . $after_script . $cond_after;
} else {
$inline_script_tag = '';
}
@@ -333,7 +349,10 @@ class WP_Scripts extends WP_Dependencies {
*/
$srce = apply_filters( 'script_loader_src', $src, $handle );
- if ( $this->in_default_dir( $srce ) && ( $before_handle || $after_handle || $translations_stop_concat ) ) {
+ if (
+ $this->in_default_dir( $srce )
+ && ( $before_script || $after_script || $translations_stop_concat || $this->is_delayed_strategy( $strategy ) )
+ ) {
$this->do_concat = false;
// Have to print the so-far concatenated scripts right away to maintain the right order.
@@ -390,9 +409,16 @@ class WP_Scripts extends WP_Dependencies {
return true;
}
- $tag = $translations . $cond_before . $before_handle;
- $tag .= sprintf( "\n", $this->type_attr, $src, esc_attr( $handle ) );
- $tag .= $after_handle . $cond_after;
+ $tag = $translations . $cond_before . $before_script;
+ $tag .= sprintf(
+ "\n",
+ $this->type_attr,
+ $src, // Value is escaped above.
+ esc_attr( $handle ),
+ $strategy ? " {$strategy}" : '',
+ $intended_strategy ? " data-wp-strategy='{$intended_strategy}'" : ''
+ );
+ $tag .= $after_script . $cond_after;
/**
* Filters the HTML script tag of an enqueued script.
@@ -445,29 +471,97 @@ class WP_Scripts extends WP_Dependencies {
* Prints inline scripts registered for a specific handle.
*
* @since 4.5.0
+ * @deprecated 6.3.0 Use methods get_inline_script_tag() or get_inline_script_data() instead.
*
- * @param string $handle Name of the script to add the inline script to.
+ * @param string $handle Name of the script to print inline scripts for.
* Must be lowercase.
* @param string $position Optional. Whether to add the inline script
* before the handle or after. Default 'after'.
- * @param bool $display Optional. Whether to print the script
- * instead of just returning it. Default true.
- * @return string|false Script on success, false otherwise.
+ * @param bool $display Optional. Whether to print the script tag
+ * instead of just returning the script data. Default true.
+ * @return string|false Script data on success, false otherwise.
*/
public function print_inline_script( $handle, $position = 'after', $display = true ) {
- $output = $this->get_data( $handle, $position );
+ _deprecated_function( __METHOD__, '6.3.0', 'WP_Scripts::get_inline_script_data() or WP_Scripts::get_inline_script_tag()' );
+ $output = $this->get_inline_script_data( $handle, $position );
if ( empty( $output ) ) {
return false;
}
- $output = trim( implode( "\n", $output ), "\n" );
-
if ( $display ) {
- printf( "\n", $this->type_attr, esc_attr( $handle ), esc_attr( $position ), $output );
+ echo $this->get_inline_script_tag( $handle, $position );
+ }
+ return $output;
+ }
+
+ /**
+ * Gets data for inline scripts registered for a specific handle.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle Name of the script to get data for.
+ * Must be lowercase.
+ * @param string $position Optional. Whether to add the inline script
+ * before the handle or after. Default 'after'.
+ * @return string Inline script, which may be empty string.
+ */
+ public function get_inline_script_data( $handle, $position = 'after' ) {
+ $data = $this->get_data( $handle, $position );
+ if ( empty( $data ) || ! is_array( $data ) ) {
+ return '';
}
- return $output;
+ return trim( implode( "\n", $data ), "\n" );
+ }
+
+ /**
+ * Gets unaliased dependencies.
+ *
+ * An alias is a dependency whose src is false. It is used as a way to bundle multiple dependencies in a single
+ * handle. This in effect flattens an alias dependency tree.
+ *
+ * @since 6.3.0
+ *
+ * @param string[] $deps Dependency handles.
+ * @return string[] Unaliased handles.
+ */
+ private function get_unaliased_deps( array $deps ) {
+ $flattened = array();
+ foreach ( $deps as $dep ) {
+ if ( ! isset( $this->registered[ $dep ] ) ) {
+ continue;
+ }
+
+ if ( $this->registered[ $dep ]->src ) {
+ $flattened[] = $dep;
+ } elseif ( $this->registered[ $dep ]->deps ) {
+ array_push( $flattened, ...$this->get_unaliased_deps( $this->registered[ $dep ]->deps ) );
+ }
+ }
+ return $flattened;
+ }
+
+ /**
+ * Gets tags for inline scripts registered for a specific handle.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle Name of the script to get associated inline script tag for.
+ * Must be lowercase.
+ * @param string $position Optional. Whether to get tag for inline
+ * scripts in the before or after position. Default 'after'.
+ * @return string Inline script, which may be empty string.
+ */
+ public function get_inline_script_tag( $handle, $position = 'after' ) {
+ $js = $this->get_inline_script_data( $handle, $position );
+ if ( empty( $js ) ) {
+ return '';
+ }
+
+ $id = "{$handle}-js-{$position}";
+
+ return wp_get_inline_script_tag( $js, compact( 'id' ) );
}
/**
@@ -714,6 +808,199 @@ JS;
return false;
}
+ /**
+ * This overrides the add_data method from WP_Dependencies, to support normalizing of $args.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle Name of the item. Should be unique.
+ * @param string $key The data key.
+ * @param mixed $value The data value.
+ * @return bool True on success, false on failure.
+ */
+ public function add_data( $handle, $key, $value ) {
+ if ( ! isset( $this->registered[ $handle ] ) ) {
+ return false;
+ }
+
+ if ( 'strategy' === $key ) {
+ if ( ! empty( $value ) && ! $this->is_delayed_strategy( $value ) ) {
+ _doing_it_wrong(
+ __METHOD__,
+ sprintf(
+ /* translators: 1: $strategy, 2: $handle */
+ __( 'Invalid strategy `%1$s` defined for `%2$s` during script registration.' ),
+ $value,
+ $handle
+ ),
+ '6.3.0'
+ );
+ return false;
+ } elseif ( ! $this->registered[ $handle ]->src && $this->is_delayed_strategy( $value ) ) {
+ _doing_it_wrong(
+ __METHOD__,
+ sprintf(
+ /* translators: 1: $strategy, 2: $handle */
+ __( 'Cannot supply a strategy `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ),
+ $value,
+ $handle
+ ),
+ '6.3.0'
+ );
+ return false;
+ }
+ }
+ return parent::add_data( $handle, $key, $value );
+ }
+
+ /**
+ * Gets all dependents of a script.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle The script handle.
+ * @return string[] Script handles.
+ */
+ private function get_dependents( $handle ) {
+ // Check if dependents map for the handle in question is present. If so, use it.
+ if ( isset( $this->dependents_map[ $handle ] ) ) {
+ return $this->dependents_map[ $handle ];
+ }
+
+ $dependents = array();
+
+ // Iterate over all registered scripts, finding dependents of the script passed to this method.
+ foreach ( $this->registered as $registered_handle => $args ) {
+ if ( in_array( $handle, $args->deps, true ) ) {
+ $dependents[] = $registered_handle;
+ }
+ }
+
+ // Add the handles dependents to the map to ease future lookups.
+ $this->dependents_map[ $handle ] = $dependents;
+
+ return $dependents;
+ }
+
+ /**
+ * Checks if the strategy passed is a valid delayed (non-blocking) strategy.
+ *
+ * @since 6.3.0
+ *
+ * @param string $strategy The strategy to check.
+ * @return bool True if $strategy is one of the delayed strategies, otherwise false.
+ */
+ private function is_delayed_strategy( $strategy ) {
+ return in_array(
+ $strategy,
+ $this->delayed_strategies,
+ true
+ );
+ }
+
+ /**
+ * Gets the best eligible loading strategy for a script.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle The script handle.
+ * @return string The best eligible loading strategy.
+ */
+ private function get_eligible_loading_strategy( $handle ) {
+ $eligible = $this->filter_eligible_strategies( $handle );
+
+ // Bail early once we know the eligible strategy is blocking.
+ if ( empty( $eligible ) ) {
+ return '';
+ }
+
+ return in_array( 'async', $eligible, true ) ? 'async' : 'defer';
+ }
+
+ /**
+ * Filter the list of eligible loading strategies for a script.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle The script handle.
+ * @param string[]|null $eligible Optional. The list of strategies to filter. Default null.
+ * @param array $checked Optional. An array of already checked script handles, used to avoid recursive loops.
+ * @return string[] A list of eligible loading strategies that could be used.
+ */
+ private function filter_eligible_strategies( $handle, $eligible = null, $checked = array() ) {
+ // If no strategies are being passed, all strategies are eligible.
+ if ( null === $eligible ) {
+ $eligible = $this->delayed_strategies;
+ }
+
+ // If this handle was already checked, return early.
+ if ( isset( $checked[ $handle ] ) ) {
+ return $eligible;
+ }
+
+ // Mark this handle as checked.
+ $checked[ $handle ] = true;
+
+ // If this handle isn't registered, don't filter anything and return.
+ if ( ! isset( $this->registered[ $handle ] ) ) {
+ return $eligible;
+ }
+
+ // If the handle is not enqueued, don't filter anything and return.
+ if ( ! $this->query( $handle, 'enqueued' ) ) {
+ return $eligible;
+ }
+
+ $is_alias = (bool) ! $this->registered[ $handle ]->src;
+ $intended_strategy = $this->get_data( $handle, 'strategy' );
+
+ // For non-alias handles, an empty intended strategy filters all strategies.
+ if ( ! $is_alias && empty( $intended_strategy ) ) {
+ return array();
+ }
+
+ // Handles with inline scripts attached in the 'after' position cannot be delayed.
+ if ( $this->has_inline_script( $handle, 'after' ) ) {
+ return array();
+ }
+
+ // If the intended strategy is 'defer', filter out 'async'.
+ if ( 'defer' === $intended_strategy ) {
+ $eligible = array( 'defer' );
+ }
+
+ $dependents = $this->get_dependents( $handle );
+
+ // Recursively filter eligible strategies for dependents.
+ foreach ( $dependents as $dependent ) {
+ // Bail early once we know the eligible strategy is blocking.
+ if ( empty( $eligible ) ) {
+ return array();
+ }
+
+ $eligible = $this->filter_eligible_strategies( $dependent, $eligible, $checked );
+ }
+
+ return $eligible;
+ }
+
+ /**
+ * Gets data for inline scripts registered for a specific handle.
+ *
+ * @since 6.3.0
+ *
+ * @param string $handle Name of the script to get data for. Must be lowercase.
+ * @param string $position The position of the inline script.
+ * @return bool Whether the handle has an inline script (either before or after).
+ */
+ private function has_inline_script( $handle, $position = null ) {
+ if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) {
+ return (bool) $this->get_data( $handle, $position );
+ }
+
+ return (bool) ( $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ) );
+ }
+
/**
* Resets class properties.
*
diff --git a/src/wp-includes/functions.wp-scripts.php b/src/wp-includes/functions.wp-scripts.php
index 64b9368344..aab583d6e3 100644
--- a/src/wp-includes/functions.wp-scripts.php
+++ b/src/wp-includes/functions.wp-scripts.php
@@ -157,6 +157,7 @@ function wp_add_inline_script( $handle, $data, $position = 'after' ) {
*
* @since 2.1.0
* @since 4.3.0 A return value was added.
+ * @since 6.3.0 The $in_footer parameter of type boolean was overloaded to be an $args parameter of type array.
*
* @param string $handle Name of the script. Should be unique.
* @param string|false $src Full URL of the script, or path of the script relative to the WordPress root directory.
@@ -166,20 +167,32 @@ function wp_add_inline_script( $handle, $data, $position = 'after' ) {
* as a query string for cache busting purposes. If version is set to false, a version
* number is automatically added equal to current installed WordPress version.
* If set to null, no version is added.
- * @param bool $in_footer Optional. Whether to enqueue the script before `
` instead of in the `
`.
- * Default 'false'.
+ * @param array|bool $args {
+ * Optional. An array of additional script loading strategies. Default empty array.
+ * Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false.
+ *
+ * @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
+ * @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
+ * }
* @return bool Whether the script has been registered. True on success, false on failure.
*/
-function wp_register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
+function wp_register_script( $handle, $src, $deps = array(), $ver = false, $args = array() ) {
+ if ( ! is_array( $args ) ) {
+ $args = array(
+ 'in_footer' => (bool) $args,
+ );
+ }
_wp_scripts_maybe_doing_it_wrong( __FUNCTION__, $handle );
$wp_scripts = wp_scripts();
$registered = $wp_scripts->add( $handle, $src, $deps, $ver );
- if ( $in_footer ) {
+ if ( ! empty( $args['in_footer'] ) ) {
$wp_scripts->add_data( $handle, 'group', 1 );
}
-
+ if ( ! empty( $args['strategy'] ) ) {
+ $wp_scripts->add_data( $handle, 'strategy', $args['strategy'] );
+ }
return $registered;
}
@@ -331,6 +344,7 @@ function wp_deregister_script( $handle ) {
* @see WP_Dependencies::enqueue()
*
* @since 2.1.0
+ * @since 6.3.0 The $in_footer parameter of type boolean was overloaded to be an $args parameter of type array.
*
* @param string $handle Name of the script. Should be unique.
* @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
@@ -340,24 +354,36 @@ function wp_deregister_script( $handle ) {
* as a query string for cache busting purposes. If version is set to false, a version
* number is automatically added equal to current installed WordPress version.
* If set to null, no version is added.
- * @param bool $in_footer Optional. Whether to enqueue the script before `` instead of in the `
`.
- * Default 'false'.
+ * @param array|bool $args {
+ * Optional. An array of additional script loading strategies. Default empty array.
+ * Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false.
+ *
+ * @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
+ * @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
+ * }
*/
-function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $in_footer = false ) {
+function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $args = array() ) {
_wp_scripts_maybe_doing_it_wrong( __FUNCTION__, $handle );
$wp_scripts = wp_scripts();
- if ( $src || $in_footer ) {
+ if ( $src || ! empty( $args ) ) {
$_handle = explode( '?', $handle );
+ if ( ! is_array( $args ) ) {
+ $args = array(
+ 'in_footer' => (bool) $args,
+ );
+ }
if ( $src ) {
$wp_scripts->add( $_handle[0], $src, $deps, $ver );
}
-
- if ( $in_footer ) {
+ if ( ! empty( $args['in_footer'] ) ) {
$wp_scripts->add_data( $_handle[0], 'group', 1 );
}
+ if ( ! empty( $args['strategy'] ) ) {
+ $wp_scripts->add_data( $_handle[0], 'strategy', $args['strategy'] );
+ }
}
$wp_scripts->enqueue( $handle );
diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php
index 78642c1178..6395f18f8d 100644
--- a/tests/phpunit/tests/dependencies/scripts.php
+++ b/tests/phpunit/tests/dependencies/scripts.php
@@ -10,17 +10,35 @@
* @covers ::wp_set_script_translations
*/
class Tests_Dependencies_Scripts extends WP_UnitTestCase {
+
+ /**
+ * @var WP_Scripts
+ */
protected $old_wp_scripts;
+ /**
+ * @var WP_Styles
+ */
+ protected $old_wp_styles;
+
protected $wp_scripts_print_translations_output;
+ /**
+ * Stores a string reference to a default scripts directory name, utilised by certain tests.
+ *
+ * @var string
+ */
+ protected $default_scripts_dir = '/directory/';
+
public function set_up() {
parent::set_up();
$this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null;
+ $this->old_wp_styles = isset( $GLOBALS['wp_styles'] ) ? $GLOBALS['wp_styles'] : null;
remove_action( 'wp_default_scripts', 'wp_default_scripts' );
remove_action( 'wp_default_scripts', 'wp_default_packages' );
$GLOBALS['wp_scripts'] = new WP_Scripts();
$GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
+ $GLOBALS['wp_styles'] = new WP_Styles();
$this->wp_scripts_print_translations_output = <<
@@ -36,6 +54,7 @@ JS;
public function tear_down() {
$GLOBALS['wp_scripts'] = $this->old_wp_scripts;
+ $GLOBALS['wp_styles'] = $this->old_wp_styles;
add_action( 'wp_default_scripts', 'wp_default_scripts' );
parent::tear_down();
}
@@ -46,14 +65,15 @@ JS;
* @ticket 11315
*/
public function test_wp_enqueue_script() {
+ global $wp_version;
+
wp_enqueue_script( 'no-deps-no-version', 'example.com', array() );
wp_enqueue_script( 'empty-deps-no-version', 'example.com' );
wp_enqueue_script( 'empty-deps-version', 'example.com', array(), 1.2 );
wp_enqueue_script( 'empty-deps-null-version', 'example.com', array(), null );
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
- $expected .= "\n";
+ $expected = "\n";
+ $expected .= "\n";
$expected .= "\n";
$expected .= "\n";
@@ -63,10 +83,1335 @@ JS;
$this->assertSame( '', get_echo( 'wp_print_scripts' ) );
}
+ /**
+ * Gets delayed strategies as a data provider.
+ *
+ * @return array[] Delayed strategies.
+ */
+ public function data_provider_delayed_strategies() {
+ return array(
+ 'defer' => array( 'defer' ),
+ 'async' => array( 'async' ),
+ );
+ }
+
+ /**
+ * Tests that inline scripts in the `after` position, attached to delayed main scripts, remain unaffected.
+ *
+ * If the main script with delayed loading strategy has an `after` inline script,
+ * the inline script should not be affected.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_inline_script_tag
+ * @covers ::wp_add_inline_script
+ * @covers ::wp_enqueue_script
+ *
+ * @dataProvider data_provider_delayed_strategies
+ *
+ * @param string $strategy Strategy.
+ */
+ public function test_after_inline_script_with_delayed_main_script( $strategy ) {
+ wp_enqueue_script( 'ms-isa-1', 'http://example.org/ms-isa-1.js', array(), null, compact( 'strategy' ) );
+ wp_add_inline_script( 'ms-isa-1', 'console.log("after one");', 'after' );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $expected .= wp_get_inline_script_tag(
+ "console.log(\"after one\");\n",
+ array(
+ 'id' => 'ms-isa-1-js-after',
+ )
+ );
+ $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a deferred main script, are failing to print/execute.' );
+ }
+
+ /**
+ * Tests that inline scripts in the `after` position, attached to a blocking main script, are rendered as javascript.
+ *
+ * If a main script with a `blocking` strategy has an `after` inline script,
+ * the inline script should be rendered as type='text/javascript'.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_inline_script_tag
+ * @covers ::wp_add_inline_script
+ * @covers ::wp_enqueue_script
+ */
+ public function test_after_inline_script_with_blocking_main_script() {
+ wp_enqueue_script( 'ms-insa-3', 'http://example.org/ms-insa-3.js', array(), null );
+ wp_add_inline_script( 'ms-insa-3', 'console.log("after one");', 'after' );
+ $output = get_echo( 'wp_print_scripts' );
+
+ $expected = "\n";
+ $expected .= wp_get_inline_script_tag(
+ "console.log(\"after one\");\n",
+ array(
+ 'id' => 'ms-insa-3-js-after',
+ )
+ );
+
+ $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a blocking main script, are failing to print/execute.' );
+ }
+
+ /**
+ * Tests that inline scripts in the `before` position, attached to a delayed inline main script, results in all
+ * dependents being delayed.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_inline_script_tag
+ * @covers ::wp_add_inline_script
+ * @covers ::wp_enqueue_script
+ *
+ * @dataProvider data_provider_delayed_strategies
+ *
+ * @param string $strategy
+ */
+ public function test_before_inline_scripts_with_delayed_main_script( $strategy ) {
+ wp_enqueue_script( 'ds-i1-1', 'http://example.org/ds-i1-1.js', array(), null, compact( 'strategy' ) );
+ wp_add_inline_script( 'ds-i1-1', 'console.log("before first");', 'before' );
+ wp_enqueue_script( 'ds-i1-2', 'http://example.org/ds-i1-2.js', array(), null, compact( 'strategy' ) );
+ wp_enqueue_script( 'ds-i1-3', 'http://example.org/ds-i1-3.js', array(), null, compact( 'strategy' ) );
+ 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' ) );
+ wp_add_inline_script( 'ms-i1-1', 'console.log("before last");', 'before' );
+ $output = get_echo( 'wp_print_scripts' );
+
+ $expected = wp_get_inline_script_tag(
+ "console.log(\"before first\");\n",
+ array(
+ 'id' => 'ds-i1-1-js-before',
+ )
+ );
+ $expected .= "\n";
+ $expected .= "\n";
+ $expected .= "\n";
+ $expected .= wp_get_inline_script_tag(
+ "console.log(\"before last\");\n",
+ array(
+ 'id' => 'ms-i1-1-js-before',
+ 'type' => 'text/javascript',
+ )
+ );
+ $expected .= "\n";
+
+ $this->assertSame( $expected, $output, 'Inline scripts in the "before" position, that are attached to a deferred main script, are failing to print/execute.' );
+ }
+
+ /**
+ * Tests that scripts registered with an async strategy print with the async attribute.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_valid_async_registration() {
+ // No dependents, No dependencies then async.
+ wp_enqueue_script( 'main-script-a1', '/main-script-a1.js', array(), null, array( 'strategy' => 'async' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $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.' );
+ }
+
+ /**
+ * Tests that dependents of a blocking dependency script are free to contain any strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ *
+ * @dataProvider data_provider_delayed_strategies
+ *
+ * @param string $strategy Strategy.
+ */
+ public function test_delayed_dependent_with_blocking_dependency( $strategy ) {
+ wp_enqueue_script( 'dependency-script-a2', '/dependency-script-a2.js', array(), null );
+ wp_enqueue_script( 'main-script-a2', '/main-script-a2.js', array( 'dependency-script-a2' ), null, compact( 'strategy' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "";
+ $this->assertStringContainsString( $expected, $output, 'Dependents of a blocking dependency are free to have any strategy.' );
+ }
+
+ /**
+ * Tests that blocking dependents force delayed dependencies to become blocking.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ *
+ * @dataProvider data_provider_delayed_strategies
+ * @param string $strategy Strategy.
+ */
+ public function test_blocking_dependent_with_delayed_dependency( $strategy ) {
+ wp_enqueue_script( 'main-script-a3', '/main-script-a3.js', array(), null, compact( 'strategy' ) );
+ wp_enqueue_script( 'dependent-script-a3', '/dependent-script-a3.js', array( 'main-script-a3' ), null );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "";
+ $this->assertStringContainsString( $expected, $output, 'Blocking dependents must force delayed dependencies to become blocking.' );
+ }
+
+ /**
+ * Tests that only enqueued dependents effect the eligible loading strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ *
+ * @dataProvider data_provider_delayed_strategies
+ * @param string $strategy Strategy.
+ */
+ public function test_delayed_dependent_with_blocking_dependency_not_enqueued( $strategy ) {
+ wp_enqueue_script( 'main-script-a4', '/main-script-a4.js', array(), null, compact( 'strategy' ) );
+ // This dependent is registered but not enqueued, so it should not factor into the eligible loading strategy.
+ wp_register_script( 'dependent-script-a4', '/dependent-script-a4.js', array( 'main-script-a4' ), null );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "";
+ $this->assertStringContainsString( $expected, $output, 'Only enqueued dependents should affect the eligible strategy.' );
+ }
+
+ /**
+ * Data provider for test_filter_eligible_strategies.
+ *
+ * @return array
+ */
+ public function get_data_to_filter_eligible_strategies() {
+ return array(
+ 'no_dependents' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'one_delayed_dependent' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'one_blocking_dependent' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
+ return 'foo';
+ },
+ 'expected' => array(),
+ ),
+ 'one_blocking_dependent_not_enqueued' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_register_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ), // Because bar was not enqueued, only foo was.
+ ),
+ 'two_delayed_dependents' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'baz', 'https://example.com/baz.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'recursion_not_delayed' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null );
+ return 'foo';
+ },
+ 'expected' => array(),
+ ),
+ 'recursion_yes_delayed' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'recursion_triple_level' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'baz' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'baz', 'https://example.com/bar.js', array( 'bar' ), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'async_only_with_async_dependency' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'async' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer', 'async' ),
+ ),
+ 'async_only_with_defer_dependency' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'async_only_with_blocking_dependency' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
+ wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null );
+ return 'foo';
+ },
+ 'expected' => array(),
+ ),
+ 'defer_with_inline_after_script' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_add_inline_script( 'foo', 'console.log("foo")', 'after' );
+ return 'foo';
+ },
+ 'expected' => array(),
+ ),
+ 'defer_with_inline_before_script' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_add_inline_script( 'foo', 'console.log("foo")', 'before' );
+ return 'foo';
+ },
+ 'expected' => array( 'defer' ),
+ ),
+ 'async_with_inline_after_script' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
+ wp_add_inline_script( 'foo', 'console.log("foo")', 'after' );
+ return 'foo';
+ },
+ 'expected' => array(),
+ ),
+ 'async_with_inline_before_script' => array(
+ 'set_up' => static function () {
+ wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) );
+ wp_add_inline_script( 'foo', 'console.log("foo")', 'before' );
+ return 'foo';
+ },
+ 'expected' => array( 'defer', 'async' ),
+ ),
+ );
+ }
+
+ /**
+ * Tests that the filter_eligible_strategies method works as expected and returns the correct value.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::filter_eligible_strategies
+ *
+ * @dataProvider get_data_to_filter_eligible_strategies
+ *
+ * @param callable $set_up Set up.
+ * @param bool $async_only Async only.
+ * @param bool $expected Expected return value.
+ */
+ public function test_filter_eligible_strategies( $set_up, $expected ) {
+ $handle = $set_up();
+
+ $wp_scripts_reflection = new ReflectionClass( WP_Scripts::class );
+ $filter_eligible_strategies = $wp_scripts_reflection->getMethod( 'filter_eligible_strategies' );
+ $filter_eligible_strategies->setAccessible( true );
+ $this->assertSame( $expected, $filter_eligible_strategies->invokeArgs( wp_scripts(), array( $handle ) ), 'Expected return value of WP_Scripts::filter_eligible_strategies to match.' );
+ }
+
+ /**
+ * Register test script.
+ *
+ * @param string $handle Dependency handle to enqueue.
+ * @param string $strategy Strategy to use for dependency.
+ * @param string[] $deps Dependencies for the script.
+ * @param bool $in_footer Whether to print the script in the footer.
+ */
+ protected function register_test_script( $handle, $strategy, $deps = array(), $in_footer = false ) {
+ wp_register_script(
+ $handle,
+ add_query_arg(
+ array(
+ 'script_event_log' => "$handle: script",
+ ),
+ 'https://example.com/external.js'
+ ),
+ $deps,
+ null
+ );
+ if ( 'blocking' !== $strategy ) {
+ wp_script_add_data( $handle, 'strategy', $strategy );
+ }
+ }
+
+ /**
+ * Enqueue test script.
+ *
+ * @param string $handle Dependency handle to enqueue.
+ * @param string $strategy Strategy to use for dependency.
+ * @param string[] $deps Dependencies for the script.
+ * @param bool $in_footer Whether to print the script in the footer.
+ */
+ protected function enqueue_test_script( $handle, $strategy, $deps = array(), $in_footer = false ) {
+ $this->register_test_script( $handle, $strategy, $deps, $in_footer );
+ wp_enqueue_script( $handle );
+ }
+
+ /**
+ * Adds test inline script.
+ *
+ * @param string $handle Dependency handle to enqueue.
+ * @param string $position Position.
+ */
+ protected function add_test_inline_script( $handle, $position ) {
+ wp_add_inline_script( $handle, sprintf( 'scriptEventLog.push( %s )', wp_json_encode( "{$handle}: {$position} inline" ) ), $position );
+ }
+
+ /**
+ * Data provider to test various strategy dependency chains.
+ *
+ * @return array[]
+ */
+ public function data_provider_to_test_various_strategy_dependency_chains() {
+ return array(
+ 'async-dependent-with-one-blocking-dependency' => array(
+ 'set_up' => function () {
+ $handle1 = 'blocking-not-async-without-dependency';
+ $handle2 = 'async-with-blocking-dependency';
+ $this->enqueue_test_script( $handle1, 'blocking', array() );
+ $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
+ foreach ( array( $handle1, $handle2 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "blocking-not-async-without-dependency: before inline" )
+
+
+
+
+
+
+HTML
+ ,
+ /*
+ * Note: The above comma must be on its own line in PHP<7.3 and not after the `HTML` identifier
+ * terminating the heredoc. Otherwise, a syntax error is raised with the line number being wildly wrong:
+ *
+ * PHP Parse error: syntax error, unexpected '' (T_ENCAPSED_AND_WHITESPACE), expecting '-' or identifier (T_STRING) or variable (T_VARIABLE) or number (T_NUM_STRING)
+ */
+ ),
+ 'async-with-async-dependencies' => array(
+ 'set_up' => function () {
+ $handle1 = 'async-no-dependency';
+ $handle2 = 'async-one-async-dependency';
+ $handle3 = 'async-two-async-dependencies';
+ $this->enqueue_test_script( $handle1, 'async', array() );
+ $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
+ $this->enqueue_test_script( $handle3, 'async', array( $handle1, $handle2 ) );
+ foreach ( array( $handle1, $handle2, $handle3 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "async-no-dependency: before inline" )
+
+
+
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'async-with-blocking-dependency' => array(
+ 'set_up' => function () {
+ $handle1 = 'async-with-blocking-dependent';
+ $handle2 = 'blocking-dependent-of-async';
+ $this->enqueue_test_script( $handle1, 'async', array() );
+ $this->enqueue_test_script( $handle2, 'blocking', array( $handle1 ) );
+ foreach ( array( $handle1, $handle2 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "async-with-blocking-dependent: before inline" )
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'defer-with-async-dependency' => array(
+ 'set_up' => function () {
+ $handle1 = 'async-with-defer-dependent';
+ $handle2 = 'defer-dependent-of-async';
+ $this->enqueue_test_script( $handle1, 'async', array() );
+ $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
+ foreach ( array( $handle1, $handle2 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "async-with-defer-dependent: before inline" )
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'blocking-bundle-of-none-with-inline-scripts-and-defer-dependent' => array(
+ 'set_up' => function () {
+ $handle1 = 'blocking-bundle-of-none';
+ $handle2 = 'defer-dependent-of-blocking-bundle-of-none';
+
+ wp_register_script( $handle1, false, array(), null );
+ $this->add_test_inline_script( $handle1, 'before' );
+ $this->add_test_inline_script( $handle1, 'after' );
+
+ // Note: the before script for this will be blocking because the dependency is blocking.
+ $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
+ $this->add_test_inline_script( $handle2, 'before' );
+ $this->add_test_inline_script( $handle2, 'after' );
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "blocking-bundle-of-none: before inline" )
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'blocking-bundle-of-two-with-defer-dependent' => array(
+ 'set_up' => function () {
+ $handle1 = 'blocking-bundle-of-two';
+ $handle2 = 'blocking-bundle-member-one';
+ $handle3 = 'blocking-bundle-member-two';
+ $handle4 = 'defer-dependent-of-blocking-bundle-of-two';
+
+ wp_register_script( $handle1, false, array( $handle2, $handle3 ), null );
+ $this->enqueue_test_script( $handle2, 'blocking' );
+ $this->enqueue_test_script( $handle3, 'blocking' );
+ $this->enqueue_test_script( $handle4, 'defer', array( $handle1 ) );
+
+ foreach ( array( $handle2, $handle3, $handle4 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "blocking-bundle-member-one: before inline" )
+
+
+
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'defer-bundle-of-none-with-inline-scripts-and-defer-dependents' => array(
+ 'set_up' => function () {
+ $handle1 = 'defer-bundle-of-none';
+ $handle2 = 'defer-dependent-of-defer-bundle-of-none';
+
+ // The eligible loading strategy for this will be forced to be blocking when rendered since $src = false.
+ wp_register_script( $handle1, false, array(), null );
+ wp_scripts()->registered[ $handle1 ]->extra['strategy'] = 'defer'; // Bypass wp_script_add_data() which should no-op with _doing_it_wrong() because of $src=false.
+ $this->add_test_inline_script( $handle1, 'before' );
+ $this->add_test_inline_script( $handle1, 'after' );
+
+ // Note: the before script for this will be blocking because the dependency is blocking.
+ $this->enqueue_test_script( $handle2, 'defer', array( $handle1 ) );
+ $this->add_test_inline_script( $handle2, 'before' );
+ $this->add_test_inline_script( $handle2, 'after' );
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "defer-bundle-of-none: before inline" )
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'defer-dependent-with-blocking-and-defer-dependencies' => array(
+ 'set_up' => function () {
+ $handle1 = 'blocking-dependency-with-defer-following-dependency';
+ $handle2 = 'defer-dependency-with-blocking-preceding-dependency';
+ $handle3 = 'defer-dependent-of-blocking-and-defer-dependencies';
+ $this->enqueue_test_script( $handle1, 'blocking', array() );
+ $this->enqueue_test_script( $handle2, 'defer', array() );
+ $this->enqueue_test_script( $handle3, 'defer', array( $handle1, $handle2 ) );
+
+ foreach ( array( $handle1, $handle2, $handle3 ) as $dep ) {
+ $this->add_test_inline_script( $dep, 'before' );
+ $this->add_test_inline_script( $dep, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "blocking-dependency-with-defer-following-dependency: before inline" )
+
+
+
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'defer-dependent-with-defer-and-blocking-dependencies' => array(
+ 'set_up' => function () {
+ $handle1 = 'defer-dependency-with-blocking-following-dependency';
+ $handle2 = 'blocking-dependency-with-defer-preceding-dependency';
+ $handle3 = 'defer-dependent-of-defer-and-blocking-dependencies';
+ $this->enqueue_test_script( $handle1, 'defer', array() );
+ $this->enqueue_test_script( $handle2, 'blocking', array() );
+ $this->enqueue_test_script( $handle3, 'defer', array( $handle1, $handle2 ) );
+
+ foreach ( array( $handle1, $handle2, $handle3 ) as $dep ) {
+ $this->add_test_inline_script( $dep, 'before' );
+ $this->add_test_inline_script( $dep, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "defer-dependency-with-blocking-following-dependency: before inline" )
+
+
+
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'async-with-defer-dependency' => array(
+ 'set_up' => function () {
+ $handle1 = 'defer-with-async-dependent';
+ $handle2 = 'async-dependent-of-defer';
+ $this->enqueue_test_script( $handle1, 'defer', array() );
+ $this->enqueue_test_script( $handle2, 'async', array( $handle1 ) );
+ foreach ( array( $handle1, $handle2 ) as $handle ) {
+ $this->add_test_inline_script( $handle, 'before' );
+ $this->add_test_inline_script( $handle, 'after' );
+ }
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "defer-with-async-dependent: before inline" )
+
+
+
+
+
+
+HTML
+ ,
+ ),
+ 'defer-with-before-inline-script' => array(
+ 'set_up' => function () {
+ // Note this should NOT result in no delayed-inline-script-loader script being added.
+ $handle = 'defer-with-before-inline';
+ $this->enqueue_test_script( $handle, 'defer', array() );
+ $this->add_test_inline_script( $handle, 'before' );
+ },
+ 'expected_markup' => <<
+scriptEventLog.push( "defer-with-before-inline: before inline" )
+
+
+HTML
+ ,
+ ),
+ 'defer-with-after-inline-script' => array(
+ 'set_up' => function () {
+ // Note this SHOULD result in delayed-inline-script-loader script being added.
+ $handle = 'defer-with-after-inline';
+ $this->enqueue_test_script( $handle, 'defer', array() );
+ $this->add_test_inline_script( $handle, 'after' );
+ },
+ 'expected_markup' => <<
+
+HTML
+ ,
+ ),
+ 'jquery-deferred' => array(
+ 'set_up' => function () {
+ $wp_scripts = wp_scripts();
+ wp_default_scripts( $wp_scripts );
+ foreach ( $wp_scripts->registered['jquery']->deps as $jquery_dep ) {
+ $wp_scripts->registered[ $jquery_dep ]->add_data( 'strategy', 'defer' );
+ $wp_scripts->registered[ $jquery_dep ]->ver = null; // Just to avoid markup changes in the test when jQuery is upgraded.
+ }
+ wp_enqueue_script( 'theme-functions', 'https://example.com/theme-functions.js', array( 'jquery' ), null, array( 'strategy' => 'defer' ) );
+ },
+ 'expected_markup' => <<
+
+
+HTML
+ ,
+ ),
+ 'nested-aliases' => array(
+ 'set_up' => function () {
+ $outer_alias_handle = 'outer-bundle-of-two';
+ $inner_alias_handle = 'inner-bundle-of-two';
+
+ // The outer alias contains a blocking member, as well as a nested alias that contains defer scripts.
+ wp_register_script( $outer_alias_handle, false, array( $inner_alias_handle, 'outer-bundle-leaf-member' ), null );
+ $this->register_test_script( 'outer-bundle-leaf-member', 'blocking', array() );
+
+ // Inner alias only contains delay scripts.
+ wp_register_script( $inner_alias_handle, false, array( 'inner-bundle-member-one', 'inner-bundle-member-two' ), null );
+ $this->register_test_script( 'inner-bundle-member-one', 'defer', array() );
+ $this->register_test_script( 'inner-bundle-member-two', 'defer', array() );
+
+ $this->enqueue_test_script( 'defer-dependent-of-nested-aliases', 'defer', array( $outer_alias_handle ) );
+ $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'before' );
+ $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'after' );
+ },
+ 'expected_markup' => <<
+
+
+
+
+
+HTML
+ ,
+ ),
+
+ 'async-alias-members-with-defer-dependency' => array(
+ 'set_up' => function () {
+ $alias_handle = 'async-alias';
+ $async_handle1 = 'async1';
+ $async_handle2 = 'async2';
+
+ wp_register_script( $alias_handle, false, array( $async_handle1, $async_handle2 ), null );
+ $this->register_test_script( $async_handle1, 'async', array() );
+ $this->register_test_script( $async_handle2, 'async', array() );
+
+ $this->enqueue_test_script( 'defer-dependent-of-async-aliases', 'defer', array( $alias_handle ) );
+ },
+ 'expected_markup' => <<
+
+
+HTML
+ ,
+ ),
+ );
+ }
+
+ /**
+ * Tests that various loading strategy dependency chains function as expected.
+ *
+ * @covers ::wp_enqueue_script()
+ * @covers ::wp_add_inline_script()
+ * @covers ::wp_print_scripts()
+ * @covers WP_Scripts::get_inline_script_tag
+ *
+ * @dataProvider data_provider_to_test_various_strategy_dependency_chains
+ *
+ * @param callable $set_up Set up.
+ * @param string $expected_markup Expected markup.
+ */
+ public function test_various_strategy_dependency_chains( $set_up, $expected_markup ) {
+ $set_up();
+ $actual_markup = get_echo( 'wp_print_scripts' );
+ $this->assertEqualMarkup( trim( $expected_markup ), trim( $actual_markup ), "Actual markup:\n{$actual_markup}" );
+ }
+
+ /**
+ * Tests that defer is the final strategy when registering a script using defer, that has no dependents/dependencies.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_defer_having_no_dependents_nor_dependencies() {
+ wp_enqueue_script( 'main-script-d1', 'http://example.com/main-script-d1.js', array(), null, array( 'strategy' => 'defer' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertStringContainsString( $expected, $output, 'Expected defer, as there is no dependent or dependency' );
+ }
+
+ /**
+ * Tests that a script registered with defer remains deferred when all dependencies are either deferred or blocking.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_defer_dependent_and_varied_dependencies() {
+ wp_enqueue_script( 'dependency-script-d2-1', 'http://example.com/dependency-script-d2-1.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependency-script-d2-2', 'http://example.com/dependency-script-d2-2.js', array(), null );
+ wp_enqueue_script( 'dependency-script-d2-3', 'http://example.com/dependency-script-d2-3.js', array( 'dependency-script-d2-2' ), null, array( 'strategy' => 'defer' ) );
+ 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' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependencies are either deferred or blocking' );
+ }
+
+ /**
+ * Tests that scripts registered with defer remain deferred when all dependents are also deferred.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_all_defer_dependencies() {
+ wp_enqueue_script( 'main-script-d3', 'http://example.com/main-script-d3.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d3-1', 'http://example.com/dependent-script-d3-1.js', array( 'main-script-d3' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d3-2', 'http://example.com/dependent-script-d3-2.js', array( 'dependent-script-d3-1' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d3-3', 'http://example.com/dependent-script-d3-3.js', array( 'dependent-script-d3-2' ), null, array( 'strategy' => 'defer' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependents have defer loading strategy' );
+ }
+
+ /**
+ * Tests that dependents that are async but attached to a deferred main script, print with defer as opposed to async.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers ::wp_enqueue_script
+ */
+ public function test_defer_with_async_dependent() {
+ // case with one async dependent.
+ wp_enqueue_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d4-1', '/dependent-script-d4-1.js', array( 'main-script-d4' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d4-2', '/dependent-script-d4-2.js', array( 'dependent-script-d4-1' ), null, array( 'strategy' => 'async' ) );
+ wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $expected .= "\n";
+ $expected .= "\n";
+ $expected .= "\n";
+
+ $this->assertSame( $expected, $output, 'Scripts registered as defer but that have dependents that are async are expected to have said dependents deferred.' );
+ }
+
+ /**
+ * Tests that scripts registered as defer become blocking when their dependents chain are all blocking.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_invalid_defer_registration() {
+ // Main script is defer and all dependent are not defer. Then main script will have blocking(or no) strategy.
+ wp_enqueue_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d4-1', '/dependent-script-d4-1.js', array( 'main-script-d4' ), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'dependent-script-d4-2', '/dependent-script-d4-2.js', array( 'dependent-script-d4-1' ), null );
+ wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertStringContainsString( $expected, $output, 'Scripts registered as defer but that have all dependents with no strategy, should become blocking (no strategy).' );
+ }
+
+ /**
+ * Tests that scripts registered as default/blocking remain as such when they have no dependencies.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers WP_Scripts::get_eligible_loading_strategy
+ * @covers WP_Scripts::filter_eligible_strategies
+ * @covers ::wp_enqueue_script
+ */
+ public function test_loading_strategy_with_valid_blocking_registration() {
+ wp_enqueue_script( 'main-script-b1', '/main-script-b1.js', array(), null );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertSame( $expected, $output, 'Scripts registered with a "blocking" strategy, and who have no dependencies, should have no loading strategy attributes printed.' );
+
+ // strategy args not set.
+ wp_enqueue_script( 'main-script-b2', '/main-script-b2.js', array(), null, array() );
+ $output = get_echo( 'wp_print_scripts' );
+ $expected = "\n";
+ $this->assertSame( $expected, $output, 'Scripts registered with no strategy assigned, and who have no dependencies, should have no loading strategy attributes printed.' );
+ }
+
+ /**
+ * Tests that scripts registered for the head do indeed end up there.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_register_script
+ */
+ public function test_scripts_targeting_head() {
+ wp_register_script( 'header-old', '/header-old.js', array(), null, false );
+ wp_register_script( 'header-new', '/header-new.js', array( 'header-old' ), null, array( 'in_footer' => false ) );
+ wp_enqueue_script( 'enqueue-header-old', '/enqueue-header-old.js', array( 'header-new' ), null, false );
+ wp_enqueue_script( 'enqueue-header-new', '/enqueue-header-new.js', array( 'enqueue-header-old' ), null, array( 'in_footer' => false ) );
+
+ $actual_header = get_echo( 'wp_print_head_scripts' );
+ $actual_footer = get_echo( 'wp_print_scripts' );
+
+ $expected_header = "\n";
+ $expected_header .= "\n";
+ $expected_header .= "\n";
+ $expected_header .= "\n";
+
+ $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.' );
+ $this->assertEmpty( $actual_footer, 'Expected footer to be empty since all scripts were for head.' );
+ }
+
+ /**
+ * Test that scripts registered for the footer do indeed end up there.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_register_script
+ */
+ public function test_scripts_targeting_footer() {
+ wp_register_script( 'footer-old', '/footer-old.js', array(), null, true );
+ wp_register_script( 'footer-new', '/footer-new.js', array( 'footer-old' ), null, array( 'in_footer' => true ) );
+ wp_enqueue_script( 'enqueue-footer-old', '/enqueue-footer-old.js', array( 'footer-new' ), null, true );
+ wp_enqueue_script( 'enqueue-footer-new', '/enqueue-footer-new.js', array( 'enqueue-footer-old' ), null, array( 'in_footer' => true ) );
+
+ $actual_header = get_echo( 'wp_print_head_scripts' );
+ $actual_footer = get_echo( 'wp_print_scripts' );
+
+ $expected_footer = "\n";
+ $expected_footer .= "\n";
+ $expected_footer .= "\n";
+ $expected_footer .= "\n";
+
+ $this->assertEmpty( $actual_header, 'Expected header to be empty since all scripts targeted footer.' );
+ $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.' );
+ }
+
+ /**
+ * Data provider for test_setting_in_footer_and_strategy.
+ *
+ * @return array[]
+ */
+ public function get_data_for_test_setting_in_footer_and_strategy() {
+ return array(
+ // Passing in_footer and strategy via args array.
+ 'async_footer_in_args_array' => array(
+ 'set_up' => static function ( $handle ) {
+ $args = array(
+ 'in_footer' => true,
+ 'strategy' => 'async',
+ );
+ wp_enqueue_script( $handle, '/footer-async.js', array(), null, $args );
+ },
+ 'group' => 1,
+ 'strategy' => 'async',
+ ),
+
+ // Passing in_footer=true but no strategy.
+ 'blocking_footer_in_args_array' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_register_script( $handle, '/defaults.js', array(), null, array( 'in_footer' => true ) );
+ },
+ 'group' => 1,
+ 'strategy' => false,
+ ),
+
+ // Passing async strategy in script args array.
+ 'async_in_args_array' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_register_script( $handle, '/defaults.js', array(), null, array( 'strategy' => 'async' ) );
+ },
+ 'group' => false,
+ 'strategy' => 'async',
+ ),
+
+ // Passing empty array as 5th arg.
+ 'empty_args_array' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_register_script( $handle, '/defaults.js', array(), null, array() );
+ },
+ 'group' => false,
+ 'strategy' => false,
+ ),
+
+ // Passing no value as 5th arg.
+ 'undefined_args_param' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_register_script( $handle, '/defaults.js', array(), null );
+ },
+ 'group' => false,
+ 'strategy' => false,
+ ),
+
+ // Test backward compatibility, passing $in_footer=true as 5th arg.
+ 'passing_bool_as_args_param' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_enqueue_script( $handle, '/footer-async.js', array(), null, true );
+ },
+ 'group' => 1,
+ 'strategy' => false,
+ ),
+
+ // Test backward compatibility, passing $in_footer=true as 5th arg and setting strategy via wp_script_add_data().
+ 'bool_as_args_and_add_data' => array(
+ 'set_up' => static function ( $handle ) {
+ wp_register_script( $handle, '/footer-async.js', array(), null, true );
+ wp_script_add_data( $handle, 'strategy', 'defer' );
+ },
+ 'group' => 1,
+ 'strategy' => 'defer',
+ ),
+ );
+ }
+
+ /**
+ * Tests that scripts print in the correct group (head/footer) when using in_footer and assigning a strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers ::wp_register_script
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_script_add_data
+ *
+ * @dataProvider get_data_for_test_setting_in_footer_and_strategy
+ *
+ * @param callable $set_up Set up.
+ * @param int|false $expected_group Expected group.
+ * @param string|false $expected_strategy Expected strategy.
+ */
+ public function test_setting_in_footer_and_strategy( $set_up, $expected_group, $expected_strategy ) {
+ $handle = 'foo';
+ $set_up( $handle );
+ $this->assertSame( $expected_group, wp_scripts()->get_data( $handle, 'group' ) );
+ $this->assertSame( $expected_strategy, wp_scripts()->get_data( $handle, 'strategy' ) );
+ }
+
+ /**
+ * Tests that scripts print with no strategy when an incorrect strategy is passed during wp_register_script.
+ *
+ * For an invalid strategy defined during script registration, default to a blocking strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::add_data
+ * @covers ::wp_register_script
+ * @covers ::wp_enqueue_script
+ *
+ * @expectedIncorrectUsage WP_Scripts::add_data
+ */
+ public function test_script_strategy_doing_it_wrong_via_register() {
+ wp_register_script( 'invalid-strategy', '/defaults.js', array(), null, array( 'strategy' => 'random-strategy' ) );
+ wp_enqueue_script( 'invalid-strategy' );
+
+ $this->assertSame(
+ "\n",
+ get_echo( 'wp_print_scripts' )
+ );
+ }
+
+ /**
+ * Tests that scripts print with no strategy when an incorrect strategy is passed via wp_script_add_data().
+ *
+ * For an invalid strategy defined during script registration, default to a blocking strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::add_data
+ * @covers ::wp_script_add_data
+ * @covers ::wp_register_script
+ * @covers ::wp_enqueue_script
+ *
+ * @expectedIncorrectUsage WP_Scripts::add_data
+ */
+ public function test_script_strategy_doing_it_wrong_via_add_data() {
+ wp_register_script( 'invalid-strategy', '/defaults.js', array(), null );
+ wp_script_add_data( 'invalid-strategy', 'strategy', 'random-strategy' );
+ wp_enqueue_script( 'invalid-strategy' );
+
+ $this->assertSame(
+ "\n",
+ get_echo( 'wp_print_scripts' )
+ );
+ }
+
+ /**
+ * Tests that scripts print with no strategy when an incorrect strategy is passed during wp_enqueue_script.
+ *
+ * For an invalid strategy defined during script registration, default to a blocking strategy.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::add_data
+ * @covers ::wp_enqueue_script
+ *
+ * @expectedIncorrectUsage WP_Scripts::add_data
+ */
+ public function test_script_strategy_doing_it_wrong_via_enqueue() {
+ wp_enqueue_script( 'invalid-strategy', '/defaults.js', array(), null, array( 'strategy' => 'random-strategy' ) );
+
+ $this->assertSame(
+ "\n",
+ get_echo( 'wp_print_scripts' )
+ );
+ }
+
+ /**
+ * Tests that scripts registered with a deferred strategy are not included in the script concat loading query.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_register_script
+ */
+ public function test_concatenate_with_defer_strategy() {
+ global $wp_scripts, $concatenate_scripts, $wp_version;
+
+ $old_value = $concatenate_scripts;
+ $concatenate_scripts = true;
+
+ $wp_scripts->do_concat = true;
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
+
+ wp_register_script( 'one-concat-dep', $this->default_scripts_dir . 'script.js' );
+ wp_register_script( 'two-concat-dep', $this->default_scripts_dir . 'script.js' );
+ wp_register_script( 'three-concat-dep', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'main-defer-script', '/main-script.js', array( 'one-concat-dep', 'two-concat-dep', 'three-concat-dep' ), null, array( 'strategy' => 'defer' ) );
+
+ wp_print_scripts();
+ $print_scripts = get_echo( '_print_scripts' );
+
+ // Reset global before asserting.
+ $concatenate_scripts = $old_value;
+
+ $expected = "\n";
+ $expected .= "\n";
+
+ $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.' );
+ }
+
+ /**
+ * Test script concatenation with `async` main script.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_register_script
+ */
+ public function test_concatenate_with_async_strategy() {
+ global $wp_scripts, $concatenate_scripts, $wp_version;
+
+ $old_value = $concatenate_scripts;
+ $concatenate_scripts = true;
+
+ $wp_scripts->do_concat = true;
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
+
+ wp_enqueue_script( 'one-concat-dep-1', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'two-concat-dep-1', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'three-concat-dep-1', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'main-async-script-1', '/main-script.js', array(), null, array( 'strategy' => 'async' ) );
+
+ wp_print_scripts();
+ $print_scripts = get_echo( '_print_scripts' );
+
+ // Reset global before asserting.
+ $concatenate_scripts = $old_value;
+
+ $expected = "\n";
+ $expected .= "\n";
+
+ $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.' );
+ }
+
+ /**
+ * Tests that script concatenation remains correct when a main script is registered as deferred after other blocking
+ * scripts are registered.
+ *
+ * @ticket 12009
+ *
+ * @covers WP_Scripts::do_item
+ * @covers ::wp_enqueue_script
+ * @covers ::wp_register_script
+ */
+ public function test_concatenate_with_blocking_script_before_and_after_script_with_defer_strategy() {
+ global $wp_scripts, $concatenate_scripts, $wp_version;
+
+ $old_value = $concatenate_scripts;
+ $concatenate_scripts = true;
+
+ $wp_scripts->do_concat = true;
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
+
+ wp_enqueue_script( 'one-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'two-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'three-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'deferred-script-2', '/main-script.js', array(), null, array( 'strategy' => 'defer' ) );
+ wp_enqueue_script( 'four-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'five-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'six-concat-dep-2', $this->default_scripts_dir . 'script.js' );
+
+ wp_print_scripts();
+ $print_scripts = get_echo( '_print_scripts' );
+
+ // Reset global before asserting.
+ $concatenate_scripts = $old_value;
+
+ $expected = "\n";
+ $expected .= "\n";
+
+ $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. ' );
+ }
+
/**
* @ticket 42804
*/
public function test_wp_enqueue_script_with_html5_support_does_not_contain_type_attribute() {
+ global $wp_version;
add_theme_support( 'html5', array( 'script' ) );
$GLOBALS['wp_scripts'] = new WP_Scripts();
@@ -74,8 +1419,7 @@ JS;
wp_enqueue_script( 'empty-deps-no-version', 'example.com' );
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
+ $expected = "\n";
$this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
}
@@ -83,37 +1427,37 @@ JS;
/**
* Test the different protocol references in wp_enqueue_script
*
- * @global WP_Scripts $wp_scripts
* @ticket 16560
+ *
+ * @global WP_Scripts $wp_scripts
*/
public function test_protocols() {
// Init.
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
$base_url_backup = $wp_scripts->base_url;
$wp_scripts->base_url = 'http://example.com/wordpress';
$expected = '';
- $ver = get_bloginfo( 'version' );
// Try with an HTTP reference.
wp_enqueue_script( 'jquery-http', 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
- $expected .= "\n";
+ $expected .= "\n";
// Try with an HTTPS reference.
wp_enqueue_script( 'jquery-https', 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
- $expected .= "\n";
+ $expected .= "\n";
// Try with an automatic protocol reference (//).
wp_enqueue_script( 'jquery-doubleslash', '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
- $expected .= "\n";
+ $expected .= "\n";
// Try with a local resource and an automatic protocol reference (//).
$url = '//my_plugin/script.js';
wp_enqueue_script( 'plugin-script', $url );
- $expected .= "\n";
+ $expected .= "\n";
// Try with a bad protocol.
wp_enqueue_script( 'jquery-ftp', 'ftp://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' );
- $expected .= "\n";
+ $expected .= "\n";
// Go!
$this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
@@ -129,20 +1473,19 @@ JS;
* Test script concatenation.
*/
public function test_script_concatenation() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
$wp_scripts->do_concat = true;
- $wp_scripts->default_dirs = array( '/directory/' );
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
- wp_enqueue_script( 'one', '/directory/script.js' );
- wp_enqueue_script( 'two', '/directory/script.js' );
- wp_enqueue_script( 'three', '/directory/script.js' );
+ wp_enqueue_script( 'one', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'two', $this->default_scripts_dir . 'script.js' );
+ wp_enqueue_script( 'three', $this->default_scripts_dir . 'script.js' );
wp_print_scripts();
$print_scripts = get_echo( '_print_scripts' );
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
+ $expected = "\n";
$this->assertSame( $expected, $print_scripts );
}
@@ -205,7 +1548,7 @@ JS;
}
/**
- * Testing `wp_script_add_data` with an anvalid key.
+ * Testing `wp_script_add_data` with an invalid key.
*
* @ticket 16024
*/
@@ -333,8 +1676,8 @@ JS;
$expected_header .= "\n";
$expected_footer = "\n";
- $this->assertSame( $expected_header, $header );
- $this->assertSame( $expected_footer, $footer );
+ $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
+ $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
}
/**
@@ -354,8 +1697,8 @@ JS;
$expected_footer = "\n";
$expected_footer .= "\n";
- $this->assertSame( $expected_header, $header );
- $this->assertSame( $expected_footer, $footer );
+ $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
+ $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
}
/**
@@ -385,8 +1728,8 @@ JS;
$expected_footer .= "\n";
$expected_footer .= "\n";
- $this->assertSame( $expected_header, $header );
- $this->assertSame( $expected_footer, $footer );
+ $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
+ $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
}
/**
@@ -416,7 +1759,7 @@ JS;
$expected = "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -429,7 +1772,7 @@ JS;
$expected = "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -444,7 +1787,7 @@ JS;
$expected .= "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -457,7 +1800,7 @@ JS;
$expected = "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -470,7 +1813,7 @@ JS;
$expected = "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -485,7 +1828,7 @@ JS;
$expected = "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -502,7 +1845,7 @@ JS;
$expected .= "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -519,85 +1862,82 @@ JS;
$expected .= "\n";
$expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
* @ticket 14853
*/
public function test_wp_add_inline_script_before_with_concat() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
$wp_scripts->do_concat = true;
- $wp_scripts->default_dirs = array( '/directory/' );
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
- wp_enqueue_script( 'one', '/directory/one.js' );
- wp_enqueue_script( 'two', '/directory/two.js' );
- wp_enqueue_script( 'three', '/directory/three.js' );
+ wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
+ wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
+ wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
wp_add_inline_script( 'one', 'console.log("before one");', 'before' );
wp_add_inline_script( 'two', 'console.log("before two");', 'before' );
- $ver = get_bloginfo( 'version' );
$expected = "\n";
- $expected .= "\n";
+ $expected .= "\n";
$expected .= "\n";
- $expected .= "\n";
- $expected .= "\n";
+ $expected .= "\n";
+ $expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
* @ticket 14853
*/
public function test_wp_add_inline_script_before_with_concat2() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
$wp_scripts->do_concat = true;
- $wp_scripts->default_dirs = array( '/directory/' );
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
- wp_enqueue_script( 'one', '/directory/one.js' );
- wp_enqueue_script( 'two', '/directory/two.js' );
- wp_enqueue_script( 'three', '/directory/three.js' );
+ wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
+ wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
+ wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
wp_add_inline_script( 'one', 'console.log("before one");', 'before' );
- $ver = get_bloginfo( 'version' );
$expected = "\n";
- $expected .= "\n";
- $expected .= "\n";
- $expected .= "\n";
+ $expected .= "\n";
+ $expected .= "\n";
+ $expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
* @ticket 14853
*/
public function test_wp_add_inline_script_after_with_concat() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
$wp_scripts->do_concat = true;
- $wp_scripts->default_dirs = array( '/directory/' );
+ $wp_scripts->default_dirs = array( $this->default_scripts_dir );
- wp_enqueue_script( 'one', '/directory/one.js' );
- wp_enqueue_script( 'two', '/directory/two.js' );
- wp_enqueue_script( 'three', '/directory/three.js' );
- wp_enqueue_script( 'four', '/directory/four.js' );
+ wp_enqueue_script( 'one', $this->default_scripts_dir . 'one.js' );
+ wp_enqueue_script( 'two', $this->default_scripts_dir . 'two.js' );
+ wp_enqueue_script( 'three', $this->default_scripts_dir . 'three.js' );
+ wp_enqueue_script( 'four', $this->default_scripts_dir . 'four.js' );
wp_add_inline_script( 'two', 'console.log("after two");' );
wp_add_inline_script( 'three', 'console.log("after three");' );
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
- $expected .= "\n";
+ $expected = "\n";
+ $expected .= "\n";
$expected .= "\n";
- $expected .= "\n";
+ $expected .= "\n";
$expected .= "\n";
- $expected .= "\n";
+ $expected .= "\n";
- $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
+ $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
}
/**
@@ -626,7 +1966,7 @@ JS;
wp_script_add_data( 'test-example', 'conditional', 'gte IE 9' );
$this->assertSame( $expected_localized, get_echo( 'wp_print_scripts' ) );
- $this->assertSame( $expected, $wp_scripts->print_html );
+ $this->assertEqualMarkup( $expected, $wp_scripts->print_html );
$this->assertTrue( $wp_scripts->do_concat );
}
@@ -634,15 +1974,14 @@ JS;
* @ticket 36392
*/
public function test_wp_add_inline_script_after_with_concat_and_core_dependency() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
wp_default_scripts( $wp_scripts );
$wp_scripts->base_url = '';
$wp_scripts->do_concat = true;
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
+ $expected = "\n";
$expected .= "\n";
$expected .= "\n";
@@ -652,22 +1991,21 @@ JS;
wp_print_scripts();
$print_scripts = get_echo( '_print_scripts' );
- $this->assertSame( $expected, $print_scripts );
+ $this->assertEqualMarkup( $expected, $print_scripts );
}
/**
* @ticket 36392
*/
public function test_wp_add_inline_script_after_with_concat_and_conditional_and_core_dependency() {
- global $wp_scripts;
+ global $wp_scripts, $wp_version;
wp_default_scripts( $wp_scripts );
$wp_scripts->base_url = '';
$wp_scripts->do_concat = true;
- $ver = get_bloginfo( 'version' );
- $expected = "\n";
+ $expected = "\n";
$expected .= "