diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-font-collection.php b/lib/compat/wordpress-6.5/fonts/class-wp-font-collection.php index 654b23c98497f..c48d28a6f01fb 100644 --- a/lib/compat/wordpress-6.5/fonts/class-wp-font-collection.php +++ b/lib/compat/wordpress-6.5/fonts/class-wp-font-collection.php @@ -16,7 +16,7 @@ * * @since 6.5.0 */ - class WP_Font_Collection { + final class WP_Font_Collection { /** * The unique slug for the font collection. * @@ -27,58 +27,31 @@ class WP_Font_Collection { public $slug; /** - * The name of the font collection. - * - * @since 6.5.0 - * - * @var string - */ - public $name; - - /** - * Description of the font collection. - * - * @since 6.5.0 - * - * @var string - */ - public $description; - - /** - * Array of font families in the collection. - * - * @since 6.5.0 - * - * @var array - */ - public $font_families; - - /** - * Categories associated with the font collection. + * Font collection data. * * @since 6.5.0 * * @var array */ - public $categories; + private $data; /** - * Font collection json cache. + * Font collection JSON file path or url. * * @since 6.5.0 * - * @var array + * @var string */ - private static $collection_json_cache = array(); + private $src; /** * WP_Font_Collection constructor. * * @since 6.5.0 * - * @param string $slug Font collection slug. - * @param array $args { - * Optional. Font collection associative array of configuration options. + * @param string $slug Font collection slug. + * @param array|string $data_or_file { + * Font collection data array or a file path or url to a JSON file containing the font collection. * * @type string $name Name of the font collection. * @type string $description Description of the font collection. @@ -86,12 +59,15 @@ class WP_Font_Collection { * @type array $categories Array of categories for the fonts that are in the collection. * } */ - public function __construct( $slug, $args = array() ) { - $this->slug = sanitize_title( $slug ); - $this->name = isset( $args['name'] ) ? $args['name'] : __( 'Unnamed Font Collection', 'gutenberg' ); - $this->description = isset( $args['description'] ) ? $args['description'] : ''; - $this->font_families = isset( $args['font_families'] ) ? $args['font_families'] : array(); - $this->categories = isset( $args['categories'] ) ? $args['categories'] : array(); + public function __construct( $slug, $data_or_file ) { + $this->slug = sanitize_title( $slug ); + + // Data or json are lazy loaded and validated in get_data(). + if ( is_array( $data_or_file ) ) { + $this->data = $data_or_file; + } else { + $this->src = $data_or_file; + } if ( $this->slug !== $slug ) { _doing_it_wrong( @@ -101,87 +77,95 @@ public function __construct( $slug, $args = array() ) { '6.5.0' ); } + } - if ( empty( $args['font_families'] ) ) { - _doing_it_wrong( - __METHOD__, - /* translators: %s: Font collection slug. */ - sprintf( __( 'Font collection "%s" does not contain any font families.', 'gutenberg' ), $slug ), - '6.5.0' - ); + /** + * Retrieves the font collection data. + * + * @since 6.5.0 + * + * @return array|WP_Error An array containing the font collection data, or a WP_Error on failure. + */ + public function get_data() { + // If we have a JSON config, load it and cache the data if it's valid. + if ( $this->src && empty( $this->data ) ) { + $data = $this->load_from_json( $this->src ); + if ( is_wp_error( $data ) ) { + return $data; + } + + $this->data = $data; } - return true; + // Validate required properties are not empty. + $data = $this->validate_data( $this->data ); + if ( is_wp_error( $data ) ) { + return $data; + } + + // Set defaults for optional properties. + $data = wp_parse_args( + $data, + array( + 'description' => '', + 'categories' => array(), + ) + ); + + return $data; } /** - * Loads the font collection data from a json file path or url. + * Loads the font collection data from a JSON file path or url. * * @since 6.5.0 * - * @param string $file_or_url File path or url to a json file containing the font collection data. + * @param string $file_or_url File path or url to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ - public static function load_from_json( $file_or_url ) { + private function load_from_json( $file_or_url ) { $url = wp_http_validate_url( $file_or_url ); $file = file_exists( $file_or_url ) ? wp_normalize_path( realpath( $file_or_url ) ) : false; if ( ! $url && ! $file ) { - // translators: %s: File path or url to font collection json file. - $message = sprintf( __( 'Font collection JSON file "%s" is invalid or does not exist.', 'gutenberg' ), $file_or_url ); + // translators: %s: File path or url to font collection JSON file. + $message = __( 'Font collection JSON file is invalid or does not exist.', 'gutenberg' ); _doing_it_wrong( __METHOD__, $message, '6.5.0' ); return new WP_Error( 'font_collection_json_missing', $message ); } - return $url ? self::load_from_url( $url ) : self::load_from_file( $file ); + return $url ? $this->load_from_url( $url ) : $this->load_from_file( $file ); } /** - * Loads the font collection data from a json file path. + * Loads the font collection data from a JSON file path. * * @since 6.5.0 * - * @param string $file File path to a json file containing the font collection data. + * @param string $file File path to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ - private static function load_from_file( $file ) { - if ( array_key_exists( $file, static::$collection_json_cache ) ) { - return static::$collection_json_cache[ $file ]; - } - + private function load_from_file( $file ) { $data = wp_json_file_decode( $file, array( 'associative' => true ) ); if ( empty( $data ) ) { return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection JSON file contents.', 'gutenberg' ) ); } - if ( empty( $data['slug'] ) ) { - // translators: %s: Font collection JSON URL. - $message = sprintf( __( 'Font collection JSON file "%s" requires a slug.', 'gutenberg' ), $file ); - _doing_it_wrong( __METHOD__, $message, '6.5.0' ); - return new WP_Error( 'font_collection_invalid_json', $message ); - } - - static::$collection_json_cache[ $file ] = $data; - return $data; } /** - * Loads the font collection data from a json file url. + * Loads the font collection data from a JSON file url. * * @since 6.5.0 * - * @param string $url Url to a json file containing the font collection data. + * @param string $url Url to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ - private static function load_from_url( $url ) { - if ( array_key_exists( $url, static::$collection_json_cache ) ) { - return static::$collection_json_cache[ $url ]; - } - + private function load_from_url( $url ) { // Limit key to 167 characters to avoid failure in the case of a long url. $transient_key = substr( 'wp_font_collection_url_' . $url, 0, 167 ); $data = get_site_transient( $transient_key ); @@ -198,18 +182,40 @@ private static function load_from_url( $url ) { return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection data from the REST response JSON.', 'gutenberg' ) ); } - if ( empty( $data['slug'] ) ) { - // translators: %s: Font collection JSON URL. - $message = sprintf( __( 'Font collection JSON file "%s" requires a slug.', 'gutenberg' ), $url ); - _doing_it_wrong( __METHOD__, $message, '6.5.0' ); - return new WP_Error( 'font_collection_invalid_json', $message ); + // Make sure the data is valid before caching it. + $data = $this->validate_data( $data ); + if ( is_wp_error( $data ) ) { + return $data; } set_site_transient( $transient_key, $data, DAY_IN_SECONDS ); } - static::$collection_json_cache[ $url ] = $data; + return $data; + } + /** + * Validates the font collection configuration. + * + * @since 6.5.0 + * + * @param array $data Font collection configuration. + * @return array|WP_Error Array of data if valid, otherwise a WP_Error instance. + */ + private function validate_data( $data ) { + $required_properties = array( 'name', 'font_families' ); + foreach ( $required_properties as $property ) { + if ( empty( $data[ $property ] ) ) { + $message = sprintf( + // translators: 1: Font collection slug, 2: Missing property name. + __( 'Font collection "%1$s" has missing or empty property: "%2$s."', 'gutenberg' ), + $this->slug, + $property + ); + _doing_it_wrong( __METHOD__, $message, '6.5.0' ); + return new WP_Error( 'font_collection_missing_property', $message ); + } + } return $data; } } diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-font-library.php b/lib/compat/wordpress-6.5/fonts/class-wp-font-library.php index bc119d3570234..a33aead5571ec 100644 --- a/lib/compat/wordpress-6.5/fonts/class-wp-font-library.php +++ b/lib/compat/wordpress-6.5/fonts/class-wp-font-library.php @@ -55,13 +55,14 @@ public static function get_expected_font_mime_types_per_php_version( $php_versio * * @since 6.5.0 * - * @param string $slug Font collection slug. - * @param array $args Font collection config options. - * See {@see wp_register_font_collection()} for the supported fields. + * @param string $slug Font collection slug. + * @param array $data_or_file Font collection data array or a file path or url to a JSON file + * containing the font collection. + * See {@see wp_register_font_collection()} for the supported fields. * @return WP_Font_Collection|WP_Error A font collection if registration was successful, else WP_Error. */ - public static function register_font_collection( $slug, $args = array() ) { - $new_collection = new WP_Font_Collection( $slug, $args ); + public static function register_font_collection( $slug, $data_or_file ) { + $new_collection = new WP_Font_Collection( $slug, $data_or_file ); if ( self::is_collection_registered( $new_collection->slug ) ) { $error_message = sprintf( @@ -80,23 +81,6 @@ public static function register_font_collection( $slug, $args = array() ) { return $new_collection; } - /** - * Register a new font collection from a json file. - * - * @since 6.5.0 - * - * @param string $file_or_url File path or URL to a JSON file containing the font collection data. - * @return WP_Font_Collection|WP_Error A font collection if registration was successful, else WP_Error. - */ - public static function register_font_collection_from_json( $file_or_url ) { - $args = WP_Font_Collection::load_from_json( $file_or_url ); - if ( is_wp_error( $args ) ) { - return $args; - } - - return self::register_font_collection( $args['slug'], $args ); - } - /** * Unregisters a previously registered font collection. * diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php index 6772a8c3b4408..5df75fff278be 100644 --- a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php +++ b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php @@ -94,8 +94,10 @@ public function get_items( $request ) { $items = array(); foreach ( $collections_page as $collection ) { $item = $this->prepare_item_for_response( $collection, $request ); + + // If there's an error loading a collection, skip it and continue loading valid collections. if ( is_wp_error( $item ) ) { - return $item; + continue; } $item = $this->prepare_response_for_collection( $item ); $items[] = $item; @@ -170,10 +172,23 @@ public function prepare_item_for_response( $collection, $request ) { $fields = $this->get_fields_for_response( $request ); $item = array(); - $config_fields = array( 'slug', 'name', 'description', 'font_families', 'categories' ); - foreach ( $config_fields as $field ) { - if ( in_array( $field, $fields, true ) ) { - $item[ $field ] = $collection->$field; + if ( rest_is_field_included( 'slug', $fields ) ) { + $item['slug'] = $collection->slug; + } + + // If any data fields are requested, get the collection data. + $data_fields = array( 'name', 'description', 'font_families', 'categories' ); + if ( ! empty( array_intersect( $fields, $data_fields ) ) ) { + $collection_data = $collection->get_data(); + if ( is_wp_error( $collection_data ) ) { + $collection_data->add_data( array( 'status' => 500 ) ); + return $collection_data; + } + + foreach ( $data_fields as $field ) { + if ( rest_is_field_included( $field, $fields ) ) { + $item[ $field ] = $collection_data[ $field ]; + } } } diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php index 01ffce1eea483..bb6e1277511d2 100644 --- a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php +++ b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php @@ -151,7 +151,7 @@ public function get_item_permissions_check( $request ) { * * @param string $value Encoded JSON string of font face settings. * @param WP_REST_Request $request Request object. - * @return false|WP_Error True if the settings are valid, otherwise a WP_Error object. + * @return true|WP_Error True if the settings are valid, otherwise a WP_Error object. */ public function validate_create_font_face_settings( $value, $request ) { $settings = json_decode( $value, true ); diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php index 8d178a2848a35..cbc80cd244d14 100644 --- a/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php +++ b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php @@ -77,7 +77,7 @@ public function get_item_permissions_check( $request ) { * * @param string $value Encoded JSON string of font family settings. * @param WP_REST_Request $request Request object. - * @return false|WP_Error True if the settings are valid, otherwise a WP_Error object. + * @return true|WP_Error True if the settings are valid, otherwise a WP_Error object. */ public function validate_font_family_settings( $value, $request ) { $settings = json_decode( $value, true ); diff --git a/lib/compat/wordpress-6.5/fonts/fonts.php b/lib/compat/wordpress-6.5/fonts/fonts.php index bcac6f627fe67..339ab32646d8b 100644 --- a/lib/compat/wordpress-6.5/fonts/fonts.php +++ b/lib/compat/wordpress-6.5/fonts/fonts.php @@ -97,9 +97,10 @@ function gutenberg_init_font_library_routes() { * * @since 6.5.0 * - * @param string $slug_or_file Font collection slug or path/url to a JSON file defining the font collection. - * @param string[] $args { - * Optional. Font collection associative array of configuration options. + * @param string $slug Font collection slug or path/url to a JSON file defining the font collection. + * @param array|string $data_or_file { + * Font collection associative array of data, or a file path or url to a JSON + * file containing the font collection. * * @type string $name Name of the font collection. * @type string $description Description of the font collection. @@ -109,22 +110,8 @@ function gutenberg_init_font_library_routes() { * @return WP_Font_Collection|WP_Error A font collection is it was registered * successfully, else WP_Error. */ - function wp_register_font_collection( $slug, $args = array() ) { - return WP_Font_Library::register_font_collection( $slug, $args ); - } -} - -if ( ! function_exists( 'wp_register_font_collection_from_json' ) ) { - /** - * Registers a new Font Collection from a json file in the Font Library. - * - * @since 6.5.0 - * - * @param string $file_or_url File path or URL to a JSON file containing the font collection data. - * @return WP_Font_Collection|WP_Error A font collection if registration was successful, else WP_Error. - */ - function wp_register_font_collection_from_json( $file_or_url ) { - return WP_Font_Library::register_font_collection_from_json( $file_or_url ); + function wp_register_font_collection( $slug, $data_or_file ) { + return WP_Font_Library::register_font_collection( $slug, $data_or_file ); } } @@ -141,8 +128,11 @@ function wp_unregister_font_collection( $collection_id ) { } } -// TODO: update to production font collection URL. -wp_register_font_collection_from_json( 'https://raw.githubusercontent.com/WordPress/google-fonts-to-wordpress-collection/01aa57731575bd13f9db8d86ab80a2d74e28a1ac/releases/gutenberg-17.6/collections/google-fonts-with-preview.json' ); +function gutenberg_register_font_collections() { + // TODO: update to production font collection URL. + wp_register_font_collection( 'google-fonts', 'https://raw.githubusercontent.com/WordPress/google-fonts-to-wordpress-collection/01aa57731575bd13f9db8d86ab80a2d74e28a1ac/releases/gutenberg-17.6/collections/google-fonts-with-preview.json' ); +} +add_action( 'init', 'gutenberg_register_font_collections' ); // @core-merge: This code should probably go into Core's src/wp-includes/functions.php. if ( ! function_exists( 'wp_get_font_dir' ) ) { diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php index 4021c9eeedf3f..53a1ce0a5482b 100644 --- a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php +++ b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php @@ -1,6 +1,6 @@ $collection->slug, - 'name' => $collection->name, - 'description' => $collection->description, - 'font_families' => $collection->font_families, - 'categories' => $collection->categories, - ); - $this->assertSame( $expected_data, $data ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_assign_properties_from_php_config() { - return array( - 'with font_families and categories' => array( - 'slug' => 'my-collection', - 'config' => array( - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( 'mock' ), - 'categories' => array( 'mock' ), - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( 'mock' ), - 'categories' => array( 'mock' ), - ), - ), - 'with font_families without categories' => array( - 'slug' => 'my-collection', - 'config' => array( - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( 'mock' ), - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( 'mock' ), - 'categories' => array(), - ), - ), - ); - } - - public function test_should_do_it_wrong_missing_font_families() { + public function test_should_do_it_wrong_with_invalid_slug() { $this->setExpectedIncorrectUsage( 'WP_Font_Collection::__construct' ); - new WP_Font_Collection( 'my-collection' ); - } - public function test_should_do_it_wrong_invalid_slug() { - $this->setExpectedIncorrectUsage( 'WP_Font_Collection::__construct' ); - new WP_Font_Collection( 'slug with spaces', array( 'font_families' => array( 'mock' ) ) ); + $collection = new WP_Font_Collection( 'slug with spaces', array() ); + + $this->assertSame( 'slug-with-spaces', $collection->slug, 'Slug is not sanitized.' ); } } diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getData.php b/phpunit/tests/fonts/font-library/wpFontCollection/getData.php new file mode 100644 index 0000000000000..42715907de9c0 --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontCollection/getData.php @@ -0,0 +1,277 @@ +get_data(); + + $this->assertSame( $slug, $collection->slug, 'The slug should match.' ); + $this->assertSame( $expected_data, $data, 'The collection data should match.' ); + } + + /** + * @dataProvider data_create_font_collection + * + * @param string $slug Font collection slug. + * @param array $config Font collection config. + * @param array $expected_data Expected collection data. + */ + public function test_should_get_data_from_json_file( $slug, $config, $expected_data ) { + $mock_file = wp_tempnam( 'my-collection-data-' ); + file_put_contents( $mock_file, wp_json_encode( $config ) ); + + $collection = new WP_Font_Collection( $slug, $mock_file ); + $data = $collection->get_data(); + + $this->assertSame( $slug, $collection->slug, 'The slug should match.' ); + $this->assertSame( $expected_data, $data, 'The collection data should match.' ); + } + + /** + * @dataProvider data_create_font_collection + * + * @param string $slug Font collection slug. + * @param array $config Font collection config. + * @param array $expected_data Expected collection data. + */ + public function test_should_get_data_from_json_url( $slug, $config, $expected_data ) { + add_filter( 'pre_http_request', array( $this, 'mock_request' ), 10, 3 ); + + self::$mock_collection_data = $config; + $collection = new WP_Font_Collection( $slug, 'https://localhost/fonts/mock-font-collection.json' ); + $data = $collection->get_data(); + + remove_filter( 'pre_http_request', array( $this, 'mock_request' ) ); + + $this->assertSame( $slug, $collection->slug, 'The slug should match.' ); + $this->assertSame( $expected_data, $data, 'The collection data should match.' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_create_font_collection() { + return array( + + 'font collection with required data' => array( + 'slug' => 'my-collection', + 'config' => array( + 'name' => 'My Collection', + 'font_families' => array( 'mock' ), + ), + 'expected_data' => array( + 'description' => '', + 'categories' => array(), + 'name' => 'My Collection', + 'font_families' => array( 'mock' ), + ), + ), + + 'font collection with all data' => array( + 'slug' => 'my-collection', + 'config' => array( + 'name' => 'My Collection', + 'description' => 'My collection description', + 'font_families' => array( 'mock' ), + 'categories' => array( 'mock' ), + ), + 'expected_data' => array( + 'description' => 'My collection description', + 'categories' => array( 'mock' ), + 'name' => 'My Collection', + 'font_families' => array( 'mock' ), + ), + ), + + ); + } + + /** + * @dataProvider data_should_error_when_missing_properties + * + * @param array $config Font collection config. + */ + public function test_should_error_when_missing_properties( $config ) { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::validate_data' ); + + $collection = new WP_Font_Collection( 'my-collection', $config ); + $data = $collection->get_data(); + + $this->assertWPError( $data, 'Error is not returned when property is missing or invalid.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_missing_property', + 'Incorrect error code when property is missing or invalid.' + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_should_error_when_missing_properties() { + return array( + 'missing name' => array( + 'config' => array( + 'font_families' => array( 'mock' ), + ), + ), + 'empty name' => array( + 'config' => array( + 'name' => '', + 'font_families' => array( 'mock' ), + ), + ), + 'missing font_families' => array( + 'config' => array( + 'name' => 'My Collection', + ), + ), + 'empty font_families' => array( + 'config' => array( + 'name' => 'My Collection', + 'font_families' => array(), + ), + ), + ); + } + + public function test_should_error_with_invalid_json_file_path() { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); + + $collection = new WP_Font_Collection( 'my-collection', 'non-existing.json' ); + $data = $collection->get_data(); + + $this->assertWPError( $data, 'Error is not returned when invalid file path is provided.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_json_missing', + 'Incorrect error code when invalid file path is provided.' + ); + } + + public function test_should_error_with_invalid_json_from_file() { + $mock_file = wp_tempnam( 'my-collection-data-' ); + file_put_contents( $mock_file, 'invalid-json' ); + + $collection = new WP_Font_Collection( 'my-collection', $mock_file ); + + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Testing error response returned by `load_from_json`, not the underlying error from `wp_json_file_decode`. + $data = @$collection->get_data(); + + $this->assertWPError( $data, 'Error is not returned with invalid json file contents.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_decode_error', + 'Incorrect error code with invalid json file contents.' + ); + } + + public function test_should_error_with_invalid_url() { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); + + $collection = new WP_Font_Collection( 'my-collection', 'not-a-url' ); + $data = $collection->get_data(); + + $this->assertWPError( $data, 'Error is not returned when invalid url is provided.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_json_missing', + 'Incorrect error code when invalid url is provided.' + ); + } + + public function test_should_error_with_unsuccessful_response_status() { + add_filter( 'pre_http_request', array( $this, 'mock_request_unsuccessful_response' ), 10, 3 ); + + $collection = new WP_Font_Collection( 'my-collection', 'https://localhost/fonts/missing-collection.json' ); + $data = $collection->get_data(); + + remove_filter( 'pre_http_request', array( $this, 'mock_request_unsuccessful_response' ) ); + + $this->assertWPError( $data, 'Error is not returned when response is unsuccessful.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_request_error', + 'Incorrect error code when response is unsuccussful.' + ); + } + + public function test_should_error_with_invalid_json_from_url() { + add_filter( 'pre_http_request', array( $this, 'mock_request_invalid_json' ), 10, 3 ); + + $collection = new WP_Font_Collection( 'my-collection', 'https://localhost/fonts/invalid-collection.json' ); + $data = $collection->get_data(); + + remove_filter( 'pre_http_request', array( $this, 'mock_request_invalid_json' ) ); + + $this->assertWPError( $data, 'Error is not returned when response is invalid json.' ); + $this->assertSame( + $data->get_error_code(), + 'font_collection_decode_error', + 'Incorrect error code when response is invalid json.' + ); + } + + public function mock_request( $preempt, $args, $url ) { + if ( 'https://localhost/fonts/mock-font-collection.json' !== $url ) { + return false; + } + + return array( + 'body' => wp_json_encode( self::$mock_collection_data ), + 'response' => array( + 'code' => 200, + ), + ); + } + + public function mock_request_unsuccessful_response( $preempt, $args, $url ) { + if ( 'https://localhost/fonts/missing-collection.json' !== $url ) { + return false; + } + + return array( + 'body' => '', + 'response' => array( + 'code' => 404, + ), + ); + } + + public function mock_request_invalid_json( $preempt, $args, $url ) { + if ( 'https://localhost/fonts/invalid-collection.json' !== $url ) { + return false; + } + + return array( + 'body' => 'invalid', + 'response' => array( + 'code' => 200, + ), + ); + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/loadFromJson.php b/phpunit/tests/fonts/font-library/wpFontCollection/loadFromJson.php deleted file mode 100644 index cf9ba7faa93f1..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontCollection/loadFromJson.php +++ /dev/null @@ -1,159 +0,0 @@ - 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description.', - 'font_families' => array( 'mock' ), - 'categories' => array( 'mock' ), - ); - - public function test_should_load_json_from_file() { - $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, wp_json_encode( self::$mock_collection_data ) ); - - $config = WP_Font_Collection::load_from_json( $mock_file ); - $this->assertSame( self::$mock_collection_data, $config ); - } - - public function test_should_load_json_from_url() { - add_filter( 'pre_http_request', array( $this, 'mock_request' ), 10, 3 ); - - $config = WP_Font_Collection::load_from_json( 'https://localhost/fonts/mock-font-collection.json' ); - remove_filter( 'pre_http_request', array( $this, 'mock_request' ) ); - - $this->assertSame( self::$mock_collection_data, $config ); - } - - public function test_should_error_with_invalid_file_path() { - $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); - - $config = WP_Font_Collection::load_from_json( 'non-existing.json' ); - $this->assertWPError( $config, 'font_collection_json_missing' ); - } - - public function test_should_error_with_invalid_url() { - $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); - - $config = WP_Font_Collection::load_from_json( 'not-a-url' ); - $this->assertWPError( $config, 'font_collection_json_missing' ); - } - - public function test_should_error_with_invalid_url_response() { - add_filter( 'pre_http_request', array( $this, 'mock_request_invalid_response' ), 10, 3 ); - - $config = WP_Font_Collection::load_from_json( 'https://localhost/fonts/missing-collection.json' ); - remove_filter( 'pre_http_request', array( $this, 'mock_request_invalid_response' ) ); - - $this->assertWPError( $config, 'font_collection_json_missing' ); - } - - public function test_should_error_with_invalid_json_from_file() { - $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, 'invalid-json' ); - - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Testing error response returned by `load_from_json`, not the underlying error from `wp_json_file_decode`. - $config = @WP_Font_Collection::load_from_json( $mock_file ); - $this->assertWPError( $config, 'font_collection_decode_error' ); - } - - public function test_should_error_with_invalid_json_from_url() { - add_filter( 'pre_http_request', array( $this, 'mock_request_invalid_json' ), 10, 3 ); - - $config = WP_Font_Collection::load_from_json( 'https://localhost/fonts/invalid-collection.json' ); - remove_filter( 'pre_http_request', array( $this, 'mock_request_invalid_json' ) ); - - $this->assertWPError( $config, 'font_collection_decode_error' ); - } - - public function test_should_error_with_json_from_file_missing_slug() { - $mock_file = wp_tempnam( 'my-collection-data-' ); - $mock_collection_data = self::$mock_collection_data; - unset( $mock_collection_data['slug'] ); - file_put_contents( $mock_file, wp_json_encode( $mock_collection_data ) ); - $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_file' ); - - $config = WP_Font_Collection::load_from_json( $mock_file ); - $this->assertWPError( $config, 'font_collection_invalid_json' ); - } - - public function test_should_error_with_json_from_url_missing_slug() { - add_filter( 'pre_http_request', array( $this, 'mock_request_missing_slug' ), 10, 3 ); - $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_url' ); - - $config = WP_Font_Collection::load_from_json( 'https://localhost/fonts/missing-slug.json' ); - remove_filter( 'pre_http_request', array( $this, 'mock_request_missing_slug' ) ); - - $this->assertWPError( $config, 'font_collection_invalid_json' ); - } - - public function mock_request( $preempt, $args, $url ) { - // if the URL is not the URL you want to mock, return false. - if ( 'https://localhost/fonts/mock-font-collection.json' !== $url ) { - return false; - } - - return array( - 'body' => wp_json_encode( self::$mock_collection_data ), - 'response' => array( - 'code' => 200, - ), - ); - } - - public function mock_request_missing_slug( $preempt, $args, $url ) { - // if the URL is not the URL you want to mock, return false. - if ( 'https://localhost/fonts/missing-slug.json' !== $url ) { - return false; - } - - $mock_collection_data = self::$mock_collection_data; - unset( $mock_collection_data['slug'] ); - - return array( - 'body' => wp_json_encode( $mock_collection_data ), - 'response' => array( - 'code' => 200, - ), - ); - } - - public function mock_request_invalid_response( $preempt, $args, $url ) { - // if the URL is not the URL you want to mock, return false. - if ( 'https://localhost/fonts/missing-collection.json' !== $url ) { - return false; - } - - return array( - 'body' => '', - 'response' => array( - 'code' => 404, - ), - ); - } - - public function mock_request_invalid_json( $preempt, $args, $url ) { - // if the URL is not the URL you want to mock, return false. - if ( 'https://localhost/fonts/invalid-collection.json' !== $url ) { - return false; - } - - return array( - 'body' => 'invalid', - 'response' => array( - 'code' => 200, - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php index 99cd506f60a17..1ba9ab1d32fb4 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php @@ -20,11 +20,6 @@ public function test_should_register_font_collection() { $this->assertInstanceOf( 'WP_Font_Collection', $collection ); } - public function test_should_set_a_default_name() { - $collection = WP_Font_Library::register_font_collection( 'my-collection', array( 'font_families' => array( 'mock' ) ) ); - $this->assertSame( 'Unnamed Font Collection', $collection->name ); - } - public function test_should_return_error_if_slug_is_repeated() { // Register first collection. $collection1 = WP_Font_Library::register_font_collection( 'my-collection-1', array( 'font_families' => array( 'mock' ) ) ); diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php index 823066c9478af..60f50e503fdbe 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php @@ -30,9 +30,9 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { ) ); $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, '{"slug": "mock-col-slug", "font_families": [ "mock" ], "categories": [ "mock" ] }' ); + file_put_contents( $mock_file, '{"name": "Mock Collection", "font_families": [ "mock" ], "categories": [ "mock" ] }' ); - wp_register_font_collection_from_json( $mock_file ); + wp_register_font_collection( 'mock-col-slug', $mock_file ); } public static function wpTearDownAfterClass() { @@ -65,6 +65,25 @@ public function test_get_items() { $this->assertSame( 200, $response->get_status() ); } + /** + * @covers WP_REST_Font_Collections_Controller::get_items + */ + public function test_get_items_should_only_return_valid_collections() { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); + + wp_set_current_user( self::$admin_id ); + wp_register_font_collection( 'invalid-collection', 'invalid-collection-file' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections' ); + $response = rest_get_server()->dispatch( $request ); + $content = $response->get_data(); + + wp_unregister_font_collection( 'invalid-collection' ); + + $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' ); + $this->assertCount( 1, $content, 'The response should only contain valid collections.' ); + } + /** * @covers WP_REST_Font_Collections_Controller::get_item */ @@ -72,7 +91,7 @@ public function test_get_item() { wp_set_current_user( self::$admin_id ); $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/mock-col-slug' ); $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 200, $response->get_status(), 'Response code is not 200' ); + $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' ); $response_data = $response->get_data(); $this->assertArrayHasKey( 'name', $response_data, 'Response data does not have the name key.' ); @@ -99,6 +118,24 @@ public function test_get_item_invalid_slug() { $this->assertErrorResponse( 'font_collection_not_found', $response, 404 ); } + /** + * @covers WP_REST_Font_Collections_Controller::get_item + */ + public function test_get_item_invalid_collection() { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' ); + + wp_set_current_user( self::$admin_id ); + $slug = 'invalid-collection'; + wp_register_font_collection( $slug, 'invalid-collection-file' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/' . $slug ); + $response = rest_get_server()->dispatch( $request ); + + wp_unregister_font_collection( $slug ); + + $this->assertErrorResponse( 'font_collection_json_missing', $response, 500, 'When the collection json file is invalid, the response should return an error for "font_collection_json_missing" with 500 status.' ); + } + /** * @covers WP_REST_Font_Collections_Controller::get_item */ @@ -107,11 +144,11 @@ public function test_get_item_invalid_id_permission() { wp_set_current_user( 0 ); $response = rest_get_server()->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'Response code should be 401 for non-authenticated users.' ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response status should be 401 for non-authenticated users.' ); wp_set_current_user( self::$editor_id ); $response = rest_get_server()->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'Response code should be 403 for users without the right permissions.' ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response status should be 403 for users without the right permissions.' ); } /**