diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php
index e08bfa6e46ca8..f3fde930383bc 100644
--- a/lib/compat/wordpress-6.2/script-loader.php
+++ b/lib/compat/wordpress-6.2/script-loader.php
@@ -127,6 +127,30 @@ function gutenberg_resolve_assets_override() {
$scripts = ob_get_clean();
+ /*
+ * Generate web font @font-face styles for the site editor iframe.
+ * Use the registered font families for printing.
+ */
+ if ( class_exists( 'WP_Web_Fonts' ) ) {
+ $wp_webfonts = wp_webfonts();
+ $registered = $wp_webfonts->get_registered_font_families();
+ if ( ! empty( $registered ) ) {
+ $queue = $wp_webfonts->queue;
+ $done = $wp_webfonts->done;
+
+ $wp_webfonts->done = array();
+ $wp_webfonts->queue = $registered;
+
+ ob_start();
+ $wp_webfonts->do_items();
+ $styles .= ob_get_clean();
+
+ // Reset the Web Fonts API.
+ $wp_webfonts->done = $done;
+ $wp_webfonts->queue = $queue;
+ }
+ }
+
return array(
'styles' => $styles,
'scripts' => $scripts,
diff --git a/lib/experimental/class-wp-web-fonts.php b/lib/experimental/class-wp-web-fonts.php
new file mode 100644
index 0000000000000..15df7ecf47e5c
--- /dev/null
+++ b/lib/experimental/class-wp-web-fonts.php
@@ -0,0 +1,744 @@
+ 'local',
+ 'font-family' => '',
+ 'font-style' => 'normal',
+ 'font-weight' => '400',
+ 'font-display' => 'fallback',
+ );
+
+ /**
+ * Constructor.
+ *
+ * @since X.X.X
+ */
+ public function __construct() {
+ /**
+ * Filters the web font variation's property defaults.
+ *
+ * @since X.X.X
+ *
+ * @param array $defaults {
+ * An array of required web font properties and defaults.
+ *
+ * @type string $provider The provider ID. Default 'local'.
+ * @type string $font-family The font-family property. Default empty string.
+ * @type string $font-style The font-style property. Default 'normal'.
+ * @type string $font-weight The font-weight property. Default '400'.
+ * @type string $font-display The font-display property. Default 'fallback'.
+ * }
+ */
+ $this->variation_property_defaults = apply_filters( 'wp_webfont_variation_defaults', $this->variation_property_defaults );
+
+ /**
+ * Fires when the WP_Webfonts instance is initialized.
+ *
+ * @since X.X.X
+ *
+ * @param WP_Web_Fonts $wp_webfonts WP_Web_Fonts instance (passed by reference).
+ */
+ do_action_ref_array( 'wp_default_webfonts', array( &$this ) );
+ }
+
+ /**
+ * Get the list of registered providers.
+ *
+ * @since X.X.X
+ *
+ * @return array $providers {
+ * An associative array of registered providers, keyed by their unique ID.
+ *
+ * @type string $provider_id => array {
+ * An associate array of provider's class name and fonts.
+ *
+ * @type string $class Fully qualified name of the provider's class.
+ * @type string[] $fonts An array of enqueued font handles for this provider.
+ * }
+ * }
+ */
+ public function get_providers() {
+ return $this->providers;
+ }
+
+ /**
+ * Register a provider.
+ *
+ * @since X.X.X
+ *
+ * @param string $provider_id The provider's unique ID.
+ * @param string $class The provider class name.
+ * @return bool True if successfully registered, else false.
+ */
+ public function register_provider( $provider_id, $class ) {
+ if ( empty( $provider_id ) || empty( $class ) || ! class_exists( $class ) ) {
+ return false;
+ }
+
+ $this->providers[ $provider_id ] = array(
+ 'class' => $class,
+ 'fonts' => array(),
+ );
+ return true;
+ }
+
+ /**
+ * Get the list of all registered font family handles.
+ *
+ * @since X.X.X
+ *
+ * @return string[]
+ */
+ public function get_registered_font_families() {
+ $font_families = array();
+ foreach ( $this->registered as $handle => $obj ) {
+ if ( $obj->extra['is_font_family'] ) {
+ $font_families[] = $handle;
+ }
+ }
+ return $font_families;
+ }
+
+ /**
+ * Get the list of all registered font families and their variations.
+ *
+ * @since X.X.X
+ *
+ * @return string[]
+ */
+ public function get_registered() {
+ return array_keys( $this->registered );
+ }
+
+ /**
+ * Get the list of enqueued font families and their variations.
+ *
+ * @since X.X.X
+ *
+ * @return array[]
+ */
+ public function get_enqueued() {
+ return $this->queue;
+ }
+
+ /**
+ * Registers a font family.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family Font family name to register.
+ * @return string|null Font family handle when registration successes. Null on failure.
+ */
+ public function add_font_family( $font_family ) {
+ $font_family_handle = WP_Webfonts_Utils::convert_font_family_into_handle( $font_family );
+ if ( ! $font_family_handle ) {
+ return null;
+ }
+
+ if ( isset( $this->registered[ $font_family_handle ] ) ) {
+ return $font_family_handle;
+ }
+
+ $registered = $this->add( $font_family_handle, false );
+ if ( ! $registered ) {
+ return null;
+ }
+
+ $this->add_data( $font_family_handle, 'font-properties', array( 'font-family' => $font_family ) );
+ $this->add_data( $font_family_handle, 'is_font_family', true );
+
+ return $font_family_handle;
+ }
+
+ /**
+ * Removes a font family and all registered variations.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle The font family to remove.
+ */
+ public function remove_font_family( $font_family_handle ) {
+ if ( ! isset( $this->registered[ $font_family_handle ] ) ) {
+ return;
+ }
+
+ $variations = $this->registered[ $font_family_handle ]->deps;
+
+ foreach ( $variations as $variation ) {
+ $this->remove( $variation );
+ }
+
+ $this->remove( $font_family_handle );
+ }
+
+ /**
+ * Add a variation to an existing family or register family if none exists.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ * @param string $variation_handle Optional. The variation's handle. When none is provided, the
+ * handle will be dynamically generated.
+ * Default empty string.
+ * @return string|null Variation handle on success. Else null.
+ */
+ public function add_variation( $font_family_handle, array $variation, $variation_handle = '' ) {
+ if ( ! WP_Webfonts_Utils::is_defined( $font_family_handle ) ) {
+ trigger_error( 'Font family handle must be a non-empty string.' );
+ return null;
+ }
+
+ // When there is a variation handle, check it.
+ if ( '' !== $variation_handle && ! WP_Webfonts_Utils::is_defined( $variation_handle ) ) {
+ trigger_error( 'Variant handle must be a non-empty string.' );
+ return null;
+ }
+
+ // Register the font family when it does not yet exist.
+ if ( ! isset( $this->registered[ $font_family_handle ] ) ) {
+ if ( ! $this->add_font_family( $font_family_handle ) ) {
+ return null;
+ }
+ }
+
+ $variation = $this->validate_variation( $variation );
+
+ // Variation validation failed.
+ if ( ! $variation ) {
+ return null;
+ }
+
+ // When there's no variation handle, attempt to create one.
+ if ( '' === $variation_handle ) {
+ $variation_handle = WP_Webfonts_Utils::convert_variation_into_handle( $font_family_handle, $variation );
+ if ( is_null( $variation_handle ) ) {
+ return null;
+ }
+ }
+
+ // Bail out if the variant is already registered.
+ if ( $this->is_variation_registered( $font_family_handle, $variation_handle ) ) {
+ return $variation_handle;
+ }
+
+ $variation_src = array_key_exists( 'src', $variation ) ? $variation['src'] : false;
+ $result = $this->add( $variation_handle, $variation_src );
+
+ // Bail out if the registration failed.
+ if ( ! $result ) {
+ return null;
+ }
+
+ $this->add_data( $variation_handle, 'font-properties', $variation );
+ $this->add_data( $variation_handle, 'is_font_family', false );
+
+ // Add the font variation as a dependency to the registered font family.
+ $this->add_dependency( $font_family_handle, $variation_handle );
+
+ $this->providers[ $variation['provider'] ]['fonts'][] = $variation_handle;
+
+ return $variation_handle;
+ }
+
+ /**
+ * Removes a variation.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle The font family for this variation.
+ * @param string $variation_handle The variation's handle to remove.
+ */
+ public function remove_variation( $font_family_handle, $variation_handle ) {
+ if ( isset( $this->registered[ $variation_handle ] ) ) {
+ $this->remove( $variation_handle );
+ }
+
+ if ( ! $this->is_variation_registered( $font_family_handle, $variation_handle ) ) {
+ return;
+ }
+
+ // Remove the variation as a dependency from its font family.
+ $this->registered[ $font_family_handle ]->deps = array_values(
+ array_diff(
+ $this->registered[ $font_family_handle ]->deps,
+ array( $variation_handle )
+ )
+ );
+ }
+
+ /**
+ * Checks if the variation is registered.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param string $variation_handle Variation's handle.
+ * @return bool True when registered to the given font family. Else false.
+ */
+ private function is_variation_registered( $font_family_handle, $variation_handle ) {
+ if ( ! isset( $this->registered[ $font_family_handle ] ) ) {
+ return false;
+ }
+
+ return in_array( $variation_handle, $this->registered[ $font_family_handle ]->deps, true );
+ }
+
+ /**
+ * Adds a variation as a dependency to the given font family.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param string $variation_handle The variation's handle.
+ */
+ private function add_dependency( $font_family_handle, $variation_handle ) {
+ $this->registered[ $font_family_handle ]->deps[] = $variation_handle;
+ }
+
+ /**
+ * Validates and sanitizes a variation.
+ *
+ * @since X.X.X
+ *
+ * @param array $variation Variation properties to add.
+ * @return false|array Validated variation on success. Else, false.
+ */
+ private function validate_variation( $variation ) {
+ $variation = wp_parse_args( $variation, $this->variation_property_defaults );
+
+ // Check the font-family.
+ if ( empty( $variation['font-family'] ) || ! is_string( $variation['font-family'] ) ) {
+ trigger_error( 'Webfont font-family must be a non-empty string.' );
+ return false;
+ }
+
+ // Local fonts need a "src".
+ if ( 'local' === $variation['provider'] ) {
+ // Make sure that local fonts have 'src' defined.
+ if ( empty( $variation['src'] ) || ( ! is_string( $variation['src'] ) && ! is_array( $variation['src'] ) ) ) {
+ trigger_error( 'Webfont src must be a non-empty string or an array of strings.' );
+ return false;
+ }
+ } elseif ( ! isset( $this->providers[ $variation['provider'] ] ) ) {
+ trigger_error( sprintf( 'The provider "%s" is not registered', $variation['provider'] ) );
+ return false;
+ } elseif ( ! class_exists( $this->providers[ $variation['provider'] ]['class'] ) ) {
+ trigger_error( sprintf( 'The provider class "%s" does not exist', $variation['provider'] ) );
+ return false;
+ }
+
+ // Validate the 'src' property.
+ if ( ! empty( $variation['src'] ) ) {
+ foreach ( (array) $variation['src'] as $src ) {
+ if ( empty( $src ) || ! is_string( $src ) ) {
+ trigger_error( 'Each webfont src must be a non-empty string.' );
+ return false;
+ }
+ }
+ }
+
+ // Check the font-weight.
+ if ( ! is_string( $variation['font-weight'] ) && ! is_int( $variation['font-weight'] ) ) {
+ trigger_error( 'Webfont font-weight must be a properly formatted string or integer.' );
+ return false;
+ }
+
+ // Check the font-display.
+ if ( ! in_array( $variation['font-display'], array( 'auto', 'block', 'fallback', 'swap', 'optional' ), true ) ) {
+ $variation['font-display'] = 'fallback';
+ }
+
+ $valid_props = array(
+ 'ascent-override',
+ 'descent-override',
+ 'font-display',
+ 'font-family',
+ 'font-stretch',
+ 'font-style',
+ 'font-weight',
+ 'font-variant',
+ 'font-feature-settings',
+ 'font-variation-settings',
+ 'line-gap-override',
+ 'size-adjust',
+ 'src',
+ 'unicode-range',
+
+ // Exceptions.
+ 'provider',
+ );
+
+ foreach ( $variation as $prop => $value ) {
+ if ( ! in_array( $prop, $valid_props, true ) ) {
+ unset( $variation[ $prop ] );
+ }
+ }
+
+ return $variation;
+ }
+
+ /**
+ * Processes the items and dependencies.
+ *
+ * Processes the items passed to it or the queue, and their dependencies.
+ *
+ * @since X.X.X
+ *
+ * @param string|string[]|bool $handles Optional. Items to be processed: queue (false),
+ * single item (string), or multiple items (array of strings).
+ * Default false.
+ * @param int|false $group Optional. Group level: level (int), no group (false).
+ *
+ * @return array|string[] Array of web font handles that have been processed.
+ * An empty array if none were processed.
+ */
+ public function do_items( $handles = false, $group = false ) {
+ $handles = $this->prepare_handles_for_printing( $handles );
+
+ if ( empty( $handles ) ) {
+ return $this->done;
+ }
+
+ $this->all_deps( $handles );
+ if ( empty( $this->to_do ) ) {
+ return $this->done;
+ }
+
+ $this->to_do_keyed_handles = array_flip( $this->to_do );
+
+ foreach ( $this->get_providers() as $provider_id => $provider ) {
+ // Alert and skip if the provider class does not exist.
+ if ( ! class_exists( $provider['class'] ) ) {
+ /* translators: %s is the provider name. */
+ trigger_error(
+ sprintf(
+ 'Class "%s" not found for "%s" web font provider',
+ $provider['class'],
+ $provider_id
+ )
+ );
+ continue;
+ }
+
+ $this->do_item( $provider_id, $group );
+ }
+
+ $this->process_font_families_after_printing( $handles );
+
+ return $this->done;
+ }
+
+ /**
+ * Prepares the given handles for printing.
+ *
+ * @since X.X.X
+ *
+ * @param string|string[]|bool $handles Optional. Handles to prepare.
+ * Default false.
+ * @return array Array of handles.
+ */
+ private function prepare_handles_for_printing( $handles = false ) {
+ if ( false !== $handles ) {
+ $handles = $this->validate_handles( $handles );
+ // Bail out when invalid.
+ if ( empty( $handles ) ) {
+ return array();
+ }
+ }
+
+ // Use the enqueued queue.
+ if ( empty( $handles ) ) {
+ if ( empty( $this->queue ) ) {
+ return array();
+ }
+ $handles = $this->queue;
+ }
+
+ return $handles;
+ }
+
+ /**
+ * Validates handle(s) to ensure each is a non-empty string.
+ *
+ * @since X.X.X
+ *
+ * @param string|string[] $handles Handles to prepare.
+ * @return string[]|null Array of handles on success. Else null.
+ */
+ private function validate_handles( $handles ) {
+ // Validate each element is a non-empty string handle.
+ $handles = array_filter( (array) $handles, array( WP_Webfonts_Utils::class, 'is_defined' ) );
+
+ if ( empty( $handles ) ) {
+ trigger_error( 'Handles must be a non-empty string or array of non-empty strings' );
+ return null;
+ }
+
+ return $handles;
+ }
+
+ /**
+ * Invokes each provider to process and print its styles.
+ *
+ * @since X.X.X
+ *
+ * @see WP_Dependencies::do_item()
+ *
+ * @param string $provider_id The provider to process.
+ * @param int|false $group Not used.
+ * @return bool
+ */
+ public function do_item( $provider_id, $group = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Bail out if the provider is not registered.
+ if ( ! isset( $this->providers[ $provider_id ] ) ) {
+ return false;
+ }
+
+ $font_handles = $this->get_enqueued_fonts_for_provider( $provider_id );
+ if ( empty( $font_handles ) ) {
+ return false;
+ }
+
+ $properties_by_font = $this->get_font_properties_for_provider( $font_handles );
+ if ( empty( $properties_by_font ) ) {
+ return false;
+ }
+
+ // Invoke provider to print its styles.
+ $provider = $this->get_provider_instance( $provider_id );
+ $provider->set_webfonts( $properties_by_font );
+ $provider->print_styles();
+
+ // Clean up.
+ $this->update_queues_for_printed_fonts( $font_handles );
+
+ return true;
+ }
+
+ /**
+ * Retrieves a list of enqueued web font variations for a provider.
+ *
+ * @since X.X.X
+ *
+ * @param string $provider_id The provider to process.
+ * @return array[] Webfonts organized by providers.
+ */
+ private function get_enqueued_fonts_for_provider( $provider_id ) {
+ $providers = $this->get_providers();
+
+ if ( empty( $providers[ $provider_id ] ) ) {
+ return array();
+ }
+
+ return array_intersect(
+ $providers[ $provider_id ]['fonts'],
+ $this->to_do
+ );
+ }
+
+ /**
+ * Gets a list of font properties for each of the given font handles.
+ *
+ * @since X.X.X
+ *
+ * @param array $font_handles Font handles to get properties.
+ * @return array A list of fonts with each font's properties.
+ */
+ private function get_font_properties_for_provider( array $font_handles ) {
+ $font_properties = array();
+
+ foreach ( $font_handles as $font_handle ) {
+ $properties = $this->get_data( $font_handle, 'font-properties' );
+ if ( ! $properties ) {
+ continue;
+ }
+ $font_properties[ $font_handle ] = $properties;
+ }
+
+ return $font_properties;
+ }
+
+ /**
+ * Gets the instance of the provider from the WP_Webfonts::$provider_instance store.
+ *
+ * @since X.X.X
+ *
+ * @param string $provider_id The provider to get.
+ * @return object Instance of the provider.
+ */
+ private function get_provider_instance( $provider_id ) {
+ if ( ! isset( $this->provider_instances[ $provider_id ] ) ) {
+ $this->provider_instances[ $provider_id ] = new $this->providers[ $provider_id ]['class']();
+ }
+ return $this->provider_instances[ $provider_id ];
+ }
+
+ /**
+ * Update queues for the given printed fonts.
+ *
+ * @since X.X.X
+ *
+ * @param array $font_handles Font handles to get properties.
+ */
+ private function update_queues_for_printed_fonts( array $font_handles ) {
+ foreach ( $font_handles as $font_handle ) {
+ $this->set_as_done( $font_handle );
+ $this->remove_from_to_do_queues( $font_handle );
+ }
+ }
+
+ /**
+ * Processes the font families after printing the variations.
+ *
+ * For each queued font family:
+ *
+ * a. if any of their variations were printed, the font family is added to the `done` list.
+ * b. removes each from the to_do queues.
+ *
+ * @since X.X.X
+ *
+ * @param array $handles Handles to process.
+ */
+ private function process_font_families_after_printing( array $handles ) {
+ foreach ( $handles as $handle ) {
+ if (
+ ! $this->get_data( $handle, 'is_font_family' ) ||
+ ! isset( $this->to_do_keyed_handles[ $handle ] )
+ ) {
+ continue;
+ }
+ $font_family = $this->registered[ $handle ];
+
+ // Add the font family to `done` list if any of its variations were printed.
+ if ( ! empty( $font_family->deps ) ) {
+ $processed = array_intersect( $font_family->deps, $this->done );
+ if ( ! empty( $processed ) ) {
+ $this->set_as_done( $handle );
+ }
+ }
+
+ $this->remove_from_to_do_queues( $handle );
+ }
+ }
+
+ /**
+ * Removes the handle from the `to_do` and `to_do_keyed_handles` lists.
+ *
+ * @since X.X.X
+ *
+ * @param string $handle Handle to remove.
+ */
+ private function remove_from_to_do_queues( $handle ) {
+ unset(
+ $this->to_do[ $this->to_do_keyed_handles[ $handle ] ],
+ $this->to_do_keyed_handles[ $handle ]
+ );
+ }
+
+ /**
+ * Sets the given handle to done by adding it to the `done` list.
+ *
+ * @since X.X.X
+ *
+ * @param string $handle Handle to set as done.
+ */
+ private function set_as_done( $handle ) {
+ if ( ! is_array( $this->done ) ) {
+ $this->done = array();
+ }
+ $this->done[] = $handle;
+ }
+
+ /**
+ * Converts the font family and its variations into theme.json structural format.
+ *
+ * @since X.X.X
+ *
+ * @param string $font_family_handle Font family to convert.
+ * @return array Webfonts in theme.json structural format.
+ */
+ public function to_theme_json( $font_family_handle ) {
+ if ( ! isset( $this->registered[ $font_family_handle ] ) ) {
+ return array();
+ }
+
+ $font_family_name = $this->registered[ $font_family_handle ]->extra['font-properties']['font-family'];
+ $theme_json_format = array(
+ 'fontFamily' => str_contains( $font_family_name, ' ' ) ? "'{$font_family_name}'" : $font_family_name,
+ 'name' => $font_family_name,
+ 'slug' => $font_family_handle,
+ 'fontFace' => array(),
+ );
+
+ foreach ( $this->registered[ $font_family_handle ]->deps as $variation_handle ) {
+ if ( ! isset( $this->registered[ $variation_handle ] ) ) {
+ continue;
+ }
+
+ $variation_obj = $this->registered[ $variation_handle ];
+ $variation_properties = array( 'origin' => 'gutenberg_wp_webfonts_api' );
+ foreach ( $variation_obj->extra['font-properties'] as $property_name => $property_value ) {
+ $property_in_camelcase = lcfirst( str_replace( '-', '', ucwords( $property_name, '-' ) ) );
+ $variation_properties[ $property_in_camelcase ] = $property_value;
+ }
+ $theme_json_format['fontFace'][ $variation_obj->handle ] = $variation_properties;
+ }
+
+ return $theme_json_format;
+ }
+}
diff --git a/lib/experimental/class-wp-webfonts-provider-local.php b/lib/experimental/class-wp-webfonts-provider-local.php
index 9af27fa7c436a..3950ebcd624c3 100644
--- a/lib/experimental/class-wp-webfonts-provider-local.php
+++ b/lib/experimental/class-wp-webfonts-provider-local.php
@@ -35,6 +35,21 @@ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider {
*/
protected $id = 'local';
+ /**
+ * Constructor.
+ *
+ * @since 6.1.0
+ */
+ public function __construct() {
+ if (
+ function_exists( 'is_admin' ) && ! is_admin()
+ &&
+ function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' )
+ ) {
+ $this->style_tag_atts = array( 'type' => 'text/css' );
+ }
+ }
+
/**
* Gets the `@font-face` CSS styles for locally-hosted font files.
*
@@ -81,7 +96,7 @@ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider {
* }
*
*
- * @since 6.0.0
+ * @since X.X.X
*
* @return string The `@font-face` CSS.
*/
@@ -183,7 +198,8 @@ private function order_src( array $webfont ) {
private function build_font_face_css( array $webfont ) {
$css = '';
- // Wrap font-family in quotes if it contains spaces.
+ // Wrap font-family in quotes if it contains spaces
+ // and is not already wrapped in quotes.
if (
str_contains( $webfont['font-family'], ' ' ) &&
! str_contains( $webfont['font-family'], '"' ) &&
diff --git a/lib/experimental/class-wp-webfonts-provider.php b/lib/experimental/class-wp-webfonts-provider.php
index a49b8a324b7f5..8fcbd76a76d3c 100644
--- a/lib/experimental/class-wp-webfonts-provider.php
+++ b/lib/experimental/class-wp-webfonts-provider.php
@@ -6,7 +6,7 @@
*
* @package WordPress
* @subpackage WebFonts
- * @since 6.0.0
+ * @since X.X.X
*/
if ( class_exists( 'WP_Webfonts_Provider' ) ) {
@@ -30,26 +30,46 @@
* into styles (in a performant way for the provider service
* it manages).
*
- * @since 6.0.0
+ * @since X.X.X
*/
abstract class WP_Webfonts_Provider {
+ /**
+ * The provider's unique ID.
+ *
+ * @since X.X.X
+ *
+ * @var string
+ */
+ protected $id = '';
+
/**
* Webfonts to be processed.
*
- * @since 6.0.0
+ * @since X.X.X
*
* @var array[]
*/
protected $webfonts = array();
+ /**
+ * Array of Font-face style tag's attribute(s)
+ * where the key is the attribute name and the
+ * value is its value.
+ *
+ * @since X.X.X
+ *
+ * @var string[]
+ */
+ protected $style_tag_atts = array();
+
/**
* Sets this provider's webfonts property.
*
* The API's Controller passes this provider's webfonts
* for processing here in the provider.
*
- * @since 6.0.0
+ * @since X.X.X
*
* @param array[] $webfonts Registered webfonts.
*/
@@ -57,6 +77,18 @@ public function set_webfonts( array $webfonts ) {
$this->webfonts = $webfonts;
}
+ /**
+ * Prints the generated styles.
+ *
+ * @since X.X.X
+ */
+ public function print_styles() {
+ printf(
+ $this->get_style_element(),
+ $this->get_css()
+ );
+ }
+
/**
* Gets the `@font-face` CSS for the provider's webfonts.
*
@@ -64,9 +96,37 @@ public function set_webfonts( array $webfonts ) {
* needed `@font-face` CSS for all of its webfonts. Specifics of how
* this processing is done is contained in each provider.
*
- * @since 6.0.0
+ * @since X.X.X
*
* @return string The `@font-face` CSS.
*/
abstract public function get_css();
+
+ /**
+ * Gets the `\n";
+ }
+
+ /**
+ * Gets the defined \n",
+ $font_faces['merriweather-200-900-normal']
+ ),
+ ),
+ 'print Source Serif Pro for local provider' => array(
+ 'setup' => array(
+ 'provider' => array( 'local' => $providers['local'] ),
+ 'provider_handles' => array( 'local' => $local_font_handles ),
+ 'registered' => $local_fonts,
+ 'enqueued' => array( 'source-serif-pro' ),
+ ),
+ 'expected_done' => array( 'source-serif-pro', 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ),
+ ),
+ 'print all fonts for local provider' => array(
+ 'setup' => array(
+ 'provider' => array( 'local' => $providers['local'] ),
+ 'provider_handles' => array( 'local' => $local_font_handles ),
+ 'registered' => $local_fonts,
+ 'enqueued' => array( 'merriweather', 'source-serif-pro' ),
+ ),
+ 'expected_done' => array(
+ 'merriweather',
+ 'merriweather-200-900-normal',
+ 'source-serif-pro',
+ 'Source Serif Pro-300-normal',
+ 'Source Serif Pro-900-italic',
+ ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ),
+ ),
+
+ // All providers registered with multiple fonts.
+
+ 'print font1 when all providers registered' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font1' ) ) ),
+ 'expected_done' => array( 'font1', 'font1-300-normal', 'font1-300-italic', 'font1-900-normal' ),
+ 'expected_output' => sprintf(
+ '%s; %s; %s\n',
+ $font_faces['font1-300-normal'],
+ $font_faces['font1-300-italic'],
+ $font_faces['font1-900-normal']
+ ),
+ ),
+ 'print all mock fonts when all providers registered' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font1', 'font2', 'font3' ) ) ),
+ 'expected_done' => array(
+ 'font1',
+ 'font1-300-normal',
+ 'font1-300-italic',
+ 'font1-900-normal',
+ 'font2',
+ 'font2-200-900-normal',
+ 'font2-200-900-italic',
+ 'font3',
+ 'font3-bold-normal',
+ ),
+ 'expected_output' => sprintf(
+ '%s; %s; %s; %s; %s; %s\n',
+ $font_faces['font1-300-normal'],
+ $font_faces['font1-300-italic'],
+ $font_faces['font1-900-normal'],
+ $font_faces['font2-200-900-normal'],
+ $font_faces['font2-200-900-italic'],
+ $font_faces['font3-bold-normal']
+ ),
+ ),
+ 'print merriweather when all providers registered' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather' ) ) ),
+ 'expected_done' => array( 'merriweather', 'merriweather-200-900-normal' ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal']
+ ),
+ ),
+ 'print all local fonts when all providers registered' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather', 'source-serif-pro' ) ) ),
+ 'expected_done' => array(
+ 'merriweather',
+ 'merriweather-200-900-normal',
+ 'source-serif-pro',
+ 'Source Serif Pro-300-normal',
+ 'Source Serif Pro-900-italic',
+ ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ),
+ ),
+
+ 'print all fonts for all providers' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => $all_variation_handles ) ),
+ 'expected_done' => $all_variation_handles,
+ 'expected_output' =>
+ sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ) .
+ sprintf(
+ '%s; %s; %s; %s; %s; %s\n',
+ $font_faces['font1-300-normal'],
+ $font_faces['font1-300-italic'],
+ $font_faces['font1-900-normal'],
+ $font_faces['font2-200-900-normal'],
+ $font_faces['font2-200-900-italic'],
+ $font_faces['font3-bold-normal']
+ ),
+ ),
+
+ // Specific variations enqueued.
+ // Validates that only these specific variations print once.
+
+ 'specific variation: 1 local' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal' ) ) ),
+ 'expected_done' => array( 'merriweather-200-900-normal' ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal']
+ ),
+ ),
+ 'specific variation: 1 local from different font families' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ) ) ),
+ 'expected_done' => array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ),
+ 'expected_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ),
+ ),
+ 'specific variation: 1 local and 1 mock' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal', 'font2-200-900-normal' ) ) ),
+ 'expected_done' => array( 'merriweather-200-900-normal', 'font2-200-900-normal' ),
+ 'expected_output' => sprintf(
+ "\n" .
+ '%s\n',
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['font2-200-900-normal']
+ ),
+ ),
+ 'specific variation: 1 mock and 1 local' => array(
+ 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font2-200-900-normal', 'Source Serif Pro-300-normal' ) ) ),
+ 'expected_done' => array( 'font2-200-900-normal', 'Source Serif Pro-300-normal' ),
+ 'expected_output' => sprintf(
+ "\n" .
+ '%s\n',
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['font2-200-900-normal']
+ ),
+ ),
+ );
+ }
+
+ protected function get_data_registry() {
+ return array(
+ 'lato' => array(),
+ 'merriweather' => array(
+ 'merriweather-200-900-normal' => array(
+ 'font-family' => 'Merriweather',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ ),
+ 'Source Serif Pro' => array(
+ 'Source Serif Pro-300-normal' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'Source Serif Pro-900-italic' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'my-font' => array(
+ 'my-font-300-normal' => array(
+ 'font-family' => 'My Font',
+ 'font-weight' => '300',
+ 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2',
+ ),
+ 'my-font-300-italic' => array(
+ 'font-family' => 'My Font',
+ 'font-weight' => '300',
+ 'font-style' => 'italic',
+ 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2',
+ ),
+ 'my-font-900-normal' => array(
+ 'font-family' => 'My Font',
+ 'font-weight' => '900',
+ 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2',
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Gets the provider definitions.
+ *
+ * @since X.X.X
+ *
+ * @param string $provider_id Optional. Provider ID to get. Default empty string.
+ * @return array
+ */
+ protected function get_provider_definitions( $provider_id = '' ) {
+ $providers = array(
+ 'mock' => array(
+ 'id' => 'mock',
+ 'class' => Mock_Provider::class,
+ ),
+ 'local' => array(
+ 'id' => 'local',
+ 'class' => WP_Webfonts_Provider_Local::class,
+ ),
+ );
+
+ if ( '' === $provider_id ) {
+ return $providers;
+ }
+
+ if ( isset( $providers[ $provider_id ] ) ) {
+ return $providers[ $provider_id ];
+ }
+
+ return array(
+ 'id' => $provider_id,
+ 'class' => '',
+ );
+ }
+
+ /**
+ * Gets web font definitions for both local and mock providers.
+ *
+ * @since X.X.X
+ *
+ * @return array|string[][][]
+ */
+ protected function get_registered_fonts() {
+ return array_merge(
+ $this->get_registered_local_fonts(),
+ $this->get_registered_mock_fonts()
+ );
+ }
+
+ /**
+ * Returns an array of font-face styles that matches the font definitions
+ * in get_registered_local_fonts() and get_registered_mock_fonts().
+ *
+ * @since X.X.X
+ *
+ * @return string[]
+ */
+ protected function get_registered_fonts_css() {
+ return array(
+ 'merriweather-200-900-normal' => << << << 'font1-300-normal',
+ 'font1-300-italic' => 'font1-300-italic',
+ 'font1-900-normal' => 'font1-900-normal',
+ 'font2-200-900-normal' => 'font2-200-900-normal',
+ 'font2-200-900-italic' => 'font2-200-900-italic',
+ 'font3-bold-normal' => 'font3-bold-normal',
+ );
+ }
+
+ /**
+ * Gets web font definitions for local provider.
+ *
+ * @since X.X.X
+ *
+ * @return string[][][]
+ */
+ protected function get_registered_local_fonts() {
+ return array(
+ 'lato' => array(),
+ 'merriweather' => array(
+ 'merriweather-200-900-normal' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Merriweather',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ ),
+ 'Source Serif Pro' => array(
+ 'Source Serif Pro-300-normal' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'Source Serif Pro-900-italic' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Gets web font definitions for mock provider.
+ *
+ * @since X.X.X
+ *
+ * @return string[][][]
+ */
+ protected function get_registered_mock_fonts() {
+ return array(
+ 'font1' => array(
+ 'font1-300-normal' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 1',
+ 'font-weight' => '300',
+ 'font-style' => 'normal',
+ 'font-display' => 'fallback',
+ ),
+ 'font1-300-italic' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 1',
+ 'font-weight' => '300',
+ 'font-style' => 'italic',
+ 'font-display' => 'fallback',
+ ),
+ 'font1-900-normal' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 1',
+ 'font-weight' => '900',
+ 'font-style' => 'normal',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'font2' => array(
+ 'font2-200-900-normal' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 2',
+ 'font-weight' => '200 900',
+ 'font-style' => 'normal',
+ 'font-display' => 'fallback',
+ ),
+ 'font2-200-900-italic' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 2',
+ 'font-weight' => '200 900',
+ 'font-style' => 'italic',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'font3' => array(
+ 'font3-bold-normal' => array(
+ 'provider' => 'mock',
+ 'font-family' => 'Font 3',
+ 'font-weight' => 'bold',
+ 'font-style' => 'normal',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpDeregisterFontFamily-test.php b/phpunit/webfonts/wpDeregisterFontFamily-test.php
new file mode 100644
index 0000000000000..2487053b6149c
--- /dev/null
+++ b/phpunit/webfonts/wpDeregisterFontFamily-test.php
@@ -0,0 +1,74 @@
+set_up_mock( 'remove_font_family' );
+ $mock->expects( $this->once() )
+ ->method( 'remove_font_family' )
+ ->with(
+ $this->identicalTo( $font_family_handle )
+ );
+
+ wp_deregister_font_family( $font_family_handle );
+ }
+
+ /**
+ * Integration test for enqueuing before registering a font family and all of its variations.
+ *
+ * @dataProvider data_font_family_handles
+ *
+ * @param string $font_family Font family to test.
+ */
+ public function test_should_deregister_before_registration( $font_family ) {
+ wp_deregister_font_family( $font_family );
+
+ $this->assertIsArray( $this->get_registered(), 'Registration queue should be an array' );
+ $this->assertEmpty( $this->get_registered(), 'Registration queue should be empty after deregistering' );
+ }
+
+ /**
+ * Integration test for deregistering a font family and all of its variations.
+ *
+ * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations
+ *
+ * @param string $font_family Font family to test.
+ * @param array $inputs Font family(ies) and variations to pre-register.
+ * @param array $registered_handles Expected handles after registering.
+ * @param array $expected Array of expected handles.
+ */
+ public function test_deregister_after_registration( $font_family, array $inputs, array $registered_handles, array $expected ) {
+ foreach ( $inputs as $handle => $variations ) {
+ $this->setup_register( $handle, $variations );
+ }
+ // Test the before state, just to make sure.
+ $this->assertSame( $registered_handles, $this->get_registered_handles(), 'Font family and variations should be registered before deregistering' );
+
+ wp_deregister_font_family( $font_family );
+
+ // Test after deregistering.
+ $this->assertIsArray( $this->get_registered_handles(), 'Registration queue should be an array' );
+ $this->assertSame( $expected, $this->get_registered_handles(), 'Registration queue should match after deregistering' );
+ }
+}
diff --git a/phpunit/webfonts/wpDeregisterWebfontVariation-test.php b/phpunit/webfonts/wpDeregisterWebfontVariation-test.php
new file mode 100644
index 0000000000000..a6a0ab67ad448
--- /dev/null
+++ b/phpunit/webfonts/wpDeregisterWebfontVariation-test.php
@@ -0,0 +1,298 @@
+wp_webfonts = wp_webfonts();
+ $this->fonts_to_register = $this->get_registered_local_fonts();
+ }
+
+ /**
+ * Sets up the unit test by mocking the WP_Dependencies object using stdClass and
+ * registering each font family directly to the WP_Webfonts::$registered property
+ * and its variations to the mocked $deps property.
+ */
+ private function setup_unit_test() {
+ $this->setup_registration_mocks( $this->fonts_to_register, $this->wp_webfonts );
+ }
+
+ /**
+ * Sets up the integration test by properly registering each font family and its variations
+ * by using the WP_Webfonts::add() and WP_Webfonts::add_variation() methods.
+ */
+ private function setup_integration_test() {
+ foreach ( $this->fonts_to_register as $font_family_handle => $variations ) {
+ $this->setup_register( $font_family_handle, $variations, $this->wp_webfonts );
+ }
+ }
+
+ /**
+ * Testing the test setup to ensure it works.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_mocked_setup( $font_family_handle, $variation_handle ) {
+ $this->setup_unit_test();
+
+ $this->assertArrayHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be in the registered queue before removal' );
+ $this->assertContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should be in its font family deps before removal' );
+ }
+
+ /**
+ * Unit test for deregistering a font-family's variation using mock of WP_Webfonts.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family to test.
+ * @param string $variation_handle Variation's handle to test.
+ */
+ public function test_should_deregister_when_mocked( $font_family_handle, $variation_handle ) {
+ $mock = $this->set_up_mock( 'remove_variation' );
+ $mock->expects( $this->once() )
+ ->method( 'remove_variation' )
+ ->with(
+ $this->identicalTo( $font_family_handle, $variation_handle )
+ );
+
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_should_do_nothing
+ *
+ * @param string $font_family Font family name.
+ * @param string $font_family_handle Font family handle.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_unit_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) {
+ // Set up the test.
+ unset( $this->fonts_to_register[ $font_family ] );
+ $this->setup_unit_test();
+ $registered_queue = $this->wp_webfonts->registered;
+
+ // Run the tests.
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $font_family_handle, $this->wp_webfonts->registered, 'Font family should not be registered' );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertSame( $registered_queue, $this->wp_webfonts->registered, 'Registered queue should not have changed' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_should_do_nothing
+ *
+ * @param string $font_family Font family name.
+ * @param string $font_family_handle Font family handle.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) {
+ // Set up the test.
+ unset( $this->fonts_to_register[ $font_family ] );
+ $this->setup_integration_test();
+ $registered_queue = $this->wp_webfonts->get_registered();
+
+ // Run the tests.
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $font_family_handle, $this->wp_webfonts->registered, 'Font family should not be registered' );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertSameSets( $registered_queue, $this->wp_webfonts->get_registered(), 'Registered queue should not have changed' );
+ }
+
+ /**
+ * Data provider for testing removal of variations.
+ *
+ * @return array
+ */
+ public function data_should_do_nothing() {
+ return array(
+ 'Font with 1 variation' => array(
+ 'font_family' => 'merriweather',
+ 'font_family_handle' => 'merriweather',
+ 'variation_handle' => 'merriweather-200-900-normal',
+ ),
+ 'Font with multiple variations' => array(
+ 'font_family' => 'Source Serif Pro',
+ 'font_family_handle' => 'source-serif-pro',
+ 'variation_handle' => 'Source Serif Pro-300-normal',
+ ),
+ );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+ $this->setup_remove_variation_from_registered( $variation_handle );
+
+ // Run the tests.
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+ $this->setup_remove_variation_from_registered( $variation_handle );
+
+ // Run the tests.
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+ $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' );
+
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+ $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' );
+
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+
+ $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' );
+
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be not be in registered queue' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+
+ $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' );
+
+ wp_deregister_webfont_variation( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be not be in registered queue' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Remove the variation handle from the font family's deps.
+ *
+ * @param string $font_family_handle Font family.
+ * @param string $variation_handle The variation handle to remove.
+ */
+ private function setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ) {
+ foreach ( $this->wp_webfonts->registered[ $font_family_handle ]->deps as $index => $vhandle ) {
+ if ( $variation_handle !== $vhandle ) {
+ continue;
+ }
+ unset( $this->wp_webfonts->registered[ $font_family_handle ]->deps[ $index ] );
+ break;
+ }
+ }
+
+ /**
+ * Removes the variation from the WP_Webfonts::$registered queue.
+ *
+ * @param string $variation_handle The variation handle to remove.
+ */
+ private function setup_remove_variation_from_registered( $variation_handle ) {
+ unset( $this->wp_webfonts->registered[ $variation_handle ] );
+ }
+}
diff --git a/phpunit/webfonts/wpEnqueueWebfontVariations-test.php b/phpunit/webfonts/wpEnqueueWebfontVariations-test.php
new file mode 100644
index 0000000000000..f6ce62d3f47ee
--- /dev/null
+++ b/phpunit/webfonts/wpEnqueueWebfontVariations-test.php
@@ -0,0 +1,82 @@
+set_up_mock( 'enqueue' );
+ $mock->expects( $this->once() )
+ ->method( 'enqueue' )
+ ->with(
+ $this->identicalTo( $handles )
+ );
+
+ wp_enqueue_webfont_variations( $handles );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_variation_handles() {
+ return array(
+ '1 variation handle' => array( 'merriweather-200-900-normal' ),
+ 'multiple same font family handles' => array( array( 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ) ),
+ 'handles from different font families' => array( array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ) ),
+ );
+ }
+
+ /**
+ * Integration test for enqueuing one or more specific variations.
+ *
+ * @dataProvider data_enqueue_variations
+ *
+ * @param string|string[] $handles Variation handles to test.
+ * @param array $expected Expected queue.
+ */
+ public function test_should_enqueue_after_registration( $handles, array $expected ) {
+ foreach ( $this->get_data_registry() as $handle => $variations ) {
+ $this->setup_register( $handle, $variations );
+ }
+
+ wp_enqueue_webfont_variations( $handles );
+ $this->assertEmpty( $this->get_queued_before_register(), '"queued_before_register" queue should be empty' );
+ $this->assertSame( $expected, $this->get_enqueued_handles(), 'Queue should contain the given handles' );
+ }
+
+ /**
+ * Integration test for enqueuing before registering one or more specific variations.
+ *
+ * @dataProvider data_enqueue_variations
+ *
+ * @param string|string[] $handles Variation handles to test.
+ * @param array $not_used Not used.
+ * @param array $expected Expected "queued_before_register" queue.
+ */
+ public function test_should_enqueue_before_registration( $handles, array $not_used, array $expected ) {
+ wp_enqueue_webfont_variations( $handles );
+
+ $this->assertSame( $expected, $this->get_queued_before_register(), '"queued_before_register" queue should contain the given handles' );
+ $this->assertEmpty( $this->get_enqueued_handles(), 'Queue should be empty' );
+ }
+}
diff --git a/phpunit/webfonts/wpEnqueueWebfonts-test.php b/phpunit/webfonts/wpEnqueueWebfonts-test.php
new file mode 100644
index 0000000000000..a0ee6af5c1dda
--- /dev/null
+++ b/phpunit/webfonts/wpEnqueueWebfonts-test.php
@@ -0,0 +1,122 @@
+set_up_mock( 'enqueue' );
+ $mock->expects( $this->once() )
+ ->method( 'enqueue' )
+ ->with(
+ $this->identicalTo( $expected_handles )
+ );
+
+ wp_enqueue_webfonts( $font_families );
+ }
+
+ /**
+ * Integration test for enqueuing a font family and all of its variations.
+ *
+ * @dataProvider data_should_enqueue
+ *
+ * @param string[] $font_families Font families to test.
+ * @param string[] $expected_handles Expected handles passed to WP_Web_Fonts::enqueue().
+ */
+ public function test_should_enqueue_after_registration( $font_families, $expected_handles ) {
+ // Register the font-families.
+ foreach ( $this->get_data_registry() as $handle => $variations ) {
+ $this->setup_register( $handle, $variations );
+ }
+
+ wp_enqueue_webfonts( $font_families );
+
+ $this->assertEmpty( $this->get_queued_before_register(), '"queued_before_register" queue should be empty' );
+ $this->assertSame( $expected_handles, $this->get_enqueued_handles(), 'Queue should contain the given font family(ies)' );
+ }
+
+ /**
+ * Integration test for enqueuing before registering a font family and all of its variations.
+ *
+ * @dataProvider data_should_enqueue
+ *
+ * @param string[] $font_families Font families to test.
+ * @param string[] $expected_handles Expected handles passed to WP_Web_Fonts::enqueue().
+ */
+ public function test_should_enqueue_before_registration( $font_families, $expected_handles ) {
+ wp_enqueue_webfonts( $font_families );
+
+ // Set up what "queued_before_register" queue should be.
+ $expected = array();
+ foreach ( $expected_handles as $handle ) {
+ $expected[ $handle ] = null;
+ }
+ $this->assertSame( $expected, $this->get_queued_before_register(), '"queued_before_register" queue should contain the given font family(ies)' );
+ $this->assertEmpty( $this->get_enqueued_handles(), 'Queue should be empty' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_should_enqueue() {
+ return array(
+ '1: single word handle' => array(
+ 'font_families' => array( 'lato' ),
+ 'expected_handles' => array( 'lato' ),
+ ),
+ '1: multiple word handle' => array(
+ 'font_families' => array( 'source-serif-pro' ),
+ 'expected_handles' => array( 'source-serif-pro' ),
+ ),
+ '1: single word name' => array(
+ 'font_families' => array( 'Merriweather' ),
+ 'expected_handles' => array( 'merriweather' ),
+ ),
+ '1: multiple word name' => array(
+ 'font_families' => array( 'My Font' ),
+ 'expected_handles' => array( 'my-font' ),
+ ),
+ '>1: single word handle' => array(
+ 'font_families' => array( 'lato', 'merriweather' ),
+ 'expected_handles' => array( 'lato', 'merriweather' ),
+ ),
+ '>1: multiple word handle' => array(
+ 'font_families' => array( 'source-serif-pro', 'my-font' ),
+ 'expected_handles' => array( 'source-serif-pro', 'my-font' ),
+ ),
+ '>1: single word name' => array(
+ 'font_families' => array( 'Lato', 'Merriweather' ),
+ 'expected_handles' => array( 'lato', 'merriweather' ),
+ ),
+ '>1: multiple word name' => array(
+ 'font_families' => array( 'My Font', 'Source Serif Pro' ),
+ 'expected_handles' => array( 'my-font', 'source-serif-pro' ),
+ ),
+ '>1: mixture of word handles and names' => array(
+ 'font_families' => array( 'Source Serif Pro', 'Merriweather', 'my-font', 'Lato' ),
+ 'expected_handles' => array( 'source-serif-pro', 'merriweather', 'my-font', 'lato' ),
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpPrintWebfonts-test.php b/phpunit/webfonts/wpPrintWebfonts-test.php
new file mode 100644
index 0000000000000..3f782fec580fd
--- /dev/null
+++ b/phpunit/webfonts/wpPrintWebfonts-test.php
@@ -0,0 +1,127 @@
+assertSame( array(), wp_print_webfonts() );
+ $this->assertNotInstanceOf( WP_Webfonts::class, $wp_webfonts );
+ }
+
+ /**
+ * Unit test to mock WP_Webfonts::do_items().
+ *
+ * @dataProvider data_mocked_handles
+ *
+ * @param string|string[]|false $handles Handles to test.
+ * @param array|string[] $expected Expected array of processed handles.
+ */
+ public function test_should_return_mocked_handles( $handles, $expected ) {
+ $mock = $this->set_up_mock( 'do_items' );
+ $mock->expects( $this->once() )
+ ->method( 'do_items' )
+ ->with(
+ $this->identicalTo( $handles )
+ )
+ ->will( $this->returnValue( $expected ) );
+
+ wp_print_webfonts( $handles );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_mocked_handles() {
+ return array(
+ 'no handles' => array(
+ 'handles' => false,
+ 'expected' => array(),
+ ),
+ 'font family handles' => array(
+ 'handles' => array( 'my-custom-font' ),
+ 'expected' => array( 'my-custom-font' ),
+ ),
+ );
+ }
+
+ /**
+ * Integration test that registers providers and fonts and then enqueues before
+ * testing the printing functionality.
+ *
+ * @dataProvider data_print_enqueued
+ *
+ * @param array $setup Test set up information for provider, fonts, and enqueued.
+ * @param array $expected_done Expected array of printed handles.
+ * @param string $expected_output Expected printed output.
+ */
+ public function test_should_print_enqueued( $setup, $expected_done, $expected_output ) {
+ $wp_webfonts = wp_webfonts();
+
+ $this->setup_integrated_deps( $setup, $wp_webfonts );
+
+ $this->expectOutputString( $expected_output );
+ $actual_done = wp_print_webfonts();
+ $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' );
+ }
+
+ /**
+ * Integration test to validate printing given handles. Rather than mocking internal functionality,
+ * it registers providers and fonts but does not enqueue.
+ *
+ * @dataProvider data_print_enqueued
+ *
+ * @param array $setup Test set up information for provider, fonts, and enqueued.
+ * @param array $expected_done Expected array of printed handles.
+ * @param string $expected_output Expected printed output.
+ */
+ public function test_should_print_handles_when_not_enqueued( $setup, $expected_done, $expected_output ) {
+ $wp_webfonts = wp_webfonts();
+
+ $this->setup_integrated_deps( $setup, $wp_webfonts, false );
+ // Do not enqueue. Instead, pass the handles to wp_print_webfonts().
+ $handles = $setup['enqueued'];
+ $this->assertEmpty( $wp_webfonts->queue, 'No fonts should be enqueued' );
+
+ $this->expectOutputString( $expected_output );
+ $actual_done = wp_print_webfonts( $handles );
+ $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' );
+ }
+
+ /**
+ * Sets up the dependencies for integration test.
+ *
+ * @param array $setup Dependencies to set up.
+ * @param WP_Web_Fonts $wp_webfonts Instance of WP_Web_Fonts.
+ * @param bool $enqueue Whether to enqueue. Default true.
+ */
+ private function setup_integrated_deps( array $setup, $wp_webfonts, $enqueue = true ) {
+ foreach ( $setup['provider'] as $provider ) {
+ $wp_webfonts->register_provider( $provider['id'], $provider['class'] );
+ }
+ foreach ( $setup['registered'] as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $wp_webfonts );
+ }
+
+ if ( $enqueue ) {
+ $wp_webfonts->enqueue( $setup['enqueued'] );
+ }
+ }
+}
diff --git a/phpunit/webfonts/wpRegisterWebfontProvider-test.php b/phpunit/webfonts/wpRegisterWebfontProvider-test.php
new file mode 100644
index 0000000000000..8f1367d6dbb23
--- /dev/null
+++ b/phpunit/webfonts/wpRegisterWebfontProvider-test.php
@@ -0,0 +1,96 @@
+set_up_mock( 'register_provider' );
+ $mock->expects( $this->once() )
+ ->method( 'register_provider' )
+ ->with(
+ $this->identicalTo( $provider_id ),
+ $this->identicalTo( $class )
+ )
+ ->will( $this->returnValue( true ) );
+
+ $this->assertTrue( wp_register_webfont_provider( $provider_id, $class ), 'wp_register_webfont_provider() should return true' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_register_providers() {
+ return array(
+ 'mock' => array(
+ 'provider_id' => 'mock',
+ 'class' => Mock_Provider::class,
+ ),
+ 'local' => array(
+ 'provider_id' => 'local',
+ 'class' => WP_Webfonts_Provider_Local::class,
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_invalid_providers
+ *
+ * @param string $provider_id Provider ID.
+ * @param string $class Provider class name.
+ */
+ public function test_should_not_register( $provider_id, $class ) {
+ $mock = $this->set_up_mock( 'register_provider' );
+ $mock->expects( $this->once() )
+ ->method( 'register_provider' )
+ ->with(
+ $this->identicalTo( $provider_id ),
+ $this->identicalTo( $class )
+ )
+ ->will( $this->returnValue( false ) );
+
+ $this->assertFalse( wp_register_webfont_provider( $provider_id, $class ), 'wp_register_webfont_provider() should return false' );
+
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_invalid_providers() {
+ return array(
+ 'provider_id is empty' => array(
+ 'provider_id' => '',
+ 'class' => Mock_Provider::class,
+ ),
+ 'class is empty' => array(
+ 'provider_id' => 'local',
+ 'class' => '',
+ ),
+ 'class does not exist' => array(
+ 'provider_id' => 'doesnotexist',
+ 'class' => 'Provider_Does_Not_Exist',
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpRegisterWebfonts-test.php b/phpunit/webfonts/wpRegisterWebfonts-test.php
new file mode 100644
index 0000000000000..11dbf9d73c2ea
--- /dev/null
+++ b/phpunit/webfonts/wpRegisterWebfonts-test.php
@@ -0,0 +1,325 @@
+assertSame( $expected['wp_register_webfonts'], $actual, 'Font family handle(s) should be returned' );
+ $this->assertSame( $expected['get_registered'], $this->get_registered_handles(), 'Web fonts should match registered queue' );
+ }
+
+ /**
+ * @dataProvider data_webfonts
+ *
+ * @param array $webfonts Array of webfonts to test.
+ */
+ public function test_should_not_enqueue_on_registration( array $webfonts ) {
+ wp_register_webfonts( $webfonts );
+ $this->assertEmpty( $this->get_enqueued_handles() );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_webfonts() {
+ return array(
+ 'font family keyed with slug' => array(
+ 'webfonts' => array(
+ 'source-serif-pro' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'wp_register_webfonts' => array( 'source-serif-pro' ),
+ 'get_registered' => array(
+ 'source-serif-pro',
+ 'source-serif-pro-200-900-normal',
+ ),
+ ),
+ ),
+ 'font family keyed with name' => array(
+ 'webfonts' => array(
+ 'Source Serif Pro' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'wp_register_webfonts' => array( 'source-serif-pro' ),
+ 'get_registered' => array(
+ 'source-serif-pro',
+ 'source-serif-pro-200-900-normal',
+ 'source-serif-pro-200-900-italic',
+ ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_deprecated_structure
+ *
+ * @param array $webfonts Webfonts to test.
+ */
+ public function test_should_throw_deprecation_with_deprecated_structure( array $webfonts ) {
+ $this->expectDeprecation();
+ $this->expectDeprecationMessage(
+ 'A deprecated web fonts array structure passed to wp_register_webfonts(). ' .
+ 'Variations must be grouped and keyed by their font family.'
+ );
+
+ wp_register_webfonts( $webfonts );
+ }
+
+ /**
+ * @dataProvider data_deprecated_structure
+ *
+ * @param array $webfonts Webfonts to test.
+ * @param array $expected Expected results.
+ */
+ public function test_should_register_with_deprecated_structure( array $webfonts, array $expected ) {
+ $this->suppress_deprecations();
+
+ $actual = wp_register_webfonts( $webfonts );
+ $this->assertSame( $expected['wp_register_webfonts'], $actual, 'Font family handle(s) should be returned' );
+ $this->assertSame( $expected['get_registered'], $this->get_registered_handles(), 'Web fonts should match registered queue' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_deprecated_structure() {
+ return array(
+ '1 web font' => array(
+ 'webfonts' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Merriweather',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ ),
+ 'expected' => array(
+ 'wp_register_webfonts' => array( 'merriweather' ),
+ 'get_registered' => array( 'merriweather', 'merriweather-200-900-normal' ),
+ ),
+ ),
+ '2 web font in same font family' => array(
+ 'webfonts' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'expected' => array(
+ 'wp_register_webfonts' => array( 'source-serif-pro' ),
+ 'get_registered' => array(
+ 'source-serif-pro',
+ 'source-serif-pro-300-normal',
+ 'source-serif-pro-900-italic',
+ ),
+ ),
+ ),
+ 'Web fonts in different font families' => array(
+ 'webfonts' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Merriweather',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'expected' => array(
+ 'wp_register_webfonts' => array( 'source-serif-pro', 'merriweather' ),
+ 'get_registered' => array(
+ 'source-serif-pro',
+ 'source-serif-pro-300-normal',
+ 'source-serif-pro-900-italic',
+ 'merriweather',
+ 'merriweather-200-900-normal',
+ ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_invalid_font_family
+ *
+ * @param array $webfonts Web fonts to test.
+ * @param string $expected_message Expected notice message.
+ */
+ public function test_should_not_register_with_undefined_font_family( array $webfonts, $expected_message ) {
+ $this->suppress_deprecations();
+
+ $this->expectNotice();
+ $this->expectNoticeMessage( $expected_message );
+
+ $actual = wp_register_webfonts( $webfonts );
+ $this->assertSame( array(), $actual, 'Return value should be an empty array' );
+ $this->assertEmpty( $this->get_registered_handles(), 'No Web fonts should have registered' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_invalid_font_family() {
+ return array(
+ 'non-string' => array(
+ 'webfonts' => array(
+ array(
+ 'provider' => 'local',
+ 'font-family' => null,
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ ),
+ 'expected_message' => 'Font family not defined in the variation.',
+ ),
+ 'empty string in deprecated structure' => array(
+ 'webfonts' => array(
+ '0' => array(
+ 'provider' => 'local',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'expected_message' => 'Font family not found.',
+ ),
+ 'incorrect parameter in deprecated structure' => array(
+ 'webfonts' => array(
+ array(
+ 'provider' => 'local',
+ 'FontFamily' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '300',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font_family' => 'Merriweather',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ ),
+ array(
+ 'provider' => 'local',
+ 'font family' => 'Source Serif Pro',
+ 'font-style' => 'italic',
+ 'font-weight' => '900',
+ 'font-display' => 'fallback',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'expected_message' => 'Font family not found.',
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts-test.php b/phpunit/webfonts/wpWebfonts-test.php
new file mode 100644
index 0000000000000..17ab8c60583fa
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts-test.php
@@ -0,0 +1,38 @@
+assertInstanceOf( WP_Web_Fonts::class, wp_webfonts() );
+ }
+
+ public function test_global_set() {
+ global $wp_webfonts;
+ $this->assertNull( $wp_webfonts );
+ $instance = wp_webfonts();
+ $this->assertInstanceOf( WP_Web_Fonts::class, $wp_webfonts );
+ $this->assertSame( $instance, $wp_webfonts );
+ }
+
+ public function test_local_provider_is_automatically_registered() {
+ $expected = array(
+ 'local' => array(
+ 'class' => 'WP_Webfonts_Provider_Local',
+ 'fonts' => array(),
+ ),
+ );
+ $this->assertSame( $expected, wp_webfonts()->get_providers() );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/add-test.php b/phpunit/webfonts/wpWebfonts/add-test.php
new file mode 100644
index 0000000000000..a432c7e857212
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/add-test.php
@@ -0,0 +1,45 @@
+assertTrue( $wp_webfonts->add( $handle, false ), 'Registering a handle should return true' );
+ $this->assertCount( 1, $wp_webfonts->registered );
+ $this->assertArrayHasKey( $handle, $wp_webfonts->registered, 'Font family handle should be in the registry after registration' );
+
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_handles() {
+ return array(
+ 'name: multiple' => array( 'Source Serif Pro' ),
+ 'handle: multiple' => array( 'source-serif-pro' ),
+ 'name: single' => array( 'Merriweather' ),
+ 'handle: single' => array( 'merriweather' ),
+ 'handle: variation' => array( 'my-custom-font-200-900-normal' ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/addFontFamily-test.php b/phpunit/webfonts/wpWebfonts/addFontFamily-test.php
new file mode 100644
index 0000000000000..4aa5e55b1ef3c
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/addFontFamily-test.php
@@ -0,0 +1,66 @@
+add_font_family( $font_family );
+
+ $this->assertSame( $expected, $font_family_handle, 'Registering a font-family should return its handle' );
+ $this->assertCount( 1, $wp_webfonts->registered );
+ $this->assertArrayHasKey( $font_family_handle, $wp_webfonts->registered, 'Font family handle should be in the registry after registration' );
+
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_handles() {
+ return array(
+ 'name: multiple' => array(
+ 'font_family' => 'Source Serif Pro',
+ 'expected' => 'source-serif-pro',
+ ),
+ 'handle: multiple' => array(
+ 'font_family' => 'source-serif-pro',
+ 'expected' => 'source-serif-pro',
+ ),
+ 'name: single' => array(
+ 'font_family' => 'Merriweather',
+ 'expected' => 'merriweather',
+ ),
+ 'handle: single' => array(
+ 'font_family' => 'merriweather',
+ 'expected' => 'merriweather',
+ ),
+ 'handle: variation' => array(
+ 'font_family' => 'my-custom-font-200-900-normal',
+ 'expected' => 'my-custom-font-200-900-normal',
+ ),
+ 'name: multiple font-families' => array(
+ 'font_family' => 'Source Serif Pro, Merriweather',
+ 'expected' => 'source-serif-pro',
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/addVariation-test.php b/phpunit/webfonts/wpWebfonts/addVariation-test.php
new file mode 100644
index 0000000000000..5f948b81fd146
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/addVariation-test.php
@@ -0,0 +1,149 @@
+add( $font_family_handle, false );
+
+ $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle );
+ $this->assertSame( $expected, $variation_handle, 'Registering a variation should return its handle' );
+ $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' );
+ $this->assertSame( array( $expected ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should be registered to font family' );
+ }
+
+ /**
+ * @dataProvider data_valid_variation
+ *
+ * @param string|bool $expected Expected results.
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ * @param string $variation_handle Optional. The variation's handle.
+ */
+ public function test_should_not_reregister_font_family( $expected, $font_family_handle, array $variation, $variation_handle = '' ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->add( $font_family_handle, false );
+
+ $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle );
+
+ // Font family should appear only once in the registered queue.
+ $expected = array( $font_family_handle, $variation_handle );
+ $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Font family should not be re-registered after registering a variation' );
+ }
+
+ /**
+ * @dataProvider data_valid_variation
+ *
+ * @param string|bool $expected Expected results.
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ * @param string $variation_handle Optional. The variation's handle.
+ */
+ public function test_should_not_reregister_variation( $expected, $font_family_handle, array $variation, $variation_handle = '' ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->add( $font_family_handle, false );
+
+ // Set up the test.
+ $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle );
+
+ // Run the test.
+ $variant_handle_on_reregister = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle );
+ $this->assertSame( $expected, $variant_handle_on_reregister, 'Variation should be registered to font family' );
+ $this->assertSame( $variation_handle, $variant_handle_on_reregister, 'Variation should return the previously registered variant handle' );
+ $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should only be registered once' );
+
+ $this->assertCount( 2, $wp_webfonts->registered );
+ $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' );
+ }
+
+ /**
+ * @dataProvider data_valid_variation
+ *
+ * @param string|bool $expected Expected results.
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ * @param string $variation_handle Optional. The variation's handle.
+ */
+ public function test_should_register_font_family_and_variation( $expected, $font_family_handle, array $variation, $variation_handle = '' ) {
+ $wp_webfonts = new WP_Web_Fonts();
+
+ $variation_handle = $wp_webfonts->add_variation( $font_family_handle, $variation, $variation_handle );
+ $this->assertSame( $expected, $variation_handle, 'Variation should return its registered handle' );
+
+ // Extra checks to ensure both are registered.
+ $this->assertCount( 2, $wp_webfonts->registered );
+ $this->assertArrayHasKey( $font_family_handle, $wp_webfonts->registered, 'Font family handle should be in the registry after registration' );
+ $this->assertArrayHasKey( $variation_handle, $wp_webfonts->registered, 'Variation handle should be in the registry after registration' );
+ $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should be registered to the font family' );
+ }
+
+ /**
+ * @dataProvider data_font_family_handle_undefined
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ */
+ public function test_should_not_register_font_family_or_variant( $font_family_handle, array $variation ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( 'Font family handle must be a non-empty string.' );
+
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->add_variation( $font_family_handle, $variation );
+
+ $this->assertEmpty( $wp_webfonts->registered, 'Registered queue should be empty' );
+ $this->assertEmpty( $this->get_variations( $font_family_handle, $wp_webfonts ), 'Variation should not be registered to the font family' );
+ }
+
+ /**
+ * @dataProvider data_font_family_undefined_in_variation
+ * @dataProviders data_unable_determine_variation_handle
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ * @param string $expected_message Expected notice message.
+ */
+ public function test_should_not_register_variation_when_font_family_not_defined( $font_family_handle, array $variation, $expected_message ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( $expected_message );
+
+ $wp_webfonts = new WP_Web_Fonts();
+ $this->assertNull( $wp_webfonts->add_variation( $font_family_handle, $variation ) );
+ }
+
+ /**
+ * @dataProvider data_unable_determine_variation_handle
+ *
+ * @param string $font_family_handle The font family's handle for this variation.
+ * @param array $variation An array of variation properties to add.
+ */
+ public function test_should_register_font_family_when_variant_fails_to_register( $font_family_handle, array $variation ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' );
+
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->add_variation( $font_family_handle, $variation );
+
+ $this->assertCount( 1, $wp_webfonts->registered );
+ $this->assertArrayHasKey( $font_family_handle, $wp_webfonts->registered );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/dequeue-test.php b/phpunit/webfonts/wpWebfonts/dequeue-test.php
new file mode 100644
index 0000000000000..c4959958a2ccf
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/dequeue-test.php
@@ -0,0 +1,72 @@
+dequeue( $handles );
+ $this->assertEmpty( $this->get_queued_before_register( $wp_webfonts ), 'Prequeue should be empty' );
+ $this->assertEmpty( $wp_webfonts->queue, 'Queue should be empty' );
+ }
+
+ /**
+ * Integration test for dequeuing from queue. It first registers and then enqueues before dequeuing.
+ *
+ * @dataProvider data_enqueue
+ * @dataProvider data_enqueue_variations
+ *
+ * @param string|string[] $handles Handles to test.
+ */
+ public function test_should_dequeue_from_queue( $handles ) {
+ $wp_webfonts = new WP_Web_Fonts();
+
+ // Register and enqueue.
+ foreach ( $this->get_data_registry() as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $wp_webfonts );
+ }
+ $wp_webfonts->enqueue( $handles );
+
+ // To make sure the handles are in the queue before dequeuing.
+ $this->assertNotEmpty( $wp_webfonts->queue, 'Queue not be empty before dequeueing' );
+
+ // Run the test.
+ $wp_webfonts->dequeue( $handles );
+ $this->assertEmpty( $wp_webfonts->queue, 'Queue should be empty after dequeueing' );
+ }
+
+ /**
+ * Integration test for dequeuing from prequeue. It enqueues first.
+ *
+ * @dataProvider data_enqueue
+ * @dataProvider data_enqueue_variations
+ *
+ * @param string|string[] $handles Handles to test.
+ */
+ public function test_should_dequeue_from_prequeue( $handles ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->enqueue( $handles );
+ $this->assertNotEmpty( $this->get_queued_before_register( $wp_webfonts ), 'Prequeue not be empty before dequeueing' );
+
+ $wp_webfonts->dequeue( $handles );
+ $this->assertEmpty( $this->get_queued_before_register( $wp_webfonts ), 'Prequeue should be empty after dequeueing' );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/doItem-test.php b/phpunit/webfonts/wpWebfonts/doItem-test.php
new file mode 100644
index 0000000000000..e54b909f83429
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/doItem-test.php
@@ -0,0 +1,336 @@
+wp_webfonts = new WP_Web_Fonts;
+ }
+
+ public function test_should_return_false_when_provider_not_registered() {
+ $this->assertFalse( $this->wp_webfonts->do_item( 'provider_not_registered' ) );
+ }
+
+ /**
+ * @dataProvider data_provider_definitions
+ *
+ * @param array $provider Provider to mock.
+ */
+ public function test_should_return_false_when_no_fonts_enqueued_for_provider( array $provider ) {
+ $this->setup_provider_property_mock( $this->wp_webfonts, $provider );
+ $this->assertFalse( $this->wp_webfonts->do_item( $provider['id'] ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_provider_definitions() {
+ $providers = $this->get_provider_definitions();
+
+ return array(
+ 'mock' => array( $providers['mock'] ),
+ 'local' => array( $providers['local'] ),
+ );
+ }
+
+ /**
+ * Test the test set up to ensure the `Tests_Webfonts_WpWebfonts_DoItem_::setup_provider_property_mock()`
+ * method works as expected.
+ */
+ public function test_mocking_providers_property() {
+ $font_handles = array( 'font1', 'font2', 'font3' );
+ $expected = array(
+ 'mock' => array(
+ 'class' => Mock_Provider::class,
+ 'fonts' => $font_handles,
+ ),
+ );
+
+ $this->setup_provider_property_mock( $this->wp_webfonts, $this->get_provider_definitions( 'mock' ), $font_handles );
+ $actual = $this->property['WP_Web_Fonts::$providers']->getValue( $this->wp_webfonts );
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * Test the private method WP_Web_Fonts::get_enqueued_fonts_for_provider().
+ *
+ * Why? This test validates the right fonts are returned for use within
+ * WP_Web_Fonts::do_item().
+ *
+ * @dataProvider data_get_enqueued_fonts_for_provider
+ *
+ * @param array $font_handles Array of handles for the provider.
+ * @param array $to_do Handles to set for the WP_Web_Fonts::$to_do property.
+ * @param array $expected Expected result.
+ */
+ public function test_get_enqueued_fonts_for_provider( $font_handles, $to_do, $expected ) {
+ // Set up the `to_do` property.
+ $this->wp_webfonts->to_do = $to_do;
+
+ // Open the method's visibility for testing.
+ $get_enqueued_fonts_for_provider = $this->get_reflection_method( 'get_enqueued_fonts_for_provider' );
+
+ // Mock the WP_Web_Fonts::$property to set up the test.
+ $this->setup_provider_property_mock( $this->wp_webfonts, $this->get_provider_definitions( 'mock' ), $font_handles );
+
+ $actual = $get_enqueued_fonts_for_provider->invoke( $this->wp_webfonts, 'mock' );
+ $this->assertSameSets( $expected, $actual );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_get_enqueued_fonts_for_provider() {
+ return array(
+ 'to_do queue is empty' => array(
+ 'font_handles ' => array( 'font1', 'font2', 'font3' ),
+ 'to_do' => array(),
+ 'expected' => array(),
+ ),
+ 'fonts not in to_do queue' => array(
+ 'font_handles ' => array( 'font1', 'font2', 'font3' ),
+ 'to_do' => array( 'font12', 'font13' ),
+ 'expected' => array(),
+ ),
+ '2 of the provider fonts in to_do queue' => array(
+ 'font_handles ' => array( 'font11', 'font12', 'font13' ),
+ 'to_do' => array( 'font11', 'font13' ),
+ 'expected' => array( 'font11', 'font13' ),
+ ),
+ 'do all of the provider fonts' => array(
+ 'font_handles ' => array( 'font21', 'font22', 'font23' ),
+ 'to_do' => array( 'font21', 'font22', 'font23' ),
+ 'expected' => array( 'font21', 'font22', 'font23' ),
+ ),
+ );
+ }
+
+ /**
+ * Test the private method WP_Web_Fonts::get_font_properties_for_provider().
+ *
+ * Why? This test validates the right font properties are returned for use within
+ * WP_Web_Fonts::do_item().
+ *
+ * @dataProvider data_get_font_properties_for_provider
+ *
+ * @param array $font_handles Web fonts for testing.
+ * @param array $expected Expected result.
+ */
+ public function test_get_font_properties_for_provider( $font_handles, $expected ) {
+ // Set up the fonts for WP_Dependencies:get_data().
+ $fonts = $this->get_registered_fonts();
+ // Set all variations to 'mock' provider.
+
+ // Mock the WP_Web_Fonts::$property to set up the test.
+ $this->setup_provider_property_mock( $this->wp_webfonts, $this->get_provider_definitions( 'mock' ), $font_handles );
+ $this->setup_registration_mocks( $fonts, $this->wp_webfonts );
+
+ // Open the method's visibility for testing.
+ $method = $this->get_reflection_method( 'get_font_properties_for_provider' );
+
+ $actual = $method->invoke( $this->wp_webfonts, $font_handles );
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_get_font_properties_for_provider() {
+ $fonts = $this->get_registered_fonts();
+
+ return array(
+ 'handles not registered' => array(
+ 'font_handles' => array( 'font-not-registered1', 'font-not-registered2', 'font-not-registered3' ),
+ 'expected' => array(),
+ ),
+ 'registered and non-registered handles' => array(
+ 'font_handles' => array( 'Source Serif Pro-300-normal', 'not-registered-handle', 'Source Serif Pro-900-italic' ),
+ 'expected' => array(
+ 'Source Serif Pro-300-normal' => $fonts['Source Serif Pro']['Source Serif Pro-300-normal'],
+ 'Source Serif Pro-900-italic' => $fonts['Source Serif Pro']['Source Serif Pro-900-italic'],
+ ),
+ ),
+ 'font-family handles, ie no "font-properties" extra data' => array(
+ 'font_handles' => array( 'font1', 'font2', 'merriweather' ),
+ 'expected' => array(),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_print_enqueued_fonts
+ *
+ * @param array $provider Define provider.
+ * @param array $fonts Fonts to register and enqueue.
+ * @param array $expected Expected results.
+ */
+ public function test_should_trigger_provider_when_mocked( array $provider, array $fonts, array $expected ) {
+ $this->setup_print_deps( $provider, $fonts );
+
+ $provider_mock = $this->setup_object_mock( array( 'set_webfonts', 'print_styles' ), $provider['class'] );
+
+ // Test the provider's methods are invoked.
+ $provider_mock->expects( $this->once() )->method( 'set_webfonts' )->with( $this->identicalTo( $expected['set_webfonts'] ) );
+ $provider_mock->expects( $this->once() )->method( 'print_styles' );
+
+ // Set up the WP_Web_Fonts::$provider_instances property.
+ $provider_instances = $this->get_reflection_property( 'provider_instances' );
+ $provider_instances->setValue( $this->wp_webfonts, array( $provider['id'] => $provider_mock ) );
+
+ // Test the method successfully processes the provider.
+ $this->expectOutputString( '' );
+ $this->assertTrue( $this->wp_webfonts->do_item( $provider['id'] ), 'WP_Web_Fonts::do_item() should return true' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_print_enqueued_fonts
+ *
+ * @param array $provider Define provider.
+ * @param array $fonts Fonts to register and enqueue.
+ * @param array $expected Expected results.
+ */
+ public function test_should_print( array $provider, array $fonts, array $expected ) {
+ $this->setup_print_deps( $provider, $fonts );
+
+ // Test the method successfully processes the provider.
+ $this->expectOutputString( $expected['printed_output'] );
+ $this->assertTrue( $this->wp_webfonts->do_item( $provider['id'] ), 'WP_Web_Fonts::do_item() should return true' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_print_enqueued_fonts() {
+ $mock = $this->get_registered_mock_fonts();
+ $local = $this->get_registered_local_fonts();
+ $font_faces = $this->get_registered_fonts_css();
+
+ return array(
+ 'mock' => array(
+ 'provider' => $this->get_provider_definitions( 'mock' ),
+ 'fonts' => $mock,
+ 'expected' => array(
+ 'set_webfonts' => array_merge( $mock['font1'], $mock['font2'], $mock['font3'] ),
+ 'printed_output' => sprintf(
+ '%s; %s; %s; %s; %s; %s\n',
+ $font_faces['font1-300-normal'],
+ $font_faces['font1-300-italic'],
+ $font_faces['font1-900-normal'],
+ $font_faces['font2-200-900-normal'],
+ $font_faces['font2-200-900-italic'],
+ $font_faces['font3-bold-normal']
+ ),
+ ),
+ ),
+ 'local' => array(
+ 'provider' => $this->get_provider_definitions( 'local' ),
+ 'fonts' => $local,
+ 'expected' => array(
+ 'set_webfonts' => array_merge( $local['merriweather'], $local['Source Serif Pro'] ),
+ 'printed_output' => sprintf(
+ "\n",
+ $font_faces['merriweather-200-900-normal'],
+ $font_faces['Source Serif Pro-300-normal'],
+ $font_faces['Source Serif Pro-900-italic']
+ ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_not_print_enqueued_fonts
+ *
+ * @param array $provider Define provider.
+ * @param array $fonts Fonts to register and enqueue.
+ * @param array $expected Not used.
+ * @param array $to_do_queue Value to set in the WP_Web_Fonts::$to_do queue.
+ */
+ public function test_should_not_print_when_to_do_queue_empty( array $provider, array $fonts, $expected, $to_do_queue ) {
+ $this->setup_print_deps( $provider, $fonts, $to_do_queue );
+
+ // Test the method successfully processes the provider.
+ $this->expectOutputString( '' );
+ $this->assertFalse( $this->wp_webfonts->do_item( $provider['id'] ), 'WP_Web_Fonts::do_item() should return false' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_not_print_enqueued_fonts() {
+ $mock = $this->get_registered_mock_fonts();
+ $local = $this->get_registered_local_fonts();
+
+ return array(
+ 'mock provider when to_do queue is empty' => array(
+ 'provider' => $this->get_provider_definitions( 'mock' ),
+ 'fonts' => $mock,
+ 'expected' => array(),
+ 'to_do_queue' => array(),
+ ),
+ 'local provider when to_do queue is empty' => array(
+ 'provider' => $this->get_provider_definitions( 'local' ),
+ 'fonts' => $local,
+ 'expected' => array(),
+ 'to_do_queue' => array(),
+ ),
+ 'fonts not in to_do queue' => array(
+ 'provider' => $this->get_provider_definitions( 'mock' ),
+ 'fonts' => $mock,
+ 'expected' => array(),
+ 'to_do_queue' => array(),
+ ),
+ );
+ }
+
+ /**
+ * Sets up the print dependencies.
+ *
+ * @param array $provider Provider id and class.
+ * @param array $fonts Fonts to register and enqueue.
+ * @param array|null $to_do_queue Set the WP_Web_Fonts:$to_do queue.
+ */
+ private function setup_print_deps( $provider, $fonts, $to_do_queue = null ) {
+ // Set up the fonts for WP_Dependencies:get_data().
+ $mocks = $this->setup_registration_mocks( $fonts, $this->wp_webfonts );
+ $handles = array_keys( $mocks );
+ $this->setup_provider_property_mock( $this->wp_webfonts, $provider, $handles );
+
+ // Set up the `WP_Web_Fonts::$to_do` and `WP_Web_Fonts::$to_do_keyed_handles` properties.
+ if ( null === $to_do_queue ) {
+ $to_do_queue = $handles;
+ }
+
+ $this->wp_webfonts->to_do = $to_do_queue;
+ $to_do_keyed_handles = $this->get_reflection_property( 'to_do_keyed_handles' );
+ $to_do_keyed_handles->setValue( $this->wp_webfonts, array_flip( $to_do_queue ) );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/doItems-test.php b/phpunit/webfonts/wpWebfonts/doItems-test.php
new file mode 100644
index 0000000000000..7efb8dff67b7e
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/doItems-test.php
@@ -0,0 +1,196 @@
+wp_webfonts = new WP_Web_Fonts;
+ }
+
+ public function test_should_not_process_when_no_providers_registered() {
+ $this->setup_deps( array( 'enqueued' => 'font1' ) );
+
+ $done = $this->wp_webfonts->do_items();
+
+ $this->assertSame( array(), $done, 'WP_Web_Fonts::do_items() should return an empty array' );
+ $this->assertSame( array(), $this->wp_webfonts->to_do, 'WP_Web_Fonts::$to_do should be an empty array' );
+ }
+
+ /**
+ * @dataProvider data_invalid_handles
+ *
+ * @param mixed $handles Handles to test.
+ */
+ public function test_should_throw_notice_when_invalid_handles( $handles ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( 'Handles must be a non-empty string or array of non-empty strings' );
+
+ $this->wp_webfonts->do_items( $handles );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_invalid_handles() {
+ return array(
+ 'null' => array( null ),
+ 'empty array' => array( array() ),
+ 'empty string' => array( '' ),
+ 'array of empty strings' => array( array( '', '' ) ),
+ 'array of mixed falsey values' => array( array( '', false, null, array() ) ),
+ );
+ }
+
+ public function test_should_throw_notice_when_provider_class_not_found() {
+ $this->expectNotice();
+ $this->expectNoticeMessage( 'Class "Provider_Does_Not_Exist" not found for "doesnotexist" web font provider' );
+
+ $setup = array(
+ 'provider' => array(
+ 'doesnotexist' => array(
+ 'id' => 'doesnotexist',
+ 'class' => 'Provider_Does_Not_Exist',
+ ),
+ ),
+ 'provider_handles' => array( 'doesnotexist' => array( 'font1' ) ),
+ 'registered' => array(
+ 'doesnotexist' => array(
+ 'font1' => array(
+ 'font1-300-normal' => array(
+ 'provider' => 'doesnotexist',
+ 'font-weight' => '300',
+ 'font-style' => 'normal',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ ),
+ ),
+ 'enqueued' => array( 'font1', 'font1-300-normal' ),
+ );
+ $this->setup_deps( $setup );
+
+ $this->wp_webfonts->do_items();
+ }
+
+ /**
+ * @dataProvider data_print_enqueued
+ *
+ * @param array $setup Test set up information for provider, fonts, and enqueued.
+ * @param array $expected_done Expected array of printed handles.
+ * @param string $expected_output Expected printed output.
+ */
+ public function test_should_print_mocked_enqueued( $setup, $expected_done, $expected_output ) {
+ $this->setup_deps( $setup );
+
+ $this->expectOutputString( $expected_output );
+ $actual_done = $this->wp_webfonts->do_items();
+ $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' );
+ }
+
+ /**
+ * Integration test that registers providers and fonts and then enqueues before
+ * testing the printing functionality.
+ *
+ * @dataProvider data_print_enqueued
+ *
+ * @param array $setup Test set up information for provider, fonts, and enqueued.
+ * @param array $expected_done Expected array of printed handles.
+ * @param string $expected_output Expected printed output.
+ */
+ public function test_should_print_enqueued( $setup, $expected_done, $expected_output ) {
+ $this->setup_integrated_deps( $setup );
+
+ $this->expectOutputString( $expected_output, 'Printed @font-face styles should match' );
+ $actual_done = $this->wp_webfonts->do_items();
+ $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' );
+ }
+
+ /**
+ * Integration test to validate printing given handles. Rather than mocking internal functionality,
+ * it registers providers and fonts but does not enqueue.
+ *
+ * @dataProvider data_print_enqueued
+ *
+ * @param array $setup Test set up information for provider, fonts, and enqueued.
+ * @param array $expected_done Expected array of printed handles.
+ * @param string $expected_output Expected printed output.
+ */
+ public function test_should_print_handles_when_not_enqueued( $setup, $expected_done, $expected_output ) {
+ $this->setup_integrated_deps( $setup, false );
+ // Do not enqueue. Instead, pass the handles to WP_Web_Fonts::do_items().
+ $handles = $setup['enqueued'];
+ $this->assertEmpty( $this->wp_webfonts->queue, 'No fonts should be enqueued' );
+
+ $this->expectOutputString( $expected_output );
+ $actual_done = $this->wp_webfonts->do_items( $handles );
+ $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' );
+ }
+
+ /**
+ * Sets up the dependencies for the mocked test.
+ *
+ * @param array $setup Dependencies to set up.
+ */
+ private function setup_deps( array $setup ) {
+ $setup = array_merge(
+ array(
+ 'provider' => array(),
+ 'provider_handles' => array(),
+ 'registered' => array(),
+ 'enqueued' => array(),
+ ),
+ $setup
+ );
+
+ if ( ! empty( $setup['provider'] ) ) {
+ foreach ( $setup['provider'] as $provider_id => $provider ) {
+ $this->setup_provider_property_mock( $this->wp_webfonts, $provider, $setup['provider_handles'][ $provider_id ] );
+ }
+ }
+
+ if ( ! empty( $setup['registered'] ) ) {
+ $this->setup_registration_mocks( $setup['registered'], $this->wp_webfonts );
+ }
+
+ if ( ! empty( $setup['enqueued'] ) ) {
+ $queue = $this->get_reflection_property( 'queue' );
+ $queue->setValue( $this->wp_webfonts, $setup['enqueued'] );
+ }
+ }
+
+ /**
+ * Sets up the dependencies for integration test.
+ *
+ * @param array $setup Dependencies to set up.
+ * @param bool $enqueue Whether to enqueue. Default true.
+ */
+ private function setup_integrated_deps( array $setup, $enqueue = true ) {
+ foreach ( $setup['provider'] as $provider ) {
+ $this->wp_webfonts->register_provider( $provider['id'], $provider['class'] );
+ }
+ foreach ( $setup['registered'] as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $this->wp_webfonts );
+ }
+
+ if ( $enqueue ) {
+ $this->wp_webfonts->enqueue( $setup['enqueued'] );
+ }
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/enqueue-test.php b/phpunit/webfonts/wpWebfonts/enqueue-test.php
new file mode 100644
index 0000000000000..dde2d92c6df37
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/enqueue-test.php
@@ -0,0 +1,53 @@
+enqueue( $handles );
+
+ $this->assertSame( $expected, $this->get_queued_before_register( $wp_webfonts ), 'Handles should be added to before registered queue' );
+ $this->assertEmpty( $wp_webfonts->queue, 'Handles should not be added to the enqueue queue when not registered' );
+ }
+
+ /**
+ * Integration test for enqueuing (a) a font family and all of its variations or (b) specific variations.
+ *
+ * @dataProvider data_enqueue
+ * @dataProviders data_enqueue_variations
+ *
+ * @param string|string[] $handles Handles to test.
+ * @param array $expected Expected queue.
+ */
+ public function test_should_enqueue_when_registered( $handles, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ foreach ( $this->get_data_registry() as $font_family => $variations ) {
+ $this->setup_register( $font_family, $variations, $wp_webfonts );
+ }
+
+ $wp_webfonts->enqueue( $handles );
+
+ $this->assertEmpty( $this->get_queued_before_register( $wp_webfonts ), '"queued_before_register" queue should be empty' );
+ $this->assertSame( $expected, $wp_webfonts->queue, 'Queue should contain the given handles' );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/getEnqueued-test.php b/phpunit/webfonts/wpWebfonts/getEnqueued-test.php
new file mode 100644
index 0000000000000..63ea923c085f5
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/getEnqueued-test.php
@@ -0,0 +1,57 @@
+assertEmpty( $wp_webfonts->get_enqueued() );
+ }
+
+ /**
+ * Unit test for when font families are enqueued.
+ *
+ * @dataProvider data_enqueue
+ *
+ * @param string|string[] $not_used Not used.
+ * @param array $expected Expected queue.
+ */
+ public function test_should_return_queue_when_property_has_font_families( $not_used, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->queue = $expected;
+
+ $this->assertSame( $expected, $wp_webfonts->get_enqueued() );
+ }
+
+ /**
+ * Full integration test that registers and enqueues the queue
+ * is properly wired for "get_enqueued()".
+ *
+ * @dataProvider data_enqueue
+ *
+ * @param string|string[] $font_family Font family to test.
+ * @param array $expected Expected queue.
+ */
+ public function test_should_return_queue_when_font_families_registered_and_enqueued( $font_family, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+
+ // Register and enqueue.
+ foreach ( $this->get_data_registry() as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $wp_webfonts );
+ }
+ $wp_webfonts->enqueue( $font_family );
+
+ $this->assertSame( $expected, $wp_webfonts->get_enqueued() );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/getProviders-test.php b/phpunit/webfonts/wpWebfonts/getProviders-test.php
new file mode 100644
index 0000000000000..7aa758582cb7c
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/getProviders-test.php
@@ -0,0 +1,62 @@
+wp_webfonts = new WP_Web_Fonts();
+
+ $this->providers_property = new ReflectionProperty( WP_Web_Fonts::class, 'providers' );
+ $this->providers_property->setAccessible( true );
+ }
+
+ public function test_should_be_empty() {
+ $actual = $this->wp_webfonts->get_providers();
+ $this->assertIsArray( $actual, 'Should return an empty array' );
+ $this->assertEmpty( $actual, 'Should return an empty array when no providers are registered' );
+ }
+
+ /**
+ * @dataProvider data_get_providers
+ *
+ * @param array $providers Array of providers to test.
+ * @param array $expected Expected results.
+ */
+ public function test_get_providers( array $providers, array $expected ) {
+ $this->setup_providers( $providers );
+ $this->assertSame( $expected, $this->wp_webfonts->get_providers() );
+ }
+
+ /**
+ * Sets up the given providers and stores them in the `WP_Web_Fonts::providers` property.
+ *
+ * @param array $providers Array of providers to set up.
+ */
+ private function setup_providers( array $providers ) {
+ $data = array();
+
+ foreach ( $providers as $provider_id => $class ) {
+ $data[ $provider_id ] = array(
+ 'class' => $class,
+ 'fonts' => array(),
+ );
+ }
+
+ $this->providers_property->setValue( $this->wp_webfonts, $data );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/getRegistered-test.php b/phpunit/webfonts/wpWebfonts/getRegistered-test.php
new file mode 100644
index 0000000000000..76b0ab3a5d097
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/getRegistered-test.php
@@ -0,0 +1,90 @@
+assertEmpty( $wp_webfonts->get_registered() );
+ }
+
+ /**
+ * Unit test for when font families are enqueued.
+ *
+ * @dataProvider data_get_registered
+ *
+ * @param array $inputs Font family(ies) and variations to register.
+ */
+ public function test_should_return_queue_when_mocking_registered_property( array $inputs ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $mocks = $this->setup_registration_mocks( $inputs, $wp_webfonts );
+ $expected = array_keys( $mocks );
+
+ $this->assertSame( $expected, $wp_webfonts->get_registered() );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_get_registered() {
+ return array(
+ 'no variations' => array(
+ 'inputs' => array(
+ 'lato' => array(),
+ ),
+ ),
+ 'with 1 variation' => array(
+ 'inputs' => array(
+ 'Source Serif Pro' => array( 'variation-1' ),
+ ),
+ ),
+ 'with 2 variations' => array(
+ 'inputs' => array(
+ 'my-cool-font' => array( 'cool-1', 'cool-2' ),
+ ),
+ ),
+ 'when multiple font families registered' => array(
+ 'inputs' => array(
+ 'font-family-1' => array( 'variation-11', 'variation-12' ),
+ 'font-family-2' => array( 'variation-21', 'variation-22' ),
+ 'font-family-3' => array( 'variation-31', 'variation-32' ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Full integration test that registers varying number of font families and variations
+ * to validate if "get_registered()" internals is property wired to the registered queue.
+ *
+ * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations
+ *
+ * @param string $font_family Not used.
+ * @param array $inputs Font family(ies) and variations to register.
+ * @param array $expected Expected results.
+ */
+ public function test_should_return_queue_when_items_are_registered( $font_family, array $inputs, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+
+ // Register before testing.
+ foreach ( $inputs as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $wp_webfonts );
+ }
+
+ $this->assertSame( $expected, $wp_webfonts->get_registered() );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/query-test.php b/phpunit/webfonts/wpWebfonts/query-test.php
new file mode 100644
index 0000000000000..75b44b2717f2b
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/query-test.php
@@ -0,0 +1,151 @@
+wp_webfonts = new WP_Web_Fonts();
+ }
+
+ /**
+ * @dataProvider data_invalid_query
+ * @dataProvider data_valid_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_should_fail_when_handles_not_registered( $query_handle ) {
+ $this->assertFalse( $this->wp_webfonts->query( $query_handle, 'registered' ) );
+ }
+
+ /**
+ * @dataProvider data_invalid_query
+ * @dataProvider data_valid_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_should_fail_when_handles_not_registered_or_enqueued( $query_handle ) {
+ $this->assertFalse( $this->wp_webfonts->query( $query_handle, 'queue' ) );
+ }
+
+ /**
+ * @dataProvider data_valid_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_registered_query_should_succeed_when_registered( $query_handle ) {
+ $this->setup_registry();
+
+ $actual = $this->wp_webfonts->query( $query_handle, 'registered' );
+ $this->assertInstanceOf( '_WP_Dependency', $actual, 'Query should return an instance of _WP_Dependency' );
+ $this->assertSame( $query_handle, $actual->handle, 'Query object handle should match the given handle to query' );
+ }
+
+ /**
+ * @dataProvider data_valid_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_enqueued_query_should_succeed_when_registered_and_enqueued( $query_handle ) {
+ $this->setup_registry();
+ $this->wp_webfonts->enqueue( $query_handle );
+
+ $this->assertTrue( $this->wp_webfonts->query( $query_handle, 'enqueued' ) );
+ }
+
+ /**
+ * @dataProvider data_valid_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_enqueued_query_should_fail_when_not_registered_but_enqueued( $query_handle ) {
+ $this->wp_webfonts->enqueue( $query_handle );
+
+ $this->assertFalse( $this->wp_webfonts->query( $query_handle, 'enqueued' ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_invalid_query() {
+ return array(
+ 'DM Sans' => array( 'DM Sans' ),
+ 'roboto' => array( 'roboto' ),
+ 'my-font' => array( 'my-font' ),
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_valid_query() {
+ return array(
+ 'lato' => array( 'lato' ),
+ 'merriweather' => array( 'merriweather' ),
+ 'Source Serif Pro' => array( 'source-serif-pro' ),
+ );
+ }
+
+ public function test_done_query_should_fail_when_no_variations() {
+ $this->wp_webfonts->register_provider( 'local', WP_Webfonts_Provider_Local::class );
+ $this->setup_registry();
+ $this->wp_webfonts->enqueue( 'lato' );
+
+ $this->wp_webfonts->do_items( 'lato' );
+
+ $this->assertFalse( $this->wp_webfonts->query( 'lato', 'done' ) );
+ }
+
+ /**
+ * @dataProvider data_done_query
+ *
+ * @param string $query_handle Handle to test.
+ */
+ public function test_done_query_should_succeed_when_registered_and_enqueued( $query_handle ) {
+ $this->wp_webfonts->register_provider( 'local', WP_Webfonts_Provider_Local::class );
+ $this->setup_registry();
+ $this->wp_webfonts->enqueue( $query_handle );
+
+ // Process the web fonts while ignoring all the printed output.
+ $this->expectOutputRegex( '`.`' );
+ $this->wp_webfonts->do_items( $query_handle );
+ $this->getActualOutput();
+
+ $this->assertTrue( $this->wp_webfonts->query( $query_handle, 'done' ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_done_query() {
+ return array(
+ 'merriweather' => array( 'merriweather' ),
+ 'Source Serif Pro' => array( 'source-serif-pro' ),
+ );
+ }
+
+ private function setup_registry() {
+ foreach ( $this->get_registered_local_fonts() as $handle => $variations ) {
+ $this->setup_register( $handle, $variations, $this->wp_webfonts );
+ }
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/registerProvider-test.php b/phpunit/webfonts/wpWebfonts/registerProvider-test.php
new file mode 100644
index 0000000000000..b73623259fcf4
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/registerProvider-test.php
@@ -0,0 +1,116 @@
+assertTrue( $wp_webfonts->register_provider( $provider_id, $class ), 'WP_Web_Fonts::register_provider() should return true' );
+ $this->assertSame( $expected, $wp_webfonts->get_providers(), 'Provider "' . $provider_id . '" should be registered in providers queue' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_register_providers() {
+ return array(
+ 'mock' => array(
+ 'provider_id' => 'mock',
+ 'class' => Mock_Provider::class,
+ 'expected' => array(
+ 'mock' => array(
+ 'class' => Mock_Provider::class,
+ 'fonts' => array(),
+ ),
+ ),
+ ),
+ 'local' => array(
+ 'provider_id' => 'local',
+ 'class' => WP_Webfonts_Provider_Local::class,
+ 'expected' => array(
+ 'local' => array(
+ 'class' => WP_Webfonts_Provider_Local::class,
+ 'fonts' => array(),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function test_should_register_mutliple_providers() {
+ $wp_webfonts = new WP_Web_Fonts();
+ $providers = $this->get_provider_definitions();
+ foreach ( $providers as $provider ) {
+ $this->assertTrue( $wp_webfonts->register_provider( $provider['id'], $provider['class'] ), 'WP_Web_Fonts::register_provider() should return true for provider ' . $provider['id'] );
+ }
+
+ $expected = array(
+ 'mock' => array(
+ 'class' => $providers['mock']['class'],
+ 'fonts' => array(),
+ ),
+ 'local' => array(
+ 'class' => $providers['local']['class'],
+ 'fonts' => array(),
+ ),
+ );
+
+ $this->assertSame( $expected, $wp_webfonts->get_providers(), 'Both local and mock providers should be registered' );
+ }
+
+ /**
+ * @dataProvider data_invalid_providers
+ *
+ * @param string $provider_id Provider ID.
+ * @param string $class Provider class name.
+ */
+ public function test_should_not_register( $provider_id, $class ) {
+ $wp_webfonts = new WP_Web_Fonts();
+
+ $this->assertFalse( $wp_webfonts->register_provider( $provider_id, $class ), 'WP_Web_Fonts::register_provider() should return false' );
+ $this->assertArrayNotHasKey( $provider_id, $wp_webfonts->get_providers(), 'Both local and mock providers should be registered' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_invalid_providers() {
+ return array(
+ 'provider_id is empty' => array(
+ 'provider_id' => '',
+ 'class' => Mock_Provider::class,
+ ),
+ 'class is empty' => array(
+ 'provider_id' => 'local',
+ 'class' => '',
+ ),
+ 'class does not exist' => array(
+ 'provider_id' => 'doesnotexist',
+ 'class' => 'Provider_Does_Not_Exist',
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/remove-test.php b/phpunit/webfonts/wpWebfonts/remove-test.php
new file mode 100644
index 0000000000000..86cf9fa6ad1ba
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/remove-test.php
@@ -0,0 +1,118 @@
+remove( array( 'handle-1', 'handle2' ) );
+
+ $this->assertEmpty( $wp_webfonts->registered );
+ }
+
+ /**
+ * @dataProvider data_remove_when_registered
+ *
+ * @param array $handles Handles to remove.
+ * @param array $expected Expected handles are running test.
+ */
+ public function test_should_remove_when_registered( array $handles, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ $wp_webfonts->registered = $this->generate_registered_queue();
+
+ $wp_webfonts->remove( $handles );
+
+ $this->assertSameSets( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing handles' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_remove_when_registered() {
+ $all = array(
+ 'handle-1',
+ 'handle-2',
+ 'handle-3',
+ 'handle-4',
+ 'handle-5',
+ 'handle-6',
+ 'handle-7',
+ 'handle-8',
+ 'handle-9',
+ 'handle-10',
+ );
+
+ return array(
+ 'remove none' => array(
+ 'handles' => array(),
+ 'expected' => $all,
+ ),
+ 'remove handle-5' => array(
+ 'handles' => array( 'handle-5' ),
+ 'expected' => array(
+ 'handle-1',
+ 'handle-2',
+ 'handle-3',
+ 'handle-4',
+ 'handle-6',
+ 'handle-7',
+ 'handle-8',
+ 'handle-9',
+ 'handle-10',
+ ),
+ ),
+ 'remove 2 from start and end' => array(
+ 'handles' => array( 'handle-1', 'handle-2', 'handle-9', 'handle-10' ),
+ 'expected' => array(
+ 'handle-3',
+ 'handle-4',
+ 'handle-5',
+ 'handle-6',
+ 'handle-7',
+ 'handle-8',
+ ),
+ ),
+ 'remove all' => array(
+ 'handles' => $all,
+ 'expected' => array(),
+ ),
+ 'remove only registered' => array(
+ 'handles' => array( 'handle-1', 'handle-10', 'handle-abc', 'handle-5' ),
+ 'expected' => array(
+ 'handle-2',
+ 'handle-3',
+ 'handle-4',
+ 'handle-6',
+ 'handle-7',
+ 'handle-8',
+ 'handle-9',
+ ),
+ ),
+ );
+ }
+
+ private function generate_registered_queue() {
+ $queue = array();
+ for ( $num = 1; $num <= 10; $num++ ) {
+ $handle = "handle-{$num}";
+ $queue[ $handle ] = $num;
+ }
+
+ return $queue;
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/removeFontFamily-test.php b/phpunit/webfonts/wpWebfonts/removeFontFamily-test.php
new file mode 100644
index 0000000000000..959744e555202
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/removeFontFamily-test.php
@@ -0,0 +1,89 @@
+setup_registration_mocks( $inputs, $wp_webfonts );
+ // Test the before state, just to make sure.
+ $this->assertArrayHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should contain the font family before remove' );
+ $this->assertSame( $registered_handles, array_keys( $wp_webfonts->registered ), 'Font family and variations should be registered before remove' );
+
+ $wp_webfonts->remove_font_family( $font_family );
+
+ $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' );
+ $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' );
+ }
+
+ /**
+ * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations
+ *
+ * @param string $font_family Font family to test.
+ * @param array $inputs Font family(ies) and variations to pre-register.
+ * @param array $registered_handles Not used.
+ * @param array $expected Array of expected handles.
+ */
+ public function test_should_bail_out_when_not_registered( $font_family, array $inputs, array $registered_handles, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ unset( $inputs[ $font_family ] );
+ $this->setup_registration_mocks( $inputs, $wp_webfonts );
+
+ $wp_webfonts->remove_font_family( $font_family );
+
+ $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' );
+ $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' );
+ }
+
+ /**
+ * Integration test for removing a font family and all of its variation when font family is registered.
+ *
+ * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations
+ *
+ * @param string $font_family Font family to test.
+ * @param array $inputs Font family(ies) and variations to pre-register.
+ * @param array $registered_handles Expected handles after registering.
+ * @param array $expected Array of expected handles.
+ */
+ public function test_should_deregister_when_registered( $font_family, array $inputs, array $registered_handles, array $expected ) {
+ $wp_webfonts = new WP_Web_Fonts();
+ // Register all font families and their variations.
+ foreach ( $inputs as $input_font_family => $variations ) {
+ $handle = $wp_webfonts->add_font_family( $input_font_family );
+ foreach ( $variations as $variation_handle => $variation ) {
+ if ( ! is_string( $variation_handle ) ) {
+ $variation_handle = '';
+ }
+ $wp_webfonts->add_variation( $handle, $variation, $variation_handle );
+ }
+ }
+ // Test the before state, just to make sure.
+ $this->assertArrayHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should contain the font family before remove' );
+ $this->assertSame( $registered_handles, array_keys( $wp_webfonts->registered ), 'Font family and variations should be registered before remove' );
+
+ $wp_webfonts->remove_font_family( $font_family );
+
+ $this->assertArrayNotHasKey( $font_family, $wp_webfonts->registered, 'Registered queue should not contain the font family' );
+ $this->assertSame( $expected, array_keys( $wp_webfonts->registered ), 'Registered queue should match after removing font family' );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfonts/removeVariation-test.php b/phpunit/webfonts/wpWebfonts/removeVariation-test.php
new file mode 100644
index 0000000000000..dd3b21a9573ea
--- /dev/null
+++ b/phpunit/webfonts/wpWebfonts/removeVariation-test.php
@@ -0,0 +1,278 @@
+wp_webfonts = new WP_Web_Fonts();
+ $this->fonts_to_register = $this->get_registered_local_fonts();
+ }
+
+ /**
+ * Sets up the unit test by mocking the WP_Dependencies object using stdClass and
+ * registering each font family directly to the WP_Web_Fonts::$registered property
+ * and its variations to the mocked $deps property.
+ */
+ private function setup_unit_test() {
+ $this->setup_registration_mocks( $this->fonts_to_register, $this->wp_webfonts );
+ }
+
+ /**
+ * Sets up the integration test by properly registering each font family and its variations
+ * by using the WP_Web_Fonts::add() and WP_Web_Fonts::add_variation() methods.
+ */
+ private function setup_integration_test() {
+ foreach ( $this->fonts_to_register as $font_family_handle => $variations ) {
+ $this->setup_register( $font_family_handle, $variations, $this->wp_webfonts );
+ }
+ }
+
+ /**
+ * Testing the test setup to ensure it works.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_mocked_setup( $font_family_handle, $variation_handle ) {
+ $this->setup_unit_test();
+
+ $this->assertArrayHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be in the registered queue before remval' );
+ $this->assertContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should be in its font family deps before removal' );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_should_do_nothing_when_variation_and_font_family_not_registered
+ *
+ * @param string $font_family Font family name.
+ * @param string $font_family_handle Font family handle.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_unit_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) {
+ // Set up the test.
+ unset( $this->fonts_to_register[ $font_family ] );
+ $this->setup_unit_test();
+ $registered_queue = $this->wp_webfonts->registered;
+
+ // Run the tests.
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $font_family_handle, $this->wp_webfonts->registered, 'Font family should not be registered' );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertSame( $registered_queue, $this->wp_webfonts->registered, 'Registered queue should not have changed' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_should_do_nothing_when_variation_and_font_family_not_registered
+ *
+ * @param string $font_family Font family name.
+ * @param string $font_family_handle Font family handle.
+ * @param string $variation_handle Variation handle to remove.
+ */
+ public function test_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) {
+ // Set up the test.
+ unset( $this->fonts_to_register[ $font_family ] );
+ $this->setup_integration_test();
+ $registered_queue = $this->wp_webfonts->get_registered();
+
+ // Run the tests.
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $font_family_handle, $this->wp_webfonts->registered, 'Font family should not be registered' );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertSameSets( $registered_queue, $this->wp_webfonts->get_registered(), 'Registered queue should not have changed' );
+ }
+
+ /**
+ * Data provider for testing removal of variations.
+ *
+ * @return array
+ */
+ public function data_should_do_nothing_when_variation_and_font_family_not_registered() {
+ return array(
+ 'Font with 1 variation' => array(
+ 'font_family' => 'merriweather',
+ 'font_family_handle' => 'merriweather',
+ 'variation_handle' => 'merriweather-200-900-normal',
+ ),
+ 'Font with multiple variations' => array(
+ 'font_family' => 'Source Serif Pro',
+ 'font_family_handle' => 'source-serif-pro',
+ 'variation_handle' => 'Source Serif Pro-300-normal',
+ ),
+ );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+ $this->setup_remove_variation_from_registered( $variation_handle );
+
+ // Run the tests.
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+ $this->setup_remove_variation_from_registered( $variation_handle );
+
+ // Run the tests.
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variant should not be registered' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+ $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' );
+
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+ $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' );
+
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Unit test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_unit_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_unit_test();
+
+ $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' );
+
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be not be in registered queue' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Integration test.
+ *
+ * @dataProvider data_remove_variations
+ *
+ * @param string $font_family_handle Font family for the variation.
+ * @param string $variation_handle Variation handle to remove.
+ * @param array $expected Expected results.
+ */
+ public function test_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) {
+ // Set up the test.
+ $this->setup_integration_test();
+
+ $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' );
+
+ $this->wp_webfonts->remove_variation( $font_family_handle, $variation_handle );
+
+ $this->assertArrayNotHasKey( $variation_handle, $this->wp_webfonts->registered, 'Variation should be not be in registered queue' );
+ $this->assertNotContains( $variation_handle, $this->wp_webfonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' );
+ $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_webfonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' );
+ }
+
+ /**
+ * Remove the variation handle from the font family's deps.
+ *
+ * @param string $font_family_handle Font family.
+ * @param string $variation_handle The variation handle to remove.
+ */
+ private function setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ) {
+ foreach ( $this->wp_webfonts->registered[ $font_family_handle ]->deps as $index => $vhandle ) {
+ if ( $variation_handle !== $vhandle ) {
+ continue;
+ }
+ unset( $this->wp_webfonts->registered[ $font_family_handle ]->deps[ $index ] );
+ break;
+ }
+ }
+
+ /**
+ * Removes the variation from the WP_Web_Fonts::$registered queue.
+ *
+ * @param string $variation_handle The variation handle to remove.
+ */
+ private function setup_remove_variation_from_registered( $variation_handle ) {
+ unset( $this->wp_webfonts->registered[ $variation_handle ] );
+ }
+}
diff --git a/phpunit/class-wp-webfonts-local-provider-test.php b/phpunit/webfonts/wpWebfontsProviderLocal-test.php
similarity index 78%
rename from phpunit/class-wp-webfonts-local-provider-test.php
rename to phpunit/webfonts/wpWebfontsProviderLocal-test.php
index 43ef4a84951c8..08f9f18c3cd6a 100644
--- a/phpunit/class-wp-webfonts-local-provider-test.php
+++ b/phpunit/webfonts/wpWebfontsProviderLocal-test.php
@@ -1,10 +1,15 @@
set_up_theme();
}
- /**
- * Local `src` paths to need to be relative to the theme. This method sets up the
- * `wp-content/themes/` directory to ensure consistency when running tests.
- */
- private function set_up_theme() {
- $this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
- $this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
- $GLOBALS['wp_theme_directories'] = array( $this->theme_root );
-
- $theme_root_callback = function () {
- return $this->theme_root;
- };
- add_filter( 'theme_root', $theme_root_callback );
- add_filter( 'stylesheet_root', $theme_root_callback );
- add_filter( 'template_root', $theme_root_callback );
-
- // Clear caches.
- wp_clean_themes_cache();
- unset( $GLOBALS['wp_themes'] );
- }
-
public function tear_down() {
// Restore the original theme directory setup.
$GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
@@ -79,7 +63,7 @@ public function test_set_webfonts() {
/**
* @covers WP_Webfonts_Provider_Local::get_css
*
- * @dataProvider data_get_css
+ * @dataProvider data_get_css_print_styles
*
* @param array $webfonts Prepared webfonts (to store in WP_Webfonts_Provider_Local::$webfonts property).
* @param string $expected Expected CSS.
@@ -88,7 +72,24 @@ public function test_get_css( array $webfonts, $expected ) {
$property = $this->get_webfonts_property();
$property->setValue( $this->provider, $webfonts );
- $this->assertSame( $expected, $this->provider->get_css() );
+ $this->assertSame( $expected['font-face-css'], $this->provider->get_css() );
+ }
+
+ /**
+ * @covers WP_Webfonts_Provider_Local::print_styles
+ *
+ * @dataProvider data_get_css_print_styles
+ *
+ * @param array $webfonts Prepared webfonts (to store in WP_Webfonts_Provider_Local::$webfonts property).
+ * @param string $expected Expected CSS.
+ */
+ public function test_print_styles( array $webfonts, $expected ) {
+ $property = $this->get_webfonts_property();
+ $property->setValue( $this->provider, $webfonts );
+
+ $expected_output = sprintf( $expected['style-element'], $expected['font-face-css'] );
+ $this->expectOutputString( $expected_output );
+ $this->provider->print_styles();
}
/**
@@ -96,7 +97,7 @@ public function test_get_css( array $webfonts, $expected ) {
*
* @return array
*/
- public function data_get_css() {
+ public function data_get_css_print_styles() {
return array(
'truetype format' => array(
'webfonts' => array(
@@ -108,10 +109,13 @@ public function data_get_css() {
'src' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf',
),
),
- 'expected' => << array(
+ 'style-element' => "\n",
+ 'font-face-css' => << array(
'webfonts' => array(
@@ -132,14 +136,39 @@ public function data_get_css() {
'src' => 'http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2',
),
),
- 'expected' => << array(
+ 'style-element' => "\n",
+ 'font-face-css' => <<theme_root = realpath( DIR_TESTDATA . '/themedir1' );
+ $this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
+ $GLOBALS['wp_theme_directories'] = array( $this->theme_root );
+
+ $theme_root_callback = function () {
+ return $this->theme_root;
+ };
+ add_filter( 'theme_root', $theme_root_callback );
+ add_filter( 'stylesheet_root', $theme_root_callback );
+ add_filter( 'template_root', $theme_root_callback );
+
+ // Clear caches.
+ wp_clean_themes_cache();
+ unset( $GLOBALS['wp_themes'] );
+ }
+
private function get_webfonts_property() {
$property = new ReflectionProperty( $this->provider, 'webfonts' );
$property->setAccessible( true );
diff --git a/phpunit/webfonts/wpWebfontsUtils/convertFontFamilyIntoHandle-test.php b/phpunit/webfonts/wpWebfontsUtils/convertFontFamilyIntoHandle-test.php
new file mode 100644
index 0000000000000..d258f1e5c435d
--- /dev/null
+++ b/phpunit/webfonts/wpWebfontsUtils/convertFontFamilyIntoHandle-test.php
@@ -0,0 +1,84 @@
+assertSame( $expected, WP_Webfonts_Utils::convert_font_family_into_handle( $font_family ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_valid_input() {
+ return array(
+ 'font family single word name' => array(
+ 'font_family' => 'Merriweather',
+ 'expected' => 'merriweather',
+ ),
+ 'font family multiword name' => array(
+ 'font_family' => 'Source Sans Pro',
+ 'expected' => 'source-sans-pro',
+ ),
+ 'font family handle delimited by hyphens' => array(
+ 'font_family' => 'source-serif-pro',
+ 'expected' => 'source-serif-pro',
+ ),
+ 'font family handle delimited by underscore' => array(
+ 'font_family' => 'source_serif_pro',
+ 'expected' => 'source_serif_pro',
+ ),
+ 'font family handle delimited by hyphens and underscore' => array(
+ 'font_family' => 'my-custom_font_family',
+ 'expected' => 'my-custom_font_family',
+ ),
+ 'font family handle delimited mixture' => array(
+ 'font_family' => 'My custom_font-family',
+ 'expected' => 'my-custom_font-family',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_with_invalid_input
+ *
+ * @covers WP_Webfonts_Utils::convert_font_family_into_handle
+ *
+ * @param mixed $invalid_input Invalid input.
+ */
+ public function test_should_not_convert_with_invalid_input( $invalid_input ) {
+ $this->assertNull( WP_Webfonts_Utils::convert_font_family_into_handle( $invalid_input ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_invalid_input() {
+ return array(
+ 'empty string' => array( '' ),
+ 'integer' => array( 10 ),
+ 'font family wrapped in an array' => array( array( 'source-serif-pro' ) ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfontsUtils/convertVariationIntoHandle-test.php b/phpunit/webfonts/wpWebfontsUtils/convertVariationIntoHandle-test.php
new file mode 100644
index 0000000000000..9e2a3462ed7a8
--- /dev/null
+++ b/phpunit/webfonts/wpWebfontsUtils/convertVariationIntoHandle-test.php
@@ -0,0 +1,122 @@
+assertSame( $expected, WP_Webfonts_Utils::convert_variation_into_handle( $font_family, $variation ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_valid_input() {
+ return array(
+ 'with only font-weight' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'font-weight' => '400',
+ ),
+ 'expected' => 'merriweather-400',
+ ),
+ 'with no font-style' => array(
+ 'font_family' => 'source-sans-pro',
+ 'variation' => array(
+ 'font-weight' => '200 900',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'provider' => 'local',
+ ),
+ 'expected' => 'source-sans-pro-200-900',
+ ),
+ 'with font family name and full variant' => array(
+ 'font_family' => 'source-sans-pro',
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'expected' => 'source-sans-pro-200-900-normal',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_with_invalid_input
+ *
+ * @param string $font_family Font family to test.
+ * @param array $invalid_input Variation to test.
+ */
+ public function tests_should_convert_with_invalid_input( $font_family, $invalid_input ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' );
+
+ $this->assertNull( WP_Webfonts_Utils::convert_variation_into_handle( $font_family, $invalid_input ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_invalid_input() {
+ return array(
+ 'with no font-weight or font-style' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ ),
+ 'with non-string font-weight' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'font-weight' => 400,
+ ),
+ ),
+ 'with non-string font-style' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'font-style' => 0,
+ ),
+ ),
+ 'with empty string font-weight' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'font-weight' => '',
+ ),
+ ),
+ 'with empty string font-style' => array(
+ 'font_family' => 'merriweather',
+ 'variation' => array(
+ 'font-style' => '',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfontsUtils/getFontFamilyFromVariation-test.php b/phpunit/webfonts/wpWebfontsUtils/getFontFamilyFromVariation-test.php
new file mode 100644
index 0000000000000..f370e1d043222
--- /dev/null
+++ b/phpunit/webfonts/wpWebfontsUtils/getFontFamilyFromVariation-test.php
@@ -0,0 +1,134 @@
+assertSame( $expected, WP_Webfonts_Utils::get_font_family_from_variation( $variation ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_valid_variation() {
+ return array(
+ 'keyed by font-family' => array(
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'expected' => 'Source Serif Pro',
+ ),
+ 'keyed by fontFamily and as a handle' => array(
+ 'variation' => array(
+ 'fontFamily' => 'source-sans-pro',
+ 'font-weight' => '200 900',
+ 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2',
+ 'provider' => 'local',
+ ),
+ 'expected' => 'source-sans-pro',
+ ),
+ 'with font family name and full variant' => array(
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font-family' => 'Merriweather',
+ 'font-style' => 'normal',
+ 'font-weight' => '400 600',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'expected' => 'Merriweather',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider data_with_invalid_input
+ *
+ * @param array $invalid_variation Variation to test.
+ * @param string $expected_message Expected notice message.
+ */
+ public function test_with_invalid_input( array $invalid_variation, $expected_message ) {
+ $this->expectNotice();
+ $this->expectNoticeMessage( $expected_message );
+
+ $this->assertNull( WP_Webfonts_Utils::get_font_family_from_variation( $invalid_variation ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_with_invalid_input() {
+ return array(
+ 'keyed with underscore' => array(
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font_family' => 'Source Serif Pro',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'expected_message' => 'Font family not found.',
+ ),
+ 'keyed with space' => array(
+ 'variation' => array(
+ 'font family' => 'Source Sans Pro',
+ 'font-weight' => '200 900',
+ 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2',
+ 'provider' => 'local',
+ ),
+ 'expected_message' => 'Font family not found.',
+ ),
+ 'fontFamily => empty string' => array(
+ 'variation' => array(
+ 'fontFamily' => '',
+ 'font-weight' => '200 900',
+ 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2',
+ 'provider' => 'local',
+ ),
+ 'expected_message' => 'Font family not defined in the variation.',
+ ),
+ 'font-family => empty string' => array(
+ 'variation' => array(
+ 'provider' => 'local',
+ 'font-family' => '',
+ 'font-style' => 'normal',
+ 'font-weight' => '200 900',
+ 'font-stretch' => 'normal',
+ 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
+ 'font-display' => 'fallback',
+ ),
+ 'expected_message' => 'Font family not defined in the variation.',
+ ),
+ );
+ }
+}
diff --git a/phpunit/webfonts/wpWebfontsUtils/isDefined-test.php b/phpunit/webfonts/wpWebfontsUtils/isDefined-test.php
new file mode 100644
index 0000000000000..c5b2a1608269c
--- /dev/null
+++ b/phpunit/webfonts/wpWebfontsUtils/isDefined-test.php
@@ -0,0 +1,61 @@
+assertTrue( WP_Webfonts_Utils::is_defined( $input ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_when_defined() {
+ return array(
+ 'name: non empty string' => array( 'Some Font Family' ),
+ 'handle: non empty string' => array( 'some-font-family' ),
+ );
+ }
+
+ /**
+ * @dataProvider data_when_not_defined
+ *
+ * @param mixed $invalid_input Input to test.
+ */
+ public function test_should_return_false_when_not_defined( $invalid_input ) {
+ $this->assertFalse( WP_Webfonts_Utils::is_defined( $invalid_input ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_when_not_defined() {
+ return array(
+ 'empty string' => array( '' ),
+ 'string 0' => array( '0' ),
+ 'integer' => array( 10 ),
+ 'name wrapped in an array' => array( array( 'Some Font Family' ) ),
+ 'handle wrapped in an array' => array( array( 'some-font-family' ) ),
+ );
+ }
+}