diff --git a/lib/experimental/class-wp-webfonts-provider-local.php b/lib/experimental/class-wp-webfonts-provider-local.php index 9af27fa7c436a7..4959fe6f586c27 100644 --- a/lib/experimental/class-wp-webfonts-provider-local.php +++ b/lib/experimental/class-wp-webfonts-provider-local.php @@ -35,6 +35,33 @@ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider { */ protected $id = 'local'; + /** + * Holds a string which contains the type attribute for style tag. + * + * If the active theme does not declare HTML5 support for 'style', + * then it initializes as `type='text/css'`. + * + * @since 6.1.0 + * + * @var string + */ + private $type_attr = ''; + + /** + * 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->type_attr = " type='text/css'"; + } + } + /** * Gets the `@font-face` CSS styles for locally-hosted font files. * @@ -81,7 +108,7 @@ class WP_Webfonts_Provider_Local extends WP_Webfonts_Provider { * } * * - * @since 6.0.0 + * @since X.X.X * * @return string The `@font-face` CSS. */ @@ -96,6 +123,12 @@ public function get_css() { $css .= '@font-face{' . $this->build_font_face_css( $webfont ) . '}'; } + $css = sprintf( + "\n", + $this->type_attr, + $css + ); + return $css; } diff --git a/lib/experimental/class-wp-webfonts-provider.php b/lib/experimental/class-wp-webfonts-provider.php index a49b8a324b7f5f..5113c0c46c023a 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,14 +30,14 @@ * 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 { /** * Webfonts to be processed. * - * @since 6.0.0 + * @since X.X.X * * @var array[] */ @@ -49,7 +49,7 @@ abstract class WP_Webfonts_Provider { * 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. */ @@ -64,9 +64,18 @@ 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(); + + /** + * Prints the generated styles. + * + * @since X.X.X + */ + public function print_styles() { + echo $this->get_css(); + } } diff --git a/lib/experimental/class-wp-webfonts.php b/lib/experimental/class-wp-webfonts.php index ecc3407079c1ae..4ec5bdae15098b 100644 --- a/lib/experimental/class-wp-webfonts.php +++ b/lib/experimental/class-wp-webfonts.php @@ -11,6 +11,25 @@ class WP_Webfonts extends WP_Dependencies { */ private $providers = array(); + /** + * The flipped $to_do array of web font handles. + * + * Used for a faster lookup of the web font handles. + * + * @since X.X.X + * + * @var string[] + */ + private $to_do_keyed_handles; + + /** + * Array of provider instances, keyed by provider ID. + * + * @since X.X.X + * + * @var array + */ + private $provider_instances = array(); /** * Constructor. * @@ -218,6 +237,7 @@ private function add_dependency( $font_family_handle, $variation_handle ) { private function validate_variation( $font_family_handle, $variation ) { $defaults = array( 'provider' => 'local', + 'font-family' => $font_family_handle, 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', @@ -291,28 +311,31 @@ private function validate_variation( $font_family_handle, $variation ) { } /** - * Generate styles for webfonts. + * Processes the items and dependencies. * - * @since 6.0.0 + * Processes the items passed to it or the queue, and their dependencies. + * + * @since X.X.X * - * @param array[] $webfonts_by_provider Webfonts organized by provider. - * @return string $styles Generated styles. + * @param string|string[]|false $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_item( $handle, $group = false ) { - if ( ! parent::do_item( $handle ) ) { - return false; - } - - $styles = ''; - $providers = $this->get_providers(); - - $obj = $this->registered[ $handle ]; - + public function do_items( $handles = false, $group = false ) { /* - * Loop through each of the providers to get the CSS for their respective webfonts - * to incrementally generate the collective styles for all of them. + * If nothing is passed, print the queue. If a string is passed, + * print that item. If an array is passed, print those items. */ - foreach ( $providers as $provider_id => $provider ) { + $handles = false === $handles ? $this->queue : (array) $handles; + $this->all_deps( $handles ); + + $this->to_do_keyed_handles = array_flip( $this->to_do ); + + foreach ( $this->get_providers() as $provider_id => $provider ) { // Bail out if the provider class does not exist. if ( ! class_exists( $provider['class'] ) ) { /* translators: %s is the provider name. */ @@ -320,68 +343,134 @@ public function do_item( $handle, $group = false ) { continue; } - $fonts = $this->get_enqueued_fonts_for_provider( $provider_id ); - - // If there are no registered webfonts for this provider, skip it. - if ( empty( $fonts ) ) { - continue; - } - - $provider_fonts = array(); - - foreach ( $fonts as $font_handle ) { - $provider_fonts[ $font_handle ] = $this->get_data( $font_handle, 'font-properties' ); - } - - /* - * Process the webfonts by first passing them to the provider via `set_webfonts()` - * and then getting the CSS from the provider. - */ - $provider = new $provider['class'](); - $provider->set_webfonts( $provider_fonts ); - $styles .= $provider->get_css(); + $this->do_item( $provider_id, $group ); } - return $styles; + return $this->done; } /** - * Register a provider. + * Invokes each provider to process and print its styles. * - * @since 6.0.0 + * @since X.X.X * - * @param string $provider The provider name. - * @param string $class The provider class name. - * @return bool True if successfully registered, else false. + * @see WP_Dependencies::do_item() + * + * @param string $provider_id The font family to process. + * @param int|false $group Not used. + * @return bool */ - public function register_provider( $provider, $class ) { - if ( empty( $provider ) || empty( $class ) || ! class_exists( $class ) ) { + 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; } - $this->providers[ $provider ] = array( - 'class' => $class, - 'fonts' => array(), - ); + $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. + if ( isset( $this->provider_instances[ $provider_id ] ) ) { + $provider = $this->provider_instances[ $provider_id ]; + } else { + $provider = new $this->providers[ $provider_id ]['class'](); + // Store the instance. + $this->provider_instances[ $provider_id ] = $provider; + } + $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 Provider's unique ID. * @return array[] Webfonts organized by providers. */ - private function get_enqueued_fonts_for_provider( $provider ) { + private function get_enqueued_fonts_for_provider( $provider_id ) { $providers = $this->get_providers(); - if ( empty( $providers[ $provider ] ) ) { + if ( empty( $providers[ $provider_id ] ) ) { return array(); } return array_intersect( - $providers[ $provider ]['fonts'], - $this->get_enqueued() + $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; + } + + /** + * 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->done[] = $font_handle; + unset( + $this->to_do[ $this->to_do_keyed_handles[ $font_handle ] ], + $this->to_do_keyed_handles[ $font_handle ] + ); + } + } + + /** + * Register a provider. + * + * @since 6.0.0 + * + * @param string $provider The provider name. + * @param string $class The provider class name. + * @return bool True if successfully registered, else false. + */ + public function register_provider( $provider, $class ) { + if ( empty( $provider ) || empty( $class ) || ! class_exists( $class ) ) { + return false; + } + + $this->providers[ $provider ] = array( + 'class' => $class, + 'fonts' => array(), + ); + return true; + } } diff --git a/lib/experimental/webfonts.php b/lib/experimental/webfonts.php index adc824e8044317..1fd651990adbc7 100644 --- a/lib/experimental/webfonts.php +++ b/lib/experimental/webfonts.php @@ -259,16 +259,20 @@ function wp_get_webfont_providers() { } if ( ! function_exists( 'wp_print_webfonts' ) ) { + /** + * Invokes each provider to process and print its styles. + * + * @since X.X.X + * + * @param string|string[]|false $handles Optional. Items to be processed: queue (false), + * single item (string), or multiple items (array of strings). + * Default false. + * @return array|string[] Array of web font handles that have been processed. + * An empty array if none were processed. + */ function wp_print_webfonts( $handles = false ) { global $wp_webfonts; - /** - * Fires before webfonts in the $handles queue are printed. - * - * @since X.X.X - */ - do_action( 'wp_print_webfonts' ); - if ( '' === $handles ) { // For 'wp_head'. $handles = false; } @@ -285,14 +289,8 @@ function wp_print_webfonts( $handles = false ) { } } - - - - - - - - +add_action( 'admin_print_styles', 'wp_print_webfonts' ); +add_action( 'wp_head', 'wp_print_webfonts' ); /** * Add webfonts mime types. diff --git a/phpunit/fixtures/mock-provider.php b/phpunit/fixtures/mock-provider.php index 152badb1ce266c..dc351625dcadf7 100644 --- a/phpunit/fixtures/mock-provider.php +++ b/phpunit/fixtures/mock-provider.php @@ -13,9 +13,10 @@ class Mock_Provider extends WP_Webfonts_Provider { * * @var string */ - public $css = ''; + public $css = '%s'; public function get_css() { - return $this->css; + $handles = array_keys( $this->webfonts ); + return sprintf( $this->css, implode( '; ', $handles ) ); } } diff --git a/phpunit/webfonts/wp-webfonts-testcase.php b/phpunit/webfonts/wp-webfonts-testcase.php index 495ef996a7e51b..ed8f8b2e2c04b4 100644 --- a/phpunit/webfonts/wp-webfonts-testcase.php +++ b/phpunit/webfonts/wp-webfonts-testcase.php @@ -21,6 +21,13 @@ abstract class WP_Webfonts_TestCase extends WP_UnitTestCase { */ private $old_wp_webfonts; + /** + * Reflection data store for non-public property access. + * + * @var ReflectionProperty[] + */ + protected $property = array(); + public function set_up() { parent::set_up(); @@ -29,15 +36,13 @@ public function set_up() { } public function tear_down() { + $this->property = array(); $GLOBALS['wp_webfonts'] = $this->old_wp_webfonts; parent::tear_down(); } protected function set_up_mock( $method ) { - if ( is_string( $method ) ) { - $method = array( $method ); - } - $mock = $this->getMockBuilder( WP_Webfonts::class )->setMethods( $method )->getMock(); + $mock = $this->setup_object_mock( $method, WP_Webfonts::class ); // Set the global. $GLOBALS['wp_webfonts'] = $mock; @@ -45,6 +50,14 @@ protected function set_up_mock( $method ) { return $mock; } + protected function setup_object_mock( $method, $class ) { + if ( is_string( $method ) ) { + $method = array( $method ); + } + + return $this->getMockBuilder( $class )->setMethods( $method )->getMock(); + } + protected function get_registered_handles() { return array_keys( $this->get_registered() ); } @@ -57,6 +70,7 @@ protected function get_variations( $font_family, $wp_webfonts = null ) { if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { $wp_webfonts = wp_webfonts(); } + return $wp_webfonts->registered[ $font_family ]->deps; } @@ -68,16 +82,51 @@ protected function get_queued_before_register( $wp_webfonts = null ) { return $this->get_property_value( 'queued_before_register', WP_Dependencies::class, $wp_webfonts ); } - protected function get_property_value( $property_name, $class, $wp_webfonts = null ) { + protected function get_reflection_property( $property_name, $class = 'WP_Webfonts' ) { $property = new ReflectionProperty( $class, $property_name ); $property->setAccessible( true ); + return $property; + } + + protected function get_property_value( $property_name, $class, $wp_webfonts = null ) { + $property = $this->get_reflection_property( $property_name, $class ); + if ( ! $wp_webfonts ) { $wp_webfonts = wp_webfonts(); } + return $property->getValue( $wp_webfonts ); } + protected function setup_property( $class, $property_name ) { + $key = $this->get_property_key( $class, $property_name ); + + if ( ! isset( $this->property[ $key ] ) ) { + $this->property[ $key ] = new ReflectionProperty( $class, 'providers' ); + $this->property[ $key ]->setAccessible( true ); + } + + return $this->property[ $key ]; + } + + protected function get_property_key( $class, $property_name ) { + return $class . '::$' . $property_name; + } + + /** + * Opens the accessibility to access the given private or protected method. + * + * @param string $method_name Name of the method to open. + * @return ReflectionMethod Instance of the method, ie to invoke it in the test. + */ + protected function get_reflection_method( $method_name ) { + $method = new ReflectionMethod( WP_Webfonts::class, $method_name ); + $method->setAccessible( true ); + + return $method; + } + /** * Sets up multiple font family and variation mocks. * @@ -88,7 +137,7 @@ protected function get_property_value( $property_name, $class, $wp_webfonts = nu protected function setup_registration_mocks( array $inputs, WP_Webfonts $wp_webfonts ) { $mocks = array(); - $build_mock = function( $handle ) use ( &$mocks, $wp_webfonts ) { + $build_mock = function ( $handle ) use ( &$mocks, $wp_webfonts ) { $mock = new stdClass(); $mock->deps = array(); // Add to each queue. @@ -105,8 +154,9 @@ protected function setup_registration_mocks( array $inputs, WP_Webfonts $wp_webf if ( ! is_string( $variation_handle ) ) { $variation_handle = $variation; } - $build_mock( $variation_handle ); - $font_mock->deps[] = $variation_handle; + $variation_mock = $build_mock( $variation_handle ); + $variation_mock->extra = array( 'font-properties' => $variation ); + $font_mock->deps[] = $variation_handle; } } @@ -134,4 +184,26 @@ protected function setup_register( $font_family, $variations, $wp_webfonts = nul $wp_webfonts->add_variation( $font_family, $variation, $variation_handle ); } } + + /** + * Sets up the WP_Webfonts::$provider property. + * + * @param WP_Webfonts $wp_webfonts Instance of WP_Webfonts. + * @param string $provider_id Provider's unique ID to set up. + * @param array $font_handles Optional. Font handles for this provider. + */ + protected function setup_provider_property_mock( WP_Webfonts $wp_webfonts, $provider_id, array $font_handles = array() ) { + $property = $this->setup_property( WP_Webfonts::class, 'providers' ); + $providers = $property->getValue( $wp_webfonts ); + $value = isset( $providers[ $provider_id ] ) + ? array_merge( $font_handles, $providers[ $provider_id ]['fonts'] ) + : array( + $provider_id => array( + 'class' => 'local' === $provider_id ? WP_Webfonts_Provider_Local::class : Mock_Provider::class, + 'fonts' => $font_handles, + ), + ); + + $property->setValue( $wp_webfonts, $value ); + } } diff --git a/phpunit/webfonts/wp-webfonts-tests-dataset.php b/phpunit/webfonts/wp-webfonts-tests-dataset.php index 70a4326a6c1c15..b9f0af4ca0dcc9 100644 --- a/phpunit/webfonts/wp-webfonts-tests-dataset.php +++ b/phpunit/webfonts/wp-webfonts-tests-dataset.php @@ -552,4 +552,98 @@ protected function get_data_registry() { ), ); } + + protected function get_registered_fonts() { + return array_merge( + $this->get_registered_local_fonts(), + $this->get_registered_mock_fonts() + ); + } + + 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', + ), + ), + ); + } + + protected function get_registered_mock_fonts() { + return array( + 'font1' => array( + 'font1-300-normal' => array( + 'provider' => 'mock', + 'font-weight' => '300', + 'font-style' => 'normal', + 'font-display' => 'fallback', + ), + 'font1-300-italic' => array( + 'provider' => 'mock', + 'font-weight' => '300', + 'font-style' => 'italic', + 'font-display' => 'fallback', + ), + 'font1-900-normal' => array( + 'provider' => 'mock', + 'font-weight' => '900', + 'font-style' => 'normal', + 'font-display' => 'fallback', + ), + ), + 'font2' => array( + 'font2-200-900-normal' => array( + 'provider' => 'mock', + 'font-weight' => '200 900', + 'font-style' => 'normal', + 'font-display' => 'fallback', + ), + 'font2-200-900-italic' => array( + 'provider' => 'mock', + 'font-weight' => '200 900', + 'font-style' => 'italic', + 'font-display' => 'fallback', + ), + ), + 'font3' => array( + 'font3-bold-normal' => array( + 'provider' => 'mock', + 'font-weight' => 'bold', + 'font-style' => 'normal', + 'font-display' => 'fallback', + 'font-stretch' => 'normal', + ), + ), + ); + } } diff --git a/phpunit/webfonts/wpPrintWebfonts-test.php b/phpunit/webfonts/wpPrintWebfonts-test.php new file mode 100644 index 00000000000000..ec2a4ea6dbb66f --- /dev/null +++ b/phpunit/webfonts/wpPrintWebfonts-test.php @@ -0,0 +1,56 @@ +assertSame( array(), wp_print_webfonts() ); + $this->assertNotInstanceOf( WP_Webfonts::class, $wp_webfonts ); + } + + /** + * @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 ); + } + + 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' ), + ), + ); + } +} diff --git a/phpunit/webfonts/wpWebfonts/doItem-test.php b/phpunit/webfonts/wpWebfonts/doItem-test.php new file mode 100644 index 00000000000000..7b4832d8c67029 --- /dev/null +++ b/phpunit/webfonts/wpWebfonts/doItem-test.php @@ -0,0 +1,370 @@ +wp_webfonts = new WP_Webfonts; + } + + public function test_should_return_false_when_provider_not_registered() { + $this->assertFalse( $this->wp_webfonts->do_item( 'provider_not_registered' ) ); + } + + /** + * @dataProvider data_provider_id + * + * @param string $provider_id Provider to mock. + */ + public function test_should_return_false_when_no_fonts_enqueued_for_provider( $provider_id ) { + $this->setup_provider_property_mock( $this->wp_webfonts, $provider_id ); + $this->assertFalse( $this->wp_webfonts->do_item( $provider_id ) ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_provider_id() { + return array( + 'mock' => array( 'mock' ), + 'local' => array( '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, 'mock', $font_handles ); + $actual = $this->property['WP_Webfonts::$providers']->getValue( $this->wp_webfonts ); + $this->assertSame( $expected, $actual ); + } + + /** + * Test the private method WP_Webfonts::get_enqueued_fonts_for_provider(). + * + * Why? This test validates the right fonts are returned for use within + * WP_Webfonts::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_Webfonts::$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. + $method = new ReflectionMethod( $this->wp_webfonts, 'get_enqueued_fonts_for_provider' ); + $method->setAccessible( true ); + + // Mock the WP_Webfonts::$property to set up the test. + $this->setup_provider_property_mock( $this->wp_webfonts, 'mock', $font_handles ); + + $actual = $method->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_Webfonts::get_font_properties_for_provider(). + * + * Why? This test validates the right font properties are returned for use within + * WP_Webfonts::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_Webfonts::$property to set up the test. + $this->setup_provider_property_mock( $this->wp_webfonts, '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 ) { + $provider_id = $provider['id']; + $provider_class = $provider['class']; + $this->setup_print_deps( $provider_id, $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_Webfonts::$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_Webfonts::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 ) { + $provider_id = $provider['id']; + + $this->setup_print_deps( $provider_id, $fonts ); + + // Test the method successfully processes the provider. + $this->expectOutputString( $expected['printed_output'] ); + $this->assertTrue( $this->wp_webfonts->do_item( $provider_id ), 'WP_Webfonts::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(); + + return array( + 'mock' => array( + 'provider' => array( + 'id' => 'mock', + 'class' => Mock_Provider::class, + ), + 'fonts' => $mock, + 'expected' => array( + 'set_webfonts' => array_merge( $mock['font1'], $mock['font2'], $mock['font3'] ), + 'printed_output' => 'font1-300-normal; font1-300-italic; font1-900-normal; font2-200-900-normal; font2-200-900-italic; font3-bold-normal', + ), + ), + 'local' => array( + 'provider' => array( + 'id' => 'local', + 'class' => WP_Webfonts_Provider_Local::class, + ), + 'fonts' => $local, + 'expected' => array( + 'set_webfonts' => array_merge( $local['merriweather'], $local['Source Serif Pro'] ), + 'printed_output' => << +@font-face{font-family:Merriweather;font-style:normal;font-weight:200 900;font-display:fallback;font-stretch:normal;src:local(Merriweather), url('https://example.com/assets/fonts/merriweather.ttf.woff2') format('woff2');}@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:300;font-display:fallback;font-stretch:normal;src:local("Source Serif Pro"), url('https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:900;font-display:fallback;font-stretch:normal;src:local("Source Serif Pro"), url('https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');} + + +CSS + , + ), + ), + ); + } + + /** + * 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_Webfonts::$to_do queue. + */ + public function test_should_not_print_when_to_do_queue_empty( array $provider, array $fonts, $expected, $to_do_queue ) { + $provider_id = $provider['id']; + + $this->setup_print_deps( $provider_id, $fonts, $to_do_queue ); + + // Test the method successfully processes the provider. + $this->expectOutputString( '' ); + $this->assertFalse( $this->wp_webfonts->do_item( $provider_id ), 'WP_Webfonts::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' => array( + 'id' => 'mock', + 'class' => Mock_Provider::class, + ), + 'fonts' => $mock, + 'expected' => array(), + 'to_do_queue' => array(), + ), + 'local provider when to_do queue is empty' => array( + 'provider' => array( + 'id' => 'local', + 'class' => WP_Webfonts_Provider_Local::class, + ), + 'fonts' => $local, + 'expected' => array(), + 'to_do_queue' => array(), + ), + 'fonts not in to_do queue' => array( + 'provider' => array( + 'id' => 'mock', + 'class' => Mock_Provider::class, + ), + 'fonts' => $mock, + 'expected' => array(), + 'to_do_queue' => array(), + ), + ); + + 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' ), + ), + ); + } + + /** + * Sets up the print dependencies. + * + * @param string $provider_id Provider ID. + * @param array $fonts Fonts to register and enqueue. + * @param array|null $to_do_queue Set the WP_Webfonts:$to_do queue. + */ + private function setup_print_deps( $provider_id, $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_id, $handles ); + + // Set up the `WP_Webfonts::$to_do` and `WP_Webfonts::$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 00000000000000..330c6ab7255a2b --- /dev/null +++ b/phpunit/webfonts/wpWebfonts/doItems-test.php @@ -0,0 +1,28 @@ +setup_mock_provider( $wp_webfonts ); + $this->assertFalse( $wp_webfonts->do ); + } + + public function test_do_items() { + $wp_webfonts = new WP_Webfonts(); + $this->setup_mock_provider( $wp_webfonts ); + $this->assertTrue( true ); + } +} diff --git a/phpunit/webfonts/wpWebfontsProviderLocal-test.php b/phpunit/webfonts/wpWebfontsProviderLocal-test.php index 9b60283fce9642..462c3e0612fb8a 100644 --- a/phpunit/webfonts/wpWebfontsProviderLocal-test.php +++ b/phpunit/webfonts/wpWebfontsProviderLocal-test.php @@ -7,7 +7,7 @@ */ /** - * @group webfonts + * @group webfonts */ class Tests_Webfonts_WpWebfontsProviderLocal extends WP_UnitTestCase { private $provider;