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
new file mode 100644
index 00000000000000..019cf0f3f0b29d
--- /dev/null
+++ b/lib/compat/wordpress-6.5/fonts/class-wp-font-collection.php
@@ -0,0 +1,263 @@
+slug = sanitize_title( $slug );
+
+ if ( is_array( $data_or_file ) ) {
+ $this->data = $this->sanitize_and_validate_data( $data_or_file );
+ } else {
+ // JSON data is lazy loaded by ::get_data().
+ $this->src = $data_or_file;
+ }
+
+ if ( $this->slug !== $slug ) {
+ _doing_it_wrong(
+ __METHOD__,
+ /* translators: %s: Font collection slug. */
+ sprintf( __( 'Font collection slug "%s" is not valid. Slugs must use only alphanumeric characters, dashes, and underscores.', '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 the collection uses JSON data, load it and cache the data/error.
+ if ( $this->src && empty( $this->data ) ) {
+ $this->data = $this->load_from_json( $this->src );
+ }
+
+ if ( is_wp_error( $this->data ) ) {
+ return $this->data;
+ }
+
+ // Set defaults for optional properties.
+ $defaults = array(
+ 'description' => '',
+ 'categories' => array(),
+ );
+ return wp_parse_args( $this->data, $defaults );
+ }
+
+ /**
+ * Loads font collection data from a JSON file or URL.
+ *
+ * @since 6.5.0
+ *
+ * @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.
+ */
+ 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 = __( '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 ? $this->load_from_url( $url ) : $this->load_from_file( $file );
+ }
+
+ /**
+ * 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.
+ * @return array|WP_Error An array containing the font collection data on success,
+ * else an instance of WP_Error on failure.
+ */
+ 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' ) );
+ }
+
+ return $this->sanitize_and_validate_data( $data );
+ }
+
+ /**
+ * 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.
+ * @return array|WP_Error An array containing the font collection data on success,
+ * else an instance of WP_Error on failure.
+ */
+ 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 );
+
+ if ( false === $data ) {
+ $response = wp_safe_remote_get( $url );
+ if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ // translators: %s: Font collection URL.
+ return new WP_Error( 'font_collection_request_error', sprintf( __( 'Error fetching the font collection data from "%s".', 'gutenberg' ), $url ) );
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+ if ( empty( $data ) ) {
+ return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection data from the REST response JSON.', 'gutenberg' ) );
+ }
+
+ // Make sure the data is valid before storing it in a transient.
+ $data = $this->sanitize_and_validate_data( $data );
+ if ( is_wp_error( $data ) ) {
+ return $data;
+ }
+
+ set_site_transient( $transient_key, $data, DAY_IN_SECONDS );
+ }
+
+ return $data;
+ }
+
+ /**
+ * Sanitizes and validates the font collection data.
+ *
+ * @since 6.5.0
+ *
+ * @param array $data Font collection data to sanitize and validate.
+ * @return array|WP_Error Sanitized data if valid, otherwise a WP_Error instance.
+ */
+ private function sanitize_and_validate_data( $data ) {
+ $schema = self::get_sanitization_schema();
+ $data = WP_Font_Utils::sanitize_from_schema( $data, $schema );
+
+ $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;
+ }
+
+ /**
+ * Retrieves the font collection sanitization schema.
+ *
+ * @since 6.5.0
+ *
+ * @return array Font collection sanitization schema.
+ */
+ private static function get_sanitization_schema() {
+ return array(
+ 'name' => 'sanitize_text_field',
+ 'description' => 'sanitize_text_field',
+ 'font_families' => array(
+ array(
+ 'font_family_settings' => array(
+ 'name' => 'sanitize_text_field',
+ 'slug' => 'sanitize_title',
+ 'fontFamily' => 'sanitize_text_field',
+ 'preview' => 'sanitize_url',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'sanitize_text_field',
+ 'fontStyle' => 'sanitize_text_field',
+ 'fontWeight' => 'sanitize_text_field',
+ 'src' => function ( $value ) {
+ return is_array( $value )
+ ? array_map( 'sanitize_text_field', $value )
+ : sanitize_text_field( $value );
+ },
+ 'preview' => 'sanitize_url',
+ 'fontDisplay' => 'sanitize_text_field',
+ 'fontStretch' => 'sanitize_text_field',
+ 'ascentOverride' => 'sanitize_text_field',
+ 'descentOverride' => 'sanitize_text_field',
+ 'fontVariant' => 'sanitize_text_field',
+ 'fontFeatureSettings' => 'sanitize_text_field',
+ 'fontVariationSettings' => 'sanitize_text_field',
+ 'lineGapOverride' => 'sanitize_text_field',
+ 'sizeAdjust' => 'sanitize_text_field',
+ 'unicodeRange' => 'sanitize_text_field',
+ ),
+ ),
+ ),
+ 'categories' => array( 'sanitize_title' ),
+ ),
+ ),
+ 'categories' => array(
+ array(
+ 'name' => 'sanitize_text_field',
+ 'slug' => 'sanitize_title',
+ ),
+ ),
+ );
+ }
+ }
+}
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
new file mode 100644
index 00000000000000..9bf90ca1d9dd8a
--- /dev/null
+++ b/lib/compat/wordpress-6.5/fonts/class-wp-font-library.php
@@ -0,0 +1,148 @@
+is_collection_registered( $new_collection->slug ) ) {
+ $error_message = sprintf(
+ /* translators: %s: Font collection slug. */
+ __( 'Font collection with slug: "%s" is already registered.', 'gutenberg' ),
+ $new_collection->slug
+ );
+ _doing_it_wrong(
+ __METHOD__,
+ $error_message,
+ '6.5.0'
+ );
+ return new WP_Error( 'font_collection_registration_error', $error_message );
+ }
+ $this->collections[ $new_collection->slug ] = $new_collection;
+ return $new_collection;
+ }
+
+ /**
+ * Unregisters a previously registered font collection.
+ *
+ * @since 6.5.0
+ *
+ * @param string $slug Font collection slug.
+ * @return bool True if the font collection was unregistered successfully and false otherwise.
+ */
+ public function unregister_font_collection( $slug ) {
+ if ( ! $this->is_collection_registered( $slug ) ) {
+ _doing_it_wrong(
+ __METHOD__,
+ /* translators: %s: Font collection slug. */
+ sprintf( __( 'Font collection "%s" not found.' ), $slug ),
+ '6.5.0'
+ );
+ return false;
+ }
+ unset( $this->collections[ $slug ] );
+ return true;
+ }
+
+ /**
+ * Checks if a font collection is registered.
+ *
+ * @since 6.5.0
+ *
+ * @param string $slug Font collection slug.
+ * @return bool True if the font collection is registered and false otherwise.
+ */
+ private function is_collection_registered( $slug ) {
+ return array_key_exists( $slug, $this->collections );
+ }
+
+ /**
+ * Gets all the font collections available.
+ *
+ * @since 6.5.0
+ *
+ * @return array List of font collections.
+ */
+ public function get_font_collections() {
+ return $this->collections;
+ }
+
+ /**
+ * Gets a font collection.
+ *
+ * @since 6.5.0
+ *
+ * @param string $slug Font collection slug.
+ * @return WP_Font_Collection|WP_Error Font collection object,
+ * or WP_Error object if the font collection doesn't exist.
+ */
+ public function get_font_collection( $slug ) {
+ if ( array_key_exists( $slug, $this->collections ) ) {
+ return $this->collections[ $slug ];
+ }
+ return new WP_Error( 'font_collection_not_found', 'Font collection not found.' );
+ }
+
+ /**
+ * Utility method to retrieve the main instance of the class.
+ *
+ * The instance will be created if it does not exist yet.
+ *
+ * @since 6.5.0
+ *
+ * @return WP_Font_Library The main instance.
+ */
+ public static function get_instance() {
+ if ( null === self::$instance ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+ }
+}
diff --git a/lib/compat/wordpress-6.5/fonts/class-wp-font-utils.php b/lib/compat/wordpress-6.5/fonts/class-wp-font-utils.php
new file mode 100644
index 00000000000000..b75ce0ec41cf69
--- /dev/null
+++ b/lib/compat/wordpress-6.5/fonts/class-wp-font-utils.php
@@ -0,0 +1,237 @@
+ '',
+ 'fontStyle' => 'normal',
+ 'fontWeight' => '400',
+ 'fontStretch' => '100%',
+ 'unicodeRange' => 'U+0-10FFFF',
+ );
+ $settings = wp_parse_args( $settings, $defaults );
+
+ $font_family = mb_strtolower( $settings['fontFamily'] );
+ $font_style = strtolower( $settings['fontStyle'] );
+ $font_weight = strtolower( $settings['fontWeight'] );
+ $font_stretch = strtolower( $settings['fontStretch'] );
+ $unicode_range = strtoupper( $settings['unicodeRange'] );
+
+ // Convert weight keywords to numeric strings.
+ $font_weight = str_replace( array( 'normal', 'bold' ), array( '400', '700' ), $font_weight );
+
+ // Convert stretch keywords to numeric strings.
+ $font_stretch_map = array(
+ 'ultra-condensed' => '50%',
+ 'extra-condensed' => '62.5%',
+ 'condensed' => '75%',
+ 'semi-condensed' => '87.5%',
+ 'normal' => '100%',
+ 'semi-expanded' => '112.5%',
+ 'expanded' => '125%',
+ 'extra-expanded' => '150%',
+ 'ultra-expanded' => '200%',
+ );
+ $font_stretch = str_replace( array_keys( $font_stretch_map ), array_values( $font_stretch_map ), $font_stretch );
+
+ $slug_elements = array( $font_family, $font_style, $font_weight, $font_stretch, $unicode_range );
+
+ $slug_elements = array_map(
+ function ( $elem ) {
+ // Remove quotes to normalize font-family names, and ';' to use as a separator.
+ $elem = trim( str_replace( array( '"', "'", ';' ), '', $elem ) );
+
+ // Normalize comma separated lists by removing whitespace in between items,
+ // but keep whitespace within items (e.g. "Open Sans" and "OpenSans" are different fonts).
+ // CSS spec for whitespace includes: U+000A LINE FEED, U+0009 CHARACTER TABULATION, or U+0020 SPACE,
+ // which by default are all matched by \s in PHP.
+ return preg_replace( '/,\s+/', ',', $elem );
+ },
+ $slug_elements
+ );
+
+ return sanitize_text_field( join( ';', $slug_elements ) );
+ }
+
+ /**
+ * Sanitizes a tree of data using a schema.
+ *
+ * The schema structure should mirror the data tree. Each value provided in the
+ * schema should be a callable that will be applied to sanitize the corresponding
+ * value in the data tree. Keys that are in the data tree, but not present in the
+ * schema, will be removed in the santized data. Nested arrays are traversed recursively.
+ *
+ * @since 6.5.0
+ *
+ * @access private
+ *
+ * @param array $tree The data to sanitize.
+ * @param array $schema The schema used for sanitization.
+ * @return array The sanitized data.
+ */
+ public static function sanitize_from_schema( $tree, $schema ) {
+ if ( ! is_array( $tree ) || ! is_array( $schema ) ) {
+ return array();
+ }
+
+ foreach ( $tree as $key => $value ) {
+ // Remove keys not in the schema or with null/empty values.
+ if ( ! array_key_exists( $key, $schema ) ) {
+ unset( $tree[ $key ] );
+ continue;
+ }
+
+ $is_value_array = is_array( $value );
+ $is_schema_array = is_array( $schema[ $key ] ) && ! is_callable( $schema[ $key ] );
+
+ if ( $is_value_array && $is_schema_array ) {
+ if ( wp_is_numeric_array( $value ) ) {
+ // If indexed, process each item in the array.
+ foreach ( $value as $item_key => $item_value ) {
+ $tree[ $key ][ $item_key ] = isset( $schema[ $key ][0] ) && is_array( $schema[ $key ][0] )
+ ? self::sanitize_from_schema( $item_value, $schema[ $key ][0] )
+ : self::apply_sanitizer( $item_value, $schema[ $key ][0] );
+ }
+ } else {
+ // If it is an associative or indexed array, process as a single object.
+ $tree[ $key ] = self::sanitize_from_schema( $value, $schema[ $key ] );
+ }
+ } elseif ( ! $is_value_array && $is_schema_array ) {
+ // If the value is not an array but the schema is, remove the key.
+ unset( $tree[ $key ] );
+ } elseif ( ! $is_schema_array ) {
+ // If the schema is not an array, apply the sanitizer to the value.
+ $tree[ $key ] = self::apply_sanitizer( $value, $schema[ $key ] );
+ }
+
+ // Remove keys with null/empty values.
+ if ( empty( $tree[ $key ] ) ) {
+ unset( $tree[ $key ] );
+ }
+ }
+
+ return $tree;
+ }
+
+ /**
+ * Applies a sanitizer function to a value.
+ *
+ * @since 6.5.0
+ *
+ * @param mixed $value The value to sanitize.
+ * @param mixed $sanitizer The sanitizer function to apply.
+ * @return mixed The sanitized value.
+ */
+ private static function apply_sanitizer( $value, $sanitizer ) {
+ if ( null === $sanitizer ) {
+ return $value;
+
+ }
+ return call_user_func( $sanitizer, $value );
+ }
+
+ /**
+ * Provide the expected mime-type value for font files per-PHP release. Due to differences in the values returned these values differ between PHP versions.
+ *
+ * This is necessary until a collection of valid mime-types per-file extension can be provided to 'upload_mimes' filter.
+ *
+ * @since 6.5.0
+ *
+ * @return Array A collection of mime types keyed by file extension.
+ */
+ public static function get_allowed_font_mime_types() {
+ $php_7_ttf_mime_type = PHP_VERSION_ID >= 70300 ? 'application/font-sfnt' : 'application/x-font-ttf';
+
+ return array(
+ 'otf' => 'application/vnd.ms-opentype',
+ 'ttf' => PHP_VERSION_ID >= 70400 ? 'font/sfnt' : $php_7_ttf_mime_type,
+ 'woff' => PHP_VERSION_ID >= 80100 ? 'font/woff' : 'application/font-woff',
+ 'woff2' => PHP_VERSION_ID >= 80100 ? 'font/woff2' : 'application/font-woff2',
+ );
+ }
+ }
+}
diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php
similarity index 84%
rename from lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php
rename to lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php
index 3711015bb275ad..06a073d426abc1 100644
--- a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php
+++ b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php
@@ -74,7 +74,7 @@ public function register_routes() {
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {
- $collections_all = WP_Font_Library::get_font_collections();
+ $collections_all = WP_Font_Library::get_instance()->get_font_collections();
$page = $request['page'];
$per_page = $request['per_page'];
@@ -84,7 +84,7 @@ public function get_items( $request ) {
if ( $page > $max_pages && $total_items > 0 ) {
return new WP_Error(
'rest_post_invalid_page_number',
- __( 'The page number requested is larger than the number of pages available.', 'default' ),
+ __( 'The page number requested is larger than the number of pages available.' ),
array( 'status' => 400 )
);
}
@@ -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;
@@ -140,7 +142,7 @@ public function get_items( $request ) {
*/
public function get_item( $request ) {
$slug = $request->get_param( 'slug' );
- $collection = WP_Font_Library::get_font_collection( $slug );
+ $collection = WP_Font_Library::get_instance()->get_font_collection( $slug );
// If the collection doesn't exist returns a 404.
if ( is_wp_error( $collection ) ) {
@@ -148,48 +150,38 @@ public function get_item( $request ) {
return $collection;
}
- $item = $this->prepare_item_for_response( $collection, $request );
-
- if ( is_wp_error( $item ) ) {
- return $item;
- }
-
- return $item;
+ return $this->prepare_item_for_response( $collection, $request );
}
- /*
+ /**
* Prepare a single collection output for response.
*
* @since 6.5.0
*
* @param WP_Font_Collection $collection Collection object.
* @param WP_REST_Request $request Request object.
- * @return array|WP_Error
+ * @return WP_REST_Response Response object.
*/
public function prepare_item_for_response( $collection, $request ) {
$fields = $this->get_fields_for_response( $request );
$item = array();
- $config_fields = array( 'slug', 'name', 'description' );
- 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;
}
- $data_fields = array( 'font_families', 'categories' );
- if ( in_array( 'font_families', $fields, true ) || in_array( 'categories', $fields, true ) ) {
- $content = $collection->get_content();
-
- // If there was an error getting the collection data, return the error.
- if ( is_wp_error( $content ) ) {
- $content->add_data( array( 'status' => 500 ) );
- return $content;
+ // 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 ( in_array( $field, $fields, true ) ) {
- $item[ $field ] = $content[ $field ];
+ if ( rest_is_field_included( $field, $fields ) ) {
+ $item[ $field ] = $collection_data[ $field ];
}
}
}
@@ -212,9 +204,9 @@ public function prepare_item_for_response( $collection, $request ) {
*
* @since 6.5.0
*
- * @param WP_REST_Response $response The response object.
+ * @param WP_REST_Response $response The response object.
* @param WP_Font_Collection $collection The Font Collection object.
- * @param WP_REST_Request $request Request used to generate the response.
+ * @param WP_REST_Request $request Request used to generate the response.
*/
return apply_filters( 'rest_prepare_font_collection', $response, $collection, $request );
}
@@ -279,7 +271,7 @@ public function get_item_schema() {
* @return array Links for the given font collection.
*/
protected function prepare_links( $collection ) {
- $links = array(
+ return array(
'self' => array(
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $collection->slug ) ),
),
@@ -287,7 +279,6 @@ protected function prepare_links( $collection ) {
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
),
);
- return $links;
}
/**
@@ -322,17 +313,17 @@ public function get_collection_params() {
* @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
*/
public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
-
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- return new WP_Error(
- 'rest_cannot_read',
- __( 'Sorry, you are not allowed to use the Font Library on this site.', 'gutenberg' ),
- array(
- 'status' => rest_authorization_required_code(),
- )
- );
+ if ( current_user_can( 'edit_theme_options' ) ) {
+ return true;
}
- return true;
+
+ return new WP_Error(
+ 'rest_cannot_read',
+ __( 'Sorry, you are not allowed to use the Font Library on this site.', 'gutenberg' ),
+ array(
+ 'status' => rest_authorization_required_code(),
+ )
+ );
}
}
}
diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php
similarity index 85%
rename from lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php
rename to lib/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php
index 01ffce1eea4838..60f04e9ff23df3 100644
--- a/lib/experimental/fonts/font-library/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 );
@@ -187,20 +187,32 @@ public function validate_create_font_face_settings( $value, $request ) {
}
}
- $srcs = is_array( $settings['src'] ) ? $settings['src'] : array( $settings['src'] );
+ $srcs = is_array( $settings['src'] ) ? $settings['src'] : array( $settings['src'] );
+ $files = $request->get_file_params();
- // Check that srcs are non-empty strings.
- $filtered_src = array_filter( array_filter( $srcs, 'is_string' ) );
- if ( empty( $filtered_src ) ) {
- return new WP_Error(
- 'rest_invalid_param',
- __( 'font_face_settings[src] values must be non-empty strings.', 'gutenberg' ),
- array( 'status' => 400 )
- );
+ foreach ( $srcs as $src ) {
+ // Check that each src is a non-empty string.
+ $src = ltrim( $src );
+ if ( empty( $src ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ __( 'font_face_settings[src] values must be non-empty strings.', 'gutenberg' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ // Check that srcs are valid URLs or file references.
+ if ( false === wp_http_validate_url( $src ) && ! isset( $files[ $src ] ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ /* translators: %s: src value in the font face settings. */
+ sprintf( __( 'font_face_settings[src] value "%s" must be a valid URL or file reference.', 'gutenberg' ), $src ),
+ array( 'status' => 400 )
+ );
+ }
}
// Check that each file in the request references a src in the settings.
- $files = $request->get_file_params();
foreach ( array_keys( $files ) as $file ) {
if ( ! in_array( $file, $srcs, true ) ) {
return new WP_Error(
@@ -227,9 +239,12 @@ public function validate_create_font_face_settings( $value, $request ) {
public function sanitize_font_face_settings( $value ) {
// Settings arrive as stringified JSON, since this is a multipart/form-data request.
$settings = json_decode( $value, true );
+ $schema = $this->get_item_schema()['properties']['font_face_settings']['properties'];
- if ( isset( $settings['fontFamily'] ) ) {
- $settings['fontFamily'] = WP_Font_Utils::format_font_family( $settings['fontFamily'] );
+ // Sanitize settings based on callbacks in the schema.
+ foreach ( $settings as $key => $value ) {
+ $sanitize_callback = $schema[ $key ]['arg_options']['sanitize_callback'];
+ $settings[ $key ] = call_user_func( $sanitize_callback, $value );
}
return $settings;
@@ -509,11 +524,17 @@ public function get_item_schema() {
'description' => __( 'CSS font-family value.', 'gutenberg' ),
'type' => 'string',
'default' => '',
+ 'arg_options' => array(
+ 'sanitize_callback' => array( 'WP_Font_Utils', 'sanitize_font_family' ),
+ ),
),
'fontStyle' => array(
'description' => __( 'CSS font-style value.', 'gutenberg' ),
'type' => 'string',
'default' => 'normal',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'fontWeight' => array(
'description' => __( 'List of available font weights, separated by a space.', 'gutenberg' ),
@@ -521,6 +542,9 @@ public function get_item_schema() {
// Changed from `oneOf` to avoid errors from loose type checking.
// e.g. a fontWeight of "400" validates as both a string and an integer due to is_numeric check.
'type' => array( 'string', 'integer' ),
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'fontDisplay' => array(
'description' => __( 'CSS font-display value.', 'gutenberg' ),
@@ -533,10 +557,14 @@ public function get_item_schema() {
'swap',
'optional',
),
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'src' => array(
'description' => __( 'Paths or URLs to the font files.', 'gutenberg' ),
- // Changed from `oneOf` to `anyOf` due to rest_sanitize_array converting a string into an array.
+ // Changed from `oneOf` to `anyOf` due to rest_sanitize_array converting a string into an array,
+ // and causing a "matches more than one of the expected formats" error.
'anyOf' => array(
array(
'type' => 'string',
@@ -549,46 +577,83 @@ public function get_item_schema() {
),
),
'default' => array(),
+ 'arg_options' => array(
+ 'sanitize_callback' => function ( $value ) {
+ return is_array( $value ) ? array_map( array( $this, 'sanitize_src' ), $value ) : $this->sanitize_src( $value );
+ },
+ ),
),
'fontStretch' => array(
'description' => __( 'CSS font-stretch value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'ascentOverride' => array(
'description' => __( 'CSS ascent-override value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'descentOverride' => array(
'description' => __( 'CSS descent-override value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'fontVariant' => array(
'description' => __( 'CSS font-variant value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'fontFeatureSettings' => array(
'description' => __( 'CSS font-feature-settings value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'fontVariationSettings' => array(
'description' => __( 'CSS font-variation-settings value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'lineGapOverride' => array(
'description' => __( 'CSS line-gap-override value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'sizeAdjust' => array(
'description' => __( 'CSS size-adjust value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'unicodeRange' => array(
'description' => __( 'CSS unicode-range value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'preview' => array(
'description' => __( 'URL to a preview image of the font face.', 'gutenberg' ),
'type' => 'string',
+ 'format' => 'uri',
+ 'default' => '',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_url',
+ ),
),
),
'required' => array( 'fontFamily', 'src' ),
@@ -602,6 +667,26 @@ public function get_item_schema() {
return $this->add_additional_fields_schema( $this->schema );
}
+ /**
+ * Retrieves the item's schema for display / public consumption purposes.
+ *
+ * @since 6.5.0
+ *
+ * @return array Public item schema data.
+ */
+ public function get_public_item_schema() {
+
+ $schema = parent::get_public_item_schema();
+
+ // Also remove `arg_options' from child font_family_settings properties, since the parent
+ // controller only handles the top level properties.
+ foreach ( $schema['properties']['font_face_settings']['properties'] as &$property ) {
+ unset( $property['arg_options'] );
+ }
+
+ return $schema;
+ }
+
/**
* Retrieves the query params for the font face collection.
*
@@ -698,7 +783,7 @@ protected function get_parent_font_family_post( $font_family_id ) {
*/
protected function prepare_links( $post ) {
// Entity meta.
- $links = array(
+ return array(
'self' => array(
'href' => rest_url( $this->namespace . '/font-families/' . $post->post_parent . '/font-faces/' . $post->ID ),
),
@@ -709,8 +794,6 @@ protected function prepare_links( $post ) {
'href' => rest_url( $this->namespace . '/font-families/' . $post->post_parent ),
),
);
-
- return $links;
}
/**
@@ -741,6 +824,20 @@ protected function prepare_item_for_database( $request ) {
return $prepared_post;
}
+ /**
+ * Sanitizes a single src value for a font face.
+ *
+ * @since 6.5.0
+ *
+ * @param string $value Font face src that is a URL or the key for a $_FILES array item.
+ *
+ * @return string Sanitized value.
+ */
+ protected function sanitize_src( $value ) {
+ $value = ltrim( $value );
+ return false === wp_http_validate_url( $value ) ? (string) $value : sanitize_url( $value );
+ }
+
/**
* Handles the upload of a font file using wp_handle_upload().
*
@@ -750,7 +847,7 @@ protected function prepare_item_for_database( $request ) {
* @return array Array containing uploaded file attributes on success, or error on failure.
*/
protected function handle_font_file_upload( $file ) {
- add_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) );
+ add_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
add_filter( 'upload_dir', 'wp_get_font_dir' );
$overrides = array(
@@ -764,13 +861,13 @@ protected function handle_font_file_upload( $file ) {
// See wp_check_filetype_and_ext().
'test_type' => true,
// Only allow uploading font files for this request.
- 'mimes' => WP_Font_Library::get_expected_font_mime_types_per_php_version(),
+ 'mimes' => WP_Font_Utils::get_allowed_font_mime_types(),
);
$uploaded_file = wp_handle_upload( $file, $overrides );
remove_filter( 'upload_dir', 'wp_get_font_dir' );
- remove_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) );
+ remove_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
return $uploaded_file;
}
@@ -788,7 +885,7 @@ public function handle_font_file_upload_error( $file, $message ) {
$status = 500;
$code = 'rest_font_upload_unknown_error';
- if ( __( 'Sorry, you are not allowed to upload this file type.', 'default' ) === $message ) {
+ if ( __( 'Sorry, you are not allowed to upload this file type.' ) === $message ) {
$status = 400;
$code = 'rest_font_upload_invalid_file_type';
}
@@ -799,7 +896,7 @@ public function handle_font_file_upload_error( $file, $message ) {
/**
* Returns relative path to an uploaded font file.
*
- * The path is relative to the current fonts dir.
+ * The path is relative to the current fonts directory.
*
* @since 6.5.0
* @access private
diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php b/lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php
similarity index 87%
rename from lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php
rename to lib/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php
index 1f91d9e2457302..e4a2b2f8e97813 100644
--- a/lib/experimental/fonts/font-library/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 );
@@ -134,22 +134,21 @@ public function validate_font_family_settings( $value, $request ) {
/**
* Sanitizes the font family settings when creating or updating a font family.
*
- * @since 6.5.0
- *
- * @param string $value Encoded JSON string of font family settings.
- * @param WP_REST_Request $request Request object.
- * @return array Decoded array font family settings.
- */
+ * @since 6.5.0
+ *
+ * @param string $value Encoded JSON string of font family settings.
+ * @param WP_REST_Request $request Request object.
+ * @return array Decoded array font family settings.
+ */
public function sanitize_font_family_settings( $value ) {
+ // Settings arrive as stringified JSON, since this is a multipart/form-data request.
$settings = json_decode( $value, true );
+ $schema = $this->get_item_schema()['properties']['font_family_settings']['properties'];
- if ( isset( $settings['fontFamily'] ) ) {
- $settings['fontFamily'] = WP_Font_Utils::format_font_family( $settings['fontFamily'] );
- }
-
- // Provide default for preview, if not provided.
- if ( ! isset( $settings['preview'] ) ) {
- $settings['preview'] = '';
+ // Sanitize settings based on callbacks in the schema.
+ foreach ( $settings as $key => $value ) {
+ $sanitize_callback = $schema[ $key ]['arg_options']['sanitize_callback'];
+ $settings[ $key ] = call_user_func( $sanitize_callback, $value );
}
return $settings;
@@ -264,7 +263,7 @@ public function prepare_item_for_response( $item, $request ) {
return apply_filters( 'rest_prepare_wp_font_family', $response, $item, $request );
}
- /**
+ /**
* Retrieves the post's schema, conforming to JSON Schema.
*
* @since 6.5.0
@@ -307,25 +306,39 @@ public function get_item_schema() {
// Font family settings come directly from theme.json schema
// See https://schemas.wp.org/trunk/theme.json
'font_family_settings' => array(
- 'description' => __( 'font-face declaration in theme.json format.', 'gutenberg' ),
+ 'description' => __( 'font-face definition in theme.json format.', 'gutenberg' ),
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
'properties' => array(
'name' => array(
- 'description' => 'Name of the font family preset, translatable.',
+ 'description' => __( 'Name of the font family preset, translatable.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
),
'slug' => array(
- 'description' => 'Kebab-case unique identifier for the font family preset.',
+ 'description' => __( 'Kebab-case unique identifier for the font family preset.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_title',
+ ),
),
'fontFamily' => array(
- 'description' => 'CSS font-family value.',
+ 'description' => __( 'CSS font-family value.', 'gutenberg' ),
'type' => 'string',
+ 'arg_options' => array(
+ 'sanitize_callback' => array( 'WP_Font_Utils', 'sanitize_font_family' ),
+ ),
),
'preview' => array(
- 'description' => 'URL to a preview image of the font family.',
+ 'description' => __( 'URL to a preview image of the font family.', 'gutenberg' ),
'type' => 'string',
+ 'format' => 'uri',
+ 'default' => '',
+ 'arg_options' => array(
+ 'sanitize_callback' => 'sanitize_url',
+ ),
),
),
'required' => array( 'name', 'slug', 'fontFamily' ),
@@ -339,6 +352,26 @@ public function get_item_schema() {
return $this->add_additional_fields_schema( $this->schema );
}
+ /**
+ * Retrieves the item's schema for display / public consumption purposes.
+ *
+ * @since 6.5.0
+ *
+ * @return array Public item schema data.
+ */
+ public function get_public_item_schema() {
+
+ $schema = parent::get_public_item_schema();
+
+ // Also remove `arg_options' from child font_family_settings properties, since the parent
+ // controller only handles the top level properties.
+ foreach ( $schema['properties']['font_family_settings']['properties'] as &$property ) {
+ unset( $property['arg_options'] );
+ }
+
+ return $schema;
+ }
+
/**
* Retrieves the query params for the font family collection.
*
@@ -405,7 +438,6 @@ public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CRE
*
* @param int $font_family_id Font family post ID.
* @return int[] Array of child font face post IDs.
- * .
*/
protected function get_font_face_ids( $font_family_id ) {
$query = new WP_Query(
diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/compat/wordpress-6.5/fonts/fonts.php
similarity index 78%
rename from lib/experimental/fonts/font-library/font-library.php
rename to lib/compat/wordpress-6.5/fonts/fonts.php
index 5af21f11ebbb26..e6913b0d8a57eb 100644
--- a/lib/experimental/fonts/font-library/font-library.php
+++ b/lib/compat/wordpress-6.5/fonts/fonts.php
@@ -19,7 +19,7 @@
*
* @since 6.5.0
*/
-function gutenberg_init_font_library_routes() {
+function gutenberg_create_initial_post_types() {
// @core-merge: This code will go into Core's `create_initial_post_types()`.
$args = array(
'labels' => array(
@@ -82,13 +82,30 @@ function gutenberg_init_font_library_routes() {
'autosave_rest_controller_class' => 'stdClass',
)
);
+}
+/**
+ * Initializes REST routes.
+ *
+ * @since 6.5
+ */
+function gutenberg_create_initial_rest_routes() {
// @core-merge: This code will go into Core's `create_initial_rest_routes()`.
$font_collections_controller = new WP_REST_Font_Collections_Controller();
$font_collections_controller->register_routes();
}
-add_action( 'rest_api_init', 'gutenberg_init_font_library_routes' );
+/**
+ * Initializes REST routes and post types.
+ *
+ * @since 6.5
+ */
+function gutenberg_init_font_library() {
+ gutenberg_create_initial_post_types();
+ gutenberg_create_initial_rest_routes();
+}
+
+add_action( 'rest_api_init', 'gutenberg_init_font_library' );
if ( ! function_exists( 'wp_register_font_collection' ) ) {
@@ -97,18 +114,24 @@ function gutenberg_init_font_library_routes() {
*
* @since 6.5.0
*
- * @param string[] $config {
- * Font collection associative array of configuration options.
+ * @param string $slug Font collection slug. May only contain alphanumeric characters, dashes,
+ * and underscores. See sanitize_title().
+ * @param array|string $data_or_file {
+ * Font collection data array or a path/URL to a JSON file containing the font collection.
*
- * @type string $id The font collection's unique ID.
- * @type string $src The font collection's data as a JSON file path.
- * @type array $data The font collection's data as a PHP array.
+ * @link https://schemas.wp.org/trunk/font-collection.json
+ *
+ * @type string $name Required. Name of the font collection shown in the Font Library.
+ * @type string $description Optional. A short descriptive summary of the font collection. Default empty.
+ * @type array $font_families Required. Array of font family definitions that are in the collection.
+ * @type array $categories Optional. Array of categories, each with a name and slug, that are used by the
+ * fonts in the collection. Default empty.
* }
- * @return WP_Font_Collection|WP_Error A font collection is it was registered
- * successfully, else WP_Error.
+ * @return WP_Font_Collection|WP_Error A font collection if it was registered
+ * successfully, or WP_Error object on failure.
*/
- function wp_register_font_collection( $config ) {
- return WP_Font_Library::register_font_collection( $config );
+ function wp_register_font_collection( $slug, $data_or_file ) {
+ return WP_Font_Library::get_instance()->register_font_collection( $slug, $data_or_file );
}
}
@@ -118,22 +141,19 @@ function wp_register_font_collection( $config ) {
*
* @since 6.5.0
*
- * @param string $collection_id The font collection ID.
+ * @param string $slug Font collection slug.
+ * @return bool True if the font collection was unregistered successfully, else false.
*/
- function wp_unregister_font_collection( $collection_id ) {
- WP_Font_Library::unregister_font_collection( $collection_id );
+ function wp_unregister_font_collection( $slug ) {
+ return WP_Font_Library::get_instance()->unregister_font_collection( $slug );
}
-
}
-$google_fonts = array(
- 'slug' => 'google-fonts',
- 'name' => 'Google Fonts',
- 'description' => __( 'Add from Google Fonts. Fonts are copied to and served from your site.', 'gutenberg' ),
- 'src' => 'https://s.w.org/images/fonts/17.6/collections/google-fonts-with-preview.json',
-);
-
-wp_register_font_collection( $google_fonts );
+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' ) ) {
@@ -152,7 +172,6 @@ function wp_unregister_font_collection( $collection_id ) {
* @type string $baseurl URL path without subdir.
* @type string|false $error False or error message.
* }
- *
* @return array $defaults {
* Array of information about the upload directory.
*
@@ -179,7 +198,15 @@ function wp_get_font_dir( $defaults = array() ) {
$defaults['baseurl'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path;
$defaults['error'] = false;
- // Filters the fonts directory data.
+ /**
+ * Filters the fonts directory data.
+ *
+ * This filter allows developers to modify the fonts directory data.
+ *
+ * @since 6.5.0
+ *
+ * @param array $defaults The original fonts directory data.
+ */
return apply_filters( 'font_dir', $defaults );
}
}
@@ -195,7 +222,6 @@ function wp_get_font_dir( $defaults = array() ) {
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
- * @return void
*/
function _wp_after_delete_font_family( $post_id, $post ) {
if ( 'wp_font_family' !== $post->post_type ) {
@@ -225,7 +251,6 @@ function _wp_after_delete_font_family( $post_id, $post ) {
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
- * @return void
*/
function _wp_before_delete_font_face( $post_id, $post ) {
if ( 'wp_font_face' !== $post->post_type ) {
@@ -275,7 +300,7 @@ function gutenberg_convert_legacy_font_family_format() {
continue;
}
- $font_faces = $font_family_json['fontFace'] ?? array();
+ $font_faces = isset( $font_family_json['fontFace'] ) ? $font_family_json['fontFace'] : array();
unset( $font_family_json['fontFace'] );
// Save wp_font_face posts within the family.
@@ -290,7 +315,7 @@ function gutenberg_convert_legacy_font_family_format() {
$font_face_id = wp_insert_post( wp_slash( $args ) );
- $file_urls = (array) $font_face['src'] ?? array();
+ $file_urls = (array) ( isset( $font_face['src'] ) ? $font_face['src'] : array() );
foreach ( $file_urls as $file_url ) {
// continue if the file is not local.
@@ -306,7 +331,7 @@ function gutenberg_convert_legacy_font_family_format() {
// Update the font family post to remove the font face data.
$args = array();
$args['ID'] = $font_family->ID;
- $args['post_title'] = $font_family_json['name'] ?? '';
+ $args['post_title'] = isset( $font_family_json['name'] ) ? $font_family_json['name'] : '';
$args['post_name'] = sanitize_title( $font_family_json['slug'] );
unset( $font_family_json['name'] );
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php b/lib/experimental/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php
rename to lib/experimental/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider-local.php b/lib/experimental/font-face/bc-layer/class-wp-fonts-provider-local.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider-local.php
rename to lib/experimental/font-face/bc-layer/class-wp-fonts-provider-local.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider.php b/lib/experimental/font-face/bc-layer/class-wp-fonts-provider.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider.php
rename to lib/experimental/font-face/bc-layer/class-wp-fonts-provider.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-resolver.php b/lib/experimental/font-face/bc-layer/class-wp-fonts-resolver.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-resolver.php
rename to lib/experimental/font-face/bc-layer/class-wp-fonts-resolver.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-utils.php b/lib/experimental/font-face/bc-layer/class-wp-fonts-utils.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-fonts-utils.php
rename to lib/experimental/font-face/bc-layer/class-wp-fonts-utils.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-fonts.php b/lib/experimental/font-face/bc-layer/class-wp-fonts.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-fonts.php
rename to lib/experimental/font-face/bc-layer/class-wp-fonts.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-web-fonts.php b/lib/experimental/font-face/bc-layer/class-wp-web-fonts.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-web-fonts.php
rename to lib/experimental/font-face/bc-layer/class-wp-web-fonts.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider-local.php b/lib/experimental/font-face/bc-layer/class-wp-webfonts-provider-local.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider-local.php
rename to lib/experimental/font-face/bc-layer/class-wp-webfonts-provider-local.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider.php b/lib/experimental/font-face/bc-layer/class-wp-webfonts-provider.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider.php
rename to lib/experimental/font-face/bc-layer/class-wp-webfonts-provider.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-utils.php b/lib/experimental/font-face/bc-layer/class-wp-webfonts-utils.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts-utils.php
rename to lib/experimental/font-face/bc-layer/class-wp-webfonts-utils.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts.php b/lib/experimental/font-face/bc-layer/class-wp-webfonts.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/class-wp-webfonts.php
rename to lib/experimental/font-face/bc-layer/class-wp-webfonts.php
diff --git a/lib/experimental/fonts/font-face/bc-layer/webfonts-deprecations.php b/lib/experimental/font-face/bc-layer/webfonts-deprecations.php
similarity index 100%
rename from lib/experimental/fonts/font-face/bc-layer/webfonts-deprecations.php
rename to lib/experimental/font-face/bc-layer/webfonts-deprecations.php
diff --git a/lib/experimental/fonts/font-library/class-wp-font-collection.php b/lib/experimental/fonts/font-library/class-wp-font-collection.php
deleted file mode 100644
index 22514151d0aa49..00000000000000
--- a/lib/experimental/fonts/font-library/class-wp-font-collection.php
+++ /dev/null
@@ -1,231 +0,0 @@
-is_config_valid( $config );
-
- $this->slug = isset( $config['slug'] ) ? $config['slug'] : '';
- $this->name = isset( $config['name'] ) ? $config['name'] : '';
- $this->description = isset( $config['description'] ) ? $config['description'] : '';
- $this->src = isset( $config['src'] ) ? $config['src'] : '';
- $this->font_families = isset( $config['font_families'] ) ? $config['font_families'] : array();
- $this->categories = isset( $config['categories'] ) ? $config['categories'] : array();
- }
-
- /**
- * Checks if the font collection config is valid.
- *
- * @since 6.5.0
- *
- * @param array $config Font collection config options. {
- * @type string $slug The font collection's unique slug.
- * @type string $name The font collection's name.
- * @type string $description The font collection's description.
- * @type string $src The font collection's source.
- * @type array $font_families An array of font families in the font collection.
- * @type array $categories The font collection's categories.
- * }
- * @return bool True if the font collection config is valid and false otherwise.
- */
- public static function is_config_valid( $config ) {
- if ( empty( $config ) || ! is_array( $config ) ) {
- _doing_it_wrong( __METHOD__, __( 'Font Collection config options are required as a non-empty array.', 'gutenberg' ), '6.5.0' );
- return false;
- }
-
- $required_keys = array( 'slug', 'name' );
- foreach ( $required_keys as $key ) {
- if ( empty( $config[ $key ] ) ) {
- _doing_it_wrong(
- __METHOD__,
- // translators: %s: Font collection config key.
- sprintf( __( 'Font Collection config %s is required as a non-empty string.', 'gutenberg' ), $key ),
- '6.5.0'
- );
- return false;
- }
- }
-
- if (
- ( empty( $config['src'] ) && empty( $config['font_families'] ) ) ||
- ( ! empty( $config['src'] ) && ! empty( $config['font_families'] ) )
- ) {
- _doing_it_wrong(
- __METHOD__,
- sprintf(
- /* translators: %1$s: src, %2$s: font_families */
- __( 'Font Collection config "%1$s" option OR "%2$s" option is required.', 'gutenberg' ),
- 'src',
- 'font_families'
- ),
- '6.5.0'
- );
- return false;
- }
-
- return true;
- }
-
- /**
- * Gets the font collection content.
- *
- * Load the font collection data from the src if it is not already loaded.
- *
- * @since 6.5.0
- *
- * @return array|WP_Error {
- * An array of font collection contents.
- *
- * @type array $font_families The font collection's font families.
- * @type string $categories The font collection's categories.
- * }
- *
- * A WP_Error object if there was an error loading the font collection data.
- */
- public function get_content() {
- // If the font families are not loaded, and the src is not empty, load the data from the src.
- if ( empty( $this->font_families ) && ! empty( $this->src ) ) {
- $data = $this->load_contents_from_src();
- if ( is_wp_error( $data ) ) {
- return $data;
- }
- }
-
- return array(
- 'font_families' => $this->font_families,
- 'categories' => $this->categories,
- );
- }
-
- /**
- * Loads the font collection data from the src.
- *
- * @since 6.5.0
- *
- * @return array|WP_Error An array containing the list of font families in font-collection.json format on success,
- * else an instance of WP_Error on failure.
- */
- private function load_contents_from_src() {
- // If the src is a URL, fetch the data from the URL.
- if ( preg_match( '#^https?://#', $this->src ) ) {
- if ( ! wp_http_validate_url( $this->src ) ) {
- return new WP_Error( 'font_collection_read_error', __( 'Invalid URL for Font Collection data.', 'gutenberg' ) );
- }
-
- $response = wp_remote_get( $this->src );
- if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
- return new WP_Error( 'font_collection_read_error', __( 'Error fetching the Font Collection data from a URL.', 'gutenberg' ) );
- }
-
- $data = json_decode( wp_remote_retrieve_body( $response ), true );
- if ( empty( $data ) ) {
- return new WP_Error( 'font_collection_read_error', __( 'Error decoding the Font Collection data from the REST response JSON.', 'gutenberg' ) );
- }
- // If the src is a file path, read the data from the file.
- } else {
- if ( ! file_exists( $this->src ) ) {
- return new WP_Error( 'font_collection_read_error', __( 'Font Collection data JSON file does not exist.', 'gutenberg' ) );
- }
- $data = wp_json_file_decode( $this->src, array( 'associative' => true ) );
- if ( empty( $data ) ) {
- return new WP_Error( 'font_collection_read_error', __( 'Error reading the Font Collection data JSON file contents.', 'gutenberg' ) );
- }
- }
-
- if ( empty( $data['font_families'] ) ) {
- return new WP_Error( 'font_collection_contents_error', __( 'Font Collection data JSON file does not contain font families.', 'gutenberg' ) );
- }
-
- $this->font_families = $data['font_families'];
- $this->categories = $data['categories'] ?? array();
-
- return $data;
- }
- }
-}
diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php
deleted file mode 100644
index 21234f47c4bb23..00000000000000
--- a/lib/experimental/fonts/font-library/class-wp-font-library.php
+++ /dev/null
@@ -1,161 +0,0 @@
-= 70300 ? 'application/font-sfnt' : 'application/x-font-ttf';
-
- return array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => $php_version_id >= 70400 ? 'font/sfnt' : $php_7_ttf_mime_type,
- 'woff' => $php_version_id >= 80100 ? 'font/woff' : 'application/font-woff',
- 'woff2' => $php_version_id >= 80100 ? 'font/woff2' : 'application/font-woff2',
- );
- }
-
- /**
- * Font collections.
- *
- * @since 6.5.0
- *
- * @var array
- */
- private static $collections = array();
-
- /**
- * Register a new font collection.
- *
- * @since 6.5.0
- *
- * @param array $config Font collection config options.
- * See {@see wp_register_font_collection()} for the supported fields.
- * @return WP_Font_Collection|WP_Error A font collection is it was registered successfully and a WP_Error otherwise.
- */
- public static function register_font_collection( $config ) {
- if ( ! WP_Font_Collection::is_config_valid( $config ) ) {
- $error_message = __( 'Font collection config is invalid.', 'gutenberg' );
- return new WP_Error( 'font_collection_registration_error', $error_message );
- }
-
- $new_collection = new WP_Font_Collection( $config );
-
- if ( self::is_collection_registered( $new_collection->slug ) ) {
- $error_message = sprintf(
- /* translators: %s: Font collection slug. */
- __( 'Font collection with slug: "%s" is already registered.', 'gutenberg' ),
- $config['slug']
- );
- _doing_it_wrong(
- __METHOD__,
- $error_message,
- '6.5.0'
- );
- return new WP_Error( 'font_collection_registration_error', $error_message );
- }
- self::$collections[ $new_collection->slug ] = $new_collection;
- return $new_collection;
- }
-
- /**
- * Unregisters a previously registered font collection.
- *
- * @since 6.5.0
- *
- * @param string $collection_slug Font collection slug.
- * @return bool True if the font collection was unregistered successfully and false otherwise.
- */
- public static function unregister_font_collection( $slug ) {
- if ( ! self::is_collection_registered( $slug ) ) {
- _doing_it_wrong(
- __METHOD__,
- /* translators: %s: Font collection slug. */
- sprintf( __( 'Font collection "%s" not found.', 'default' ), $slug ),
- '6.5.0'
- );
- return false;
- }
- unset( self::$collections[ $slug ] );
- return true;
- }
-
- /**
- * Checks if a font collection is registered.
- *
- * @since 6.5.0
- *
- * @param string $slug Font collection slug.
- * @return bool True if the font collection is registered and false otherwise.
- */
- private static function is_collection_registered( $slug ) {
- return array_key_exists( $slug, self::$collections );
- }
-
- /**
- * Gets all the font collections available.
- *
- * @since 6.5.0
- *
- * @return array List of font collections.
- */
- public static function get_font_collections() {
- return self::$collections;
- }
-
- /**
- * Gets a font collection.
- *
- * @since 6.5.0
- *
- * @param string $slug Font collection slug.
- * @return array List of font collections.
- */
- public static function get_font_collection( $slug ) {
- if ( array_key_exists( $slug, self::$collections ) ) {
- return self::$collections[ $slug ];
- }
- return new WP_Error( 'font_collection_not_found', 'Font collection not found.' );
- }
-
-
-
- /**
- * Sets the allowed mime types for fonts.
- *
- * @since 6.5.0
- *
- * @param array $mime_types List of allowed mime types.
- * @return array Modified upload directory.
- */
- public static function set_allowed_mime_types( $mime_types ) {
- return array_merge( $mime_types, self::get_expected_font_mime_types_per_php_version() );
- }
- }
-}
diff --git a/lib/experimental/fonts/font-library/class-wp-font-utils.php b/lib/experimental/fonts/font-library/class-wp-font-utils.php
deleted file mode 100644
index e6fdc5ae08f5d8..00000000000000
--- a/lib/experimental/fonts/font-library/class-wp-font-utils.php
+++ /dev/null
@@ -1,137 +0,0 @@
- '',
- 'fontStyle' => 'normal',
- 'fontWeight' => '400',
- 'fontStretch' => '100%',
- 'unicodeRange' => 'U+0-10FFFF',
- )
- );
-
- // Convert all values to lowercase for comparison.
- // Font family names may use multibyte characters.
- $font_family = mb_strtolower( $settings['fontFamily'] );
- $font_style = strtolower( $settings['fontStyle'] );
- $font_weight = strtolower( $settings['fontWeight'] );
- $font_stretch = strtolower( $settings['fontStretch'] );
- $unicode_range = strtoupper( $settings['unicodeRange'] );
-
- // Convert weight keywords to numeric strings.
- $font_weight = str_replace( 'normal', '400', $font_weight );
- $font_weight = str_replace( 'bold', '700', $font_weight );
-
- // Convert stretch keywords to numeric strings.
- $font_stretch_map = array(
- 'ultra-condensed' => '50%',
- 'extra-condensed' => '62.5%',
- 'condensed' => '75%',
- 'semi-condensed' => '87.5%',
- 'normal' => '100%',
- 'semi-expanded' => '112.5%',
- 'expanded' => '125%',
- 'extra-expanded' => '150%',
- 'ultra-expanded' => '200%',
- );
- $font_stretch = str_replace( array_keys( $font_stretch_map ), array_values( $font_stretch_map ), $font_stretch );
-
- $slug_elements = array( $font_family, $font_style, $font_weight, $font_stretch, $unicode_range );
-
- $slug_elements = array_map(
- function ( $elem ) {
- // Remove quotes to normalize font-family names, and ';' to use as a separator.
- $elem = trim( str_replace( array( '"', "'", ';' ), '', $elem ) );
-
- // Normalize comma separated lists by removing whitespace in between items,
- // but keep whitespace within items (e.g. "Open Sans" and "OpenSans" are different fonts).
- // CSS spec for whitespace includes: U+000A LINE FEED, U+0009 CHARACTER TABULATION, or U+0020 SPACE,
- // which by default are all matched by \s in PHP.
- return preg_replace( '/,\s+/', ',', $elem );
- },
- $slug_elements
- );
-
- return join( ';', $slug_elements );
- }
- }
-}
diff --git a/lib/load.php b/lib/load.php
index 46ee3bc3681eab..b92f5b734548a0 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -137,13 +137,13 @@ function gutenberg_is_experiment_enabled( $name ) {
remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WordPress 6.0's stopgap handler.
// Loads the Font Library.
-require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php';
-require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php';
-require __DIR__ . '/experimental/fonts/font-library/class-wp-font-utils.php';
-require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-families-controller.php';
-require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php';
-require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php';
-require __DIR__ . '/experimental/fonts/font-library/font-library.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-font-collection.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-font-library.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-font-utils.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-rest-font-families-controller.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-rest-font-faces-controller.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/class-wp-rest-font-collections-controller.php';
+require __DIR__ . '/compat/wordpress-6.5/fonts/fonts.php';
// Load the Font Face and Font Face Resolver, if not already loaded by WordPress Core.
if ( ! class_exists( 'WP_Font_Face' ) ) {
@@ -160,18 +160,18 @@ function gutenberg_is_experiment_enabled( $name ) {
// Load the BC Layer to avoid fatal errors of extenders using the Fonts API.
// @core-merge: do not merge the BC layer files into WordPress Core.
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-fonts-utils.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-fonts.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-fonts-provider-local.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-fonts-resolver.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/webfonts-deprecations.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-webfonts-utils.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-webfonts-provider-local.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-webfonts.php';
-require __DIR__ . '/experimental/fonts/font-face/bc-layer/class-wp-web-fonts.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-fonts-provider.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-fonts-utils.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-fonts.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-fonts-provider-local.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-fonts-resolver.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-gutenberg-fonts-api-bc-layer.php';
+require __DIR__ . '/experimental/font-face/bc-layer/webfonts-deprecations.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-webfonts-utils.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-webfonts-provider.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-webfonts-provider-local.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-webfonts.php';
+require __DIR__ . '/experimental/font-face/bc-layer/class-wp-web-fonts.php';
// Plugin specific code.
require __DIR__ . '/script-loader.php';
diff --git a/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php
index a971bd51234305..dc720b6b7db701 100644
--- a/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php
+++ b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php
@@ -6,18 +6,28 @@
*
* @package WordPress
* @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
*/
class Tests_Font_Family_Backwards_Compatibility extends WP_UnitTestCase {
+ private $post_ids_to_delete;
+
public function set_up() {
parent::set_up();
+ $this->post_ids_to_delete = array();
delete_option( 'gutenberg_font_family_format_converted' );
}
public function tear_down() {
- parent::tear_down();
+ foreach ( $this->post_ids_to_delete as $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
delete_option( 'gutenberg_font_family_format_converted' );
+
+ parent::tear_down();
}
public function test_font_faces_with_remote_src() {
@@ -27,79 +37,74 @@ public function test_font_faces_with_remote_src() {
gutenberg_convert_legacy_font_family_format();
- $font_family = get_post( $font_family_id );
+ $font_family = $this->get_post( $font_family_id );
$font_faces = $this->get_font_faces( $font_family_id );
list( $font_face1, $font_face2, $font_face3 ) = $font_faces;
// Updated font family post.
- $this->assertSame( 'wp_font_family', $font_family->post_type );
- $this->assertSame( 'publish', $font_family->post_status );
+ $this->assertSame( 'wp_font_family', $font_family->post_type, 'The font family post type should be wp_font_family.' );
+ $this->assertSame( 'publish', $font_family->post_status, 'The font family post status should be publish.' );
$font_family_title = 'Open Sans';
- $this->assertSame( $font_family_title, $font_family->post_title );
+ $this->assertSame( $font_family_title, $font_family->post_title, 'The font family post title should be Open Sans.' );
$font_family_slug = 'open-sans';
- $this->assertSame( $font_family_slug, $font_family->post_name );
+ $this->assertSame( $font_family_slug, $font_family->post_name, 'The font family post name should be open-sans.' );
$font_family_content = wp_json_encode( json_decode( '{"fontFamily":"\'Open Sans\', sans-serif","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans.svg"}', true ) );
- $this->assertSame( $font_family_content, $font_family->post_content );
+ $this->assertSame( $font_family_content, $font_family->post_content, 'The font family post content should match.' );
$meta = get_post_meta( $font_family_id, '_gutenberg_legacy_font_family', true );
- $this->assertSame( $legacy_content, $meta );
+ $this->assertSame( $legacy_content, $meta, 'The _gutenberg_legacy_font_family post meta content should match.' );
// First font face post.
- $this->assertSame( 'wp_font_face', $font_face1->post_type );
- $this->assertSame( $font_family_id, $font_face1->post_parent );
- $this->assertSame( 'publish', $font_face1->post_status );
+ $this->assertSame( 'wp_font_face', $font_face1->post_type, 'The 1st font face post type should be wp_font_face.' );
+ $this->assertSame( $font_family_id, $font_face1->post_parent, 'The 1st font face post parent should match.' );
+ $this->assertSame( 'publish', $font_face1->post_status, 'The 1st font face post status should be publish.' );
$font_face1_title = 'open sans;normal;400;100%;U+0-10FFFF';
- $this->assertSame( $font_face1_title, $font_face1->post_title );
- $this->assertSame( sanitize_title( $font_face1_title ), $font_face1->post_name );
+ $this->assertSame( $font_face1_title, $font_face1->post_title, 'The 1st font face post title should match.' );
+ $this->assertSame( sanitize_title( $font_face1_title ), $font_face1->post_name, 'The 1st font face post name should match.' );
$font_face1_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"normal","fontWeight":"400","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0C4nY1M2xLER.ttf"}' ) );
- $this->assertSame( $font_face1_content, $font_face1->post_content );
+ $this->assertSame( $font_face1_content, $font_face1->post_content, 'The 1st font face post content should match.' );
// With a remote url, file post meta should not be set.
$meta = get_post_meta( $font_face1->ID, '_wp_font_face_file', true );
- $this->assertSame( '', $meta );
+ $this->assertSame( '', $meta, 'The _wp_font_face_file post meta for the 1st font face should be an empty string.' );
// Second font face post.
- $this->assertSame( 'wp_font_face', $font_face2->post_type );
- $this->assertSame( $font_family_id, $font_face2->post_parent );
- $this->assertSame( 'publish', $font_face2->post_status );
+ $this->assertSame( 'wp_font_face', $font_face2->post_type, 'The 2nd font face post type should be wp_font_face.' );
+ $this->assertSame( $font_family_id, $font_face2->post_parent, 'The 2md font face post type should be wp_font_face.' );
+ $this->assertSame( 'publish', $font_face2->post_status, 'The 2nd font face post status should be publish.' );
$font_face2_title = 'open sans;italic;400;100%;U+0-10FFFF';
- $this->assertSame( $font_face2_title, $font_face2->post_title );
- $this->assertSame( sanitize_title( $font_face2_title ), $font_face2->post_name );
+ $this->assertSame( $font_face2_title, $font_face2->post_title, 'The 2nd font face post title should match.' );
+ $this->assertSame( sanitize_title( $font_face2_title ), $font_face2->post_name, 'The 2nd font face post name should match.' );
$font_face2_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"italic","fontWeight":"400","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-italic.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkaVcUwaERZjA.ttf"}' ) );
- $this->assertSame( $font_face2_content, $font_face2->post_content );
+ $this->assertSame( $font_face2_content, $font_face2->post_content, 'The 2nd font face post content should match.' );
// With a remote url, file post meta should not be set.
$meta = get_post_meta( $font_face2->ID, '_wp_font_face_file', true );
- $this->assertSame( '', $meta );
+ $this->assertSame( '', $meta, 'The _wp_font_face_file post meta for the 2nd font face should be an empty string.' );
// Third font face post.
- $this->assertSame( 'wp_font_face', $font_face3->post_type );
- $this->assertSame( $font_family_id, $font_face3->post_parent );
- $this->assertSame( 'publish', $font_face3->post_status );
+ $this->assertSame( 'wp_font_face', $font_face3->post_type, 'The 3rd font face post type should be wp_font_face.' );
+ $this->assertSame( $font_family_id, $font_face3->post_parent, 'The 3rd font face post type should be wp_font_face.' );
+ $this->assertSame( 'publish', $font_face3->post_status, 'The 3rd font face post status should be publish.' );
$font_face3_title = 'open sans;normal;700;100%;U+0-10FFFF';
- $this->assertSame( $font_face3_title, $font_face3->post_title );
- $this->assertSame( sanitize_title( $font_face3_title ), $font_face3->post_name );
+ $this->assertSame( $font_face3_title, $font_face3->post_title, 'The 3rd font face post title should match.' );
+ $this->assertSame( sanitize_title( $font_face3_title ), $font_face3->post_name, 'The 3rd font face post name should match.' );
$font_face3_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"normal","fontWeight":"700","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-700-normal.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsg-1y4nY1M2xLER.ttf"}' ) );
- $this->assertSame( $font_face3_content, $font_face3->post_content );
+ $this->assertSame( $font_face3_content, $font_face3->post_content, 'The 3rd font face post content should match.' );
// With a remote url, file post meta should not be set.
$meta = get_post_meta( $font_face3->ID, '_wp_font_face_file', true );
- $this->assertSame( '', $meta );
-
- wp_delete_post( $font_family_id, true );
- wp_delete_post( $font_face1->ID, true );
- wp_delete_post( $font_face2->ID, true );
- wp_delete_post( $font_face3->ID, true );
+ $this->assertSame( '', $meta, 'The _wp_font_face_file post meta for the 3rd font face should be an empty string.' );
}
public function test_font_faces_with_local_src() {
@@ -110,16 +115,14 @@ public function test_font_faces_with_local_src() {
gutenberg_convert_legacy_font_family_format();
$font_faces = $this->get_font_faces( $font_family_id );
- $this->assertCount( 1, $font_faces );
+
+ $this->assertCount( 1, $font_faces, 'There should be 1 font face.' );
$font_face = reset( $font_faces );
// Check that file meta is present.
$file_path = 'open-sans_normal_400.ttf';
$meta = get_post_meta( $font_face->ID, '_wp_font_face_file', true );
- $this->assertSame( $file_path, $meta );
-
- wp_delete_post( $font_family_id, true );
- wp_delete_post( $font_face->ID, true );
+ $this->assertSame( $file_path, $meta, 'The _wp_font_face_file should match.' );
}
public function test_migration_only_runs_once() {
@@ -135,12 +138,10 @@ public function test_migration_only_runs_once() {
// Meta with backup content will not be present if migration isn't triggered.
$meta = get_post_meta( $font_family_id, '_gutenberg_legacy_font_family', true );
$this->assertSame( '', $meta );
-
- wp_delete_post( $font_family_id, true );
}
protected function create_font_family( $content ) {
- return wp_insert_post(
+ $post_id = wp_insert_post(
array(
'post_type' => 'wp_font_family',
'post_status' => 'publish',
@@ -149,10 +150,22 @@ protected function create_font_family( $content ) {
'post_content' => $content,
)
);
+
+ $this->store_id_for_cleanup_in_teardown( $post_id );
+
+ return $post_id;
+ }
+
+ private function get_post( $post_id ) {
+ $post = get_post( $post_id );
+
+ $this->store_id_for_cleanup_in_teardown( $post );
+
+ return $post;
}
protected function get_font_faces( $font_family_id ) {
- return get_posts(
+ $posts = get_posts(
array(
'post_parent' => $font_family_id,
'post_type' => 'wp_font_face',
@@ -160,5 +173,24 @@ protected function get_font_faces( $font_family_id ) {
'orderby' => 'id',
)
);
+
+ $this->store_id_for_cleanup_in_teardown( $posts );
+
+ return $posts;
+ }
+
+ private function store_id_for_cleanup_in_teardown( $post ) {
+ if ( null === $post ) {
+ return;
+ }
+
+ $posts = is_array( $post ) ? $post : array( $post );
+
+ foreach ( $posts as $post ) {
+ if ( null === $post ) {
+ continue;
+ }
+ $this->post_ids_to_delete[] = is_int( $post ) ? $post : $post->ID;
+ }
}
}
diff --git a/phpunit/tests/fonts/font-library/fontLibraryHooks.php b/phpunit/tests/fonts/font-library/fontLibraryHooks.php
index 2c471e2a9759c6..85d631ecaa45f2 100644
--- a/phpunit/tests/fonts/font-library/fontLibraryHooks.php
+++ b/phpunit/tests/fonts/font-library/fontLibraryHooks.php
@@ -4,9 +4,12 @@
*
* @package WordPress
* @subpackage Font Library
+ *
+ * @group fonts
+ * @group font-library
*/
+class Tests_Fonts_FontLibraryHooks extends WP_UnitTestCase {
-class Tests_Font_Library_Hooks extends WP_UnitTestCase {
public function test_deleting_font_family_deletes_child_font_faces() {
$font_family_id = self::factory()->post->create(
array(
@@ -33,8 +36,8 @@ public function test_deleting_font_family_deletes_child_font_faces() {
wp_delete_post( $font_family_id, true );
- $this->assertNull( get_post( $font_face_id ) );
- $this->assertNotNull( get_post( $other_font_face_id ) );
+ $this->assertNull( get_post( $font_face_id ), 'Font face post should also have been deleted.' );
+ $this->assertNotNull( get_post( $other_font_face_id ), 'The other post should exist.' );
}
public function test_deleting_font_faces_deletes_associated_font_files() {
@@ -43,8 +46,8 @@ public function test_deleting_font_faces_deletes_associated_font_files() {
wp_delete_post( $font_face_id, true );
- $this->assertFalse( file_exists( $font_path ) );
- $this->assertTrue( file_exists( $other_font_path ) );
+ $this->assertFalse( file_exists( $font_path ), 'The font file should have been deleted when the post was deleted.' );
+ $this->assertTrue( file_exists( $other_font_path ), 'The other font file should exist.' );
}
protected function create_font_face_with_file( $filename ) {
@@ -70,7 +73,7 @@ protected function upload_font_file( $font_filename ) {
// @core-merge Use `DIR_TESTDATA` instead of `GUTENBERG_DIR_TESTDATA`.
$font_file_path = GUTENBERG_DIR_TESTDATA . 'fonts/' . $font_filename;
- add_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) );
+ add_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
add_filter( 'upload_dir', 'wp_get_font_dir' );
$font_file = wp_upload_bits(
$font_filename,
@@ -78,7 +81,7 @@ protected function upload_font_file( $font_filename ) {
file_get_contents( $font_file_path )
);
remove_filter( 'upload_dir', 'wp_get_font_dir' );
- remove_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) );
+ remove_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
return $font_file;
}
diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
index d4b75038a6e7ac..a0693ce3414565 100644
--- a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
+++ b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
@@ -1,6 +1,6 @@
setAccessible( true );
-
- $name = new ReflectionProperty( WP_Font_Collection::class, 'name' );
- $name->setAccessible( true );
-
- $description = new ReflectionProperty( WP_Font_Collection::class, 'description' );
- $description->setAccessible( true );
-
- $src = new ReflectionProperty( WP_Font_Collection::class, 'src' );
- $src->setAccessible( true );
-
- $config = array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
+ public function test_should_do_it_wrong_with_invalid_slug() {
+ $this->setExpectedIncorrectUsage( 'WP_Font_Collection::__construct' );
+ $mock_collection_data = array(
+ 'name' => 'Test Collection',
+ 'font_families' => array( 'mock ' ),
);
- $collection = new WP_Font_Collection( $config );
-
- $actual_slug = $slug->getValue( $collection );
- $this->assertSame( 'my-collection', $actual_slug, 'Provided slug and initialized slug should match.' );
- $slug->setAccessible( false );
-
- $actual_name = $name->getValue( $collection );
- $this->assertSame( 'My Collection', $actual_name, 'Provided name and initialized name should match.' );
- $name->setAccessible( false );
-
- $actual_description = $description->getValue( $collection );
- $this->assertSame( 'My collection description', $actual_description, 'Provided description and initialized description should match.' );
- $description->setAccessible( false );
-
- $actual_src = $src->getValue( $collection );
- $this->assertSame( 'my-collection-data.json', $actual_src, 'Provided src and initialized src should match.' );
- $src->setAccessible( false );
- }
-
- /**
- * @dataProvider data_should_do_it_wrong
- *
- * @param mixed $config Config of the font collection.
- */
- public function test_should_do_it_wrong( $config ) {
- $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid' );
- new WP_Font_Collection( $config );
- }
-
- /**
- * Data provider.
- *
- * @return array
- */
- public function data_should_do_it_wrong() {
- return array(
- 'no id' => array(
- array(
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
- ),
- ),
- 'no config' => array(
- '',
- ),
+ $collection = new WP_Font_Collection( 'slug with spaces', $mock_collection_data );
- 'empty array' => array(
- array(),
- ),
-
- 'boolean instead of config array' => array(
- false,
- ),
-
- 'null instead of config array' => array(
- null,
- ),
-
- 'missing src' => array(
- array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- ),
- ),
- );
+ $this->assertSame( 'slug-with-spaces', $collection->slug, 'Slug is not sanitized.' );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getContent.php b/phpunit/tests/fonts/font-library/wpFontCollection/getContent.php
deleted file mode 100644
index ab0e87cde000e7..00000000000000
--- a/phpunit/tests/fonts/font-library/wpFontCollection/getContent.php
+++ /dev/null
@@ -1,121 +0,0 @@
- array( 'mock' ),
- 'categories' => array( 'mock' ),
- );
-
- return array(
- 'body' => json_encode( $mock_collection_data ),
- 'response' => array(
- 'code' => 200,
- ),
- );
- }
-
- /**
- * @dataProvider data_should_get_content
- *
- * @param array $config Font collection config options.
- * @param array $expected_data Expected output data.
- */
- public function test_should_get_content( $config, $expected_data ) {
- $collection = new WP_Font_Collection( $config );
- $this->assertSame( $expected_data, $collection->get_content() );
- }
-
- /**
- * Data provider.
- *
- * @return array[]
- */
- public function data_should_get_content() {
- $mock_file = wp_tempnam( 'my-collection-data-' );
- file_put_contents( $mock_file, '{"font_families":[ "mock" ], "categories":[ "mock" ] }' );
-
- return array(
- 'with a file' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => $mock_file,
- ),
- 'expected_data' => array(
- 'font_families' => array( 'mock' ),
- 'categories' => array( 'mock' ),
- ),
- ),
- 'with a url' => array(
- 'config' => array(
- 'slug' => 'my-collection-with-url',
- 'name' => 'My Collection with URL',
- 'description' => 'My collection description',
- 'src' => 'https://localhost/fonts/mock-font-collection.json',
- ),
- 'expected_data' => array(
- 'font_families' => array( 'mock' ),
- 'categories' => array( 'mock' ),
- ),
- ),
- 'with font_families and categories' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'font_families' => array( 'mock' ),
- 'categories' => array( 'mock' ),
- ),
- 'expected_data' => array(
- 'font_families' => array( 'mock' ),
- 'categories' => array( 'mock' ),
- ),
- ),
- 'with font_families without categories' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'font_families' => array( 'mock' ),
- ),
- 'expected_data' => array(
- 'font_families' => array( 'mock' ),
- 'categories' => array(),
- ),
- ),
- );
- }
-}
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 00000000000000..1cb16e271db61a
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontCollection/getData.php
@@ -0,0 +1,358 @@
+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( array() ),
+ ),
+ 'expected_data' => array(
+ 'description' => '',
+ 'categories' => array(),
+ 'name' => 'My Collection',
+ 'font_families' => array( array() ),
+ ),
+ ),
+
+ 'font collection with all data' => array(
+ 'slug' => 'my-collection',
+ 'config' => array(
+ 'name' => 'My Collection',
+ 'description' => 'My collection description',
+ 'font_families' => array( array() ),
+ 'categories' => array(),
+ ),
+ 'expected_data' => array(
+ 'description' => 'My collection description',
+ 'categories' => array(),
+ 'name' => 'My Collection',
+ 'font_families' => array( array() ),
+ ),
+ ),
+
+ 'font collection with risky data' => array(
+ 'slug' => 'my-collection',
+ 'config' => array(
+ 'name' => 'My Collection',
+ 'description' => 'My collection description',
+ 'font_families' => array(
+ array(
+ 'font_family_settings' => array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'slug' => 'open-sans',
+ 'name' => 'Open Sans',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Open Sans',
+ 'fontStyle' => 'normal',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/src-as-string.ttf?a=',
+ ),
+ array(
+ 'fontFamily' => 'Open Sans',
+ 'fontStyle' => 'normal',
+ 'fontWeight' => '400',
+ 'src' => array(
+ 'https://example.com/src-as-array.woff2?a=',
+ 'https://example.com/src-as-array.ttf',
+ ),
+ ),
+ ),
+ 'unwanted_property' => 'potentially evil value',
+ ),
+ 'categories' => array( 'sans-serif' ),
+ ),
+ ),
+ 'categories' => array(
+ array(
+ 'name' => 'Mock col',
+ 'slug' => 'mock-col',
+ 'unwanted_property' => 'potentially evil value',
+ ),
+ ),
+ 'unwanted_property' => 'potentially evil value',
+ ),
+ 'expected_data' => array(
+ 'description' => 'My collection description',
+ 'categories' => array(
+ array(
+ 'name' => 'Mock col',
+ 'slug' => 'mock-colalertxss',
+ ),
+ ),
+ 'name' => 'My Collection',
+ 'font_families' => array(
+ array(
+ 'font_family_settings' => array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'slug' => 'open-sans',
+ 'name' => 'Open Sans',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Open Sans',
+ 'fontStyle' => 'normal',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/src-as-string.ttf?a=',
+ ),
+ array(
+ 'fontFamily' => 'Open Sans',
+ 'fontStyle' => 'normal',
+ 'fontWeight' => '400',
+ 'src' => array(
+ 'https://example.com/src-as-array.woff2?a=',
+ 'https://example.com/src-as-array.ttf',
+ ),
+ ),
+ ),
+ ),
+ 'categories' => array( 'sans-serifalertxss' ),
+ ),
+ ),
+ ),
+ ),
+
+ );
+ }
+
+ /**
+ * @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::sanitize_and_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/isConfigValid.php b/phpunit/tests/fonts/font-library/wpFontCollection/isConfigValid.php
deleted file mode 100644
index 9179db9db10a8a..00000000000000
--- a/phpunit/tests/fonts/font-library/wpFontCollection/isConfigValid.php
+++ /dev/null
@@ -1,103 +0,0 @@
-assertTrue( WP_Font_Collection::is_config_valid( $config ) );
- }
-
- public function data_is_config_valid() {
- return array(
- 'with src' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
- ),
- ),
- 'with font families' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'font_families' => array( 'mock' ),
- ),
- ),
-
- );
- }
-
- /**
- * @dataProvider data_is_config_valid_should_call_doing_it_wrong
- *
- * @param mixed $config Config of the font collection.
- */
- public function test_is_config_valid_should_call_doing_it_wrong( $config ) {
- $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid', 'Should call _doing_it_wrong if the config is not valid.' );
- $this->assertFalse( WP_Font_Collection::is_config_valid( $config ), 'Should return false if the config is not valid.' );
- }
-
- public function data_is_config_valid_should_call_doing_it_wrong() {
- return array(
- 'with missing slug' => array(
- 'config' => array(
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
- ),
- ),
- 'with missing name' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
- ),
- ),
- 'with missing src' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- ),
- ),
- 'with both src and font_families' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- 'src' => 'my-collection-data.json',
- 'font_families' => array( 'mock' ),
- ),
- ),
- 'without src or font_families' => array(
- 'config' => array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My collection description',
- ),
- ),
- 'with empty config' => array(
- 'config' => array(),
- ),
- 'without an array' => array(
- 'config' => 'not an array',
- ),
- );
- }
-}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/base.php b/phpunit/tests/fonts/font-library/wpFontLibrary/base.php
index e8d970f5b3d393..135329e5add73a 100644
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/base.php
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/base.php
@@ -7,11 +7,10 @@
*/
abstract class WP_Font_Library_UnitTestCase extends WP_UnitTestCase {
public function reset_font_collections() {
- // Resets the private static property WP_Font_Library::$collections to empty array.
- $reflection = new ReflectionClass( 'WP_Font_Library' );
- $property = $reflection->getProperty( 'collections' );
- $property->setAccessible( true );
- $property->setValue( array() );
+ $collections = WP_Font_Library::get_instance()->get_font_collections();
+ foreach ( $collections as $slug => $collection ) {
+ WP_Font_Library::get_instance()->unregister_font_collection( $slug );
+ }
}
public function set_up() {
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
index 082ca892114659..675efe81aec59b 100644
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php
@@ -13,19 +13,18 @@
class Tests_Fonts_WpFontLibrary_GetFontCollection extends WP_Font_Library_UnitTestCase {
public function test_should_get_font_collection() {
- $my_font_collection_config = array(
- 'slug' => 'my-font-collection',
- 'name' => 'My Font Collection',
- 'description' => 'Demo about how to a font collection to your WordPress Font Library.',
- 'src' => path_join( __DIR__, 'my-font-collection-data.json' ),
+ $mock_collection_data = array(
+ 'name' => 'Test Collection',
+ 'font_families' => array( 'mock' ),
);
- wp_register_font_collection( $my_font_collection_config );
- $font_collection = WP_Font_Library::get_font_collection( 'my-font-collection' );
+
+ wp_register_font_collection( 'my-font-collection', $mock_collection_data );
+ $font_collection = WP_Font_Library::get_instance()->get_font_collection( 'my-font-collection' );
$this->assertInstanceOf( 'WP_Font_Collection', $font_collection );
}
public function test_should_get_no_font_collection_if_the_slug_is_not_registered() {
- $font_collection = WP_Font_Library::get_font_collection( 'not-registered-font-collection' );
+ $font_collection = WP_Font_Library::get_instance()->get_font_collection( 'not-registered-font-collection' );
$this->assertWPError( $font_collection );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
index a405584efccc23..f5ca6389b8ff51 100644
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php
@@ -12,22 +12,21 @@
*/
class Tests_Fonts_WpFontLibrary_GetFontCollections extends WP_Font_Library_UnitTestCase {
public function test_should_get_an_empty_list() {
- $font_collections = WP_Font_Library::get_font_collections();
+ $font_collections = WP_Font_Library::get_instance()->get_font_collections();
$this->assertEmpty( $font_collections, 'Should return an empty array.' );
}
public function test_should_get_mock_font_collection() {
$my_font_collection_config = array(
- 'slug' => 'my-font-collection',
- 'name' => 'My Font Collection',
- 'description' => 'Demo about how to a font collection to your WordPress Font Library.',
- 'src' => path_join( __DIR__, 'my-font-collection-data.json' ),
+ 'name' => 'My Font Collection',
+ 'description' => 'Demo about how to a font collection to your WordPress Font Library.',
+ 'font_families' => array( 'mock' ),
);
- WP_Font_Library::register_font_collection( $my_font_collection_config );
+ WP_Font_Library::get_instance()->register_font_collection( 'my-font-collection', $my_font_collection_config );
- $font_collections = WP_Font_Library::get_font_collections();
- $this->assertNotEmpty( $font_collections, 'Sould return an array of font collections.' );
+ $font_collections = WP_Font_Library::get_instance()->get_font_collections();
+ $this->assertNotEmpty( $font_collections, 'Should return an array of font collections.' );
$this->assertCount( 1, $font_collections, 'Should return an array with one font collection.' );
$this->assertArrayHasKey( 'my-font-collection', $font_collections, 'The array should have the key of the registered font collection id.' );
$this->assertInstanceOf( 'WP_Font_Collection', $font_collections['my-font-collection'], 'The value of the array $font_collections[id] should be an instance of WP_Font_Collection class.' );
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php
deleted file mode 100644
index 579baa2d248e0b..00000000000000
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php
+++ /dev/null
@@ -1,90 +0,0 @@
-assertEquals( $mimes, $expected );
- }
-
- /**
- * Data provider.
- *
- * @return array[]
- */
- public function data_should_supply_correct_mime_type_for_php_version() {
- return array(
- 'version 7.2' => array(
- 'php_version_id' => 70200,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'application/x-font-ttf',
- 'woff' => 'application/font-woff',
- 'woff2' => 'application/font-woff2',
- ),
- ),
- 'version 7.3' => array(
- 'php_version_id' => 70300,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'application/font-sfnt',
- 'woff' => 'application/font-woff',
- 'woff2' => 'application/font-woff2',
- ),
- ),
- 'version 7.4' => array(
- 'php_version_id' => 70400,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'font/sfnt',
- 'woff' => 'application/font-woff',
- 'woff2' => 'application/font-woff2',
- ),
- ),
- 'version 8.0' => array(
- 'php_version_id' => 80000,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'font/sfnt',
- 'woff' => 'application/font-woff',
- 'woff2' => 'application/font-woff2',
- ),
- ),
- 'version 8.1' => array(
- 'php_version_id' => 80100,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'font/sfnt',
- 'woff' => 'font/woff',
- 'woff2' => 'font/woff2',
- ),
- ),
- 'version 8.2' => array(
- 'php_version_id' => 80200,
- 'expected' => array(
- 'otf' => 'application/vnd.ms-opentype',
- 'ttf' => 'font/sfnt',
- 'woff' => 'font/woff',
- 'woff2' => 'font/woff2',
- ),
- ),
- );
- }
-}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
index b06ae3c8d53548..d3b0f126e2e7e1 100644
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php
@@ -11,69 +11,30 @@
* @covers WP_Font_Library::register_font_collection
*/
class Tests_Fonts_WpFontLibrary_RegisterFontCollection extends WP_Font_Library_UnitTestCase {
-
public function test_should_register_font_collection() {
- $config = array(
- 'slug' => 'my-collection',
- 'name' => 'My Collection',
- 'description' => 'My Collection Description',
- 'src' => 'my-collection-data.json',
- );
- $collection = WP_Font_Library::register_font_collection( $config );
- $this->assertInstanceOf( 'WP_Font_Collection', $collection );
- }
-
- public function test_should_return_error_if_slug_is_missing() {
$config = array(
- 'name' => 'My Collection',
- 'description' => 'My Collection Description',
- 'src' => 'my-collection-data.json',
+ 'name' => 'My Collection',
+ 'font_families' => array( 'mock' ),
);
- $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid' );
- $collection = WP_Font_Library::register_font_collection( $config );
- $this->assertWPError( $collection, 'A WP_Error should be returned.' );
- }
- public function test_should_return_error_if_name_is_missing() {
- $config = array(
- 'slug' => 'my-collection',
- 'description' => 'My Collection Description',
- 'src' => 'my-collection-data.json',
- );
- $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid' );
- $collection = WP_Font_Library::register_font_collection( $config );
- $this->assertWPError( $collection, 'A WP_Error should be returned.' );
- }
-
- public function test_should_return_error_if_config_is_empty() {
- $config = array();
- $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid' );
- $collection = WP_Font_Library::register_font_collection( $config );
- $this->assertWPError( $collection, 'A WP_Error should be returned.' );
+ $collection = WP_Font_Library::get_instance()->register_font_collection( 'my-collection', $config );
+ $this->assertInstanceOf( 'WP_Font_Collection', $collection );
}
public function test_should_return_error_if_slug_is_repeated() {
- $config1 = array(
- 'slug' => 'my-collection-1',
- 'name' => 'My Collection 1',
- 'description' => 'My Collection 1 Description',
- 'src' => 'my-collection-1-data.json',
- );
- $config2 = array(
- 'slug' => 'my-collection-1',
- 'name' => 'My Collection 2',
- 'description' => 'My Collection 2 Description',
- 'src' => 'my-collection-2-data.json',
+ $mock_collection_data = array(
+ 'name' => 'Test Collection',
+ 'font_families' => array( 'mock' ),
);
// Register first collection.
- $collection1 = WP_Font_Library::register_font_collection( $config1 );
+ $collection1 = WP_Font_Library::get_instance()->register_font_collection( 'my-collection-1', $mock_collection_data );
$this->assertInstanceOf( 'WP_Font_Collection', $collection1, 'A collection should be registered.' );
// Expects a _doing_it_wrong notice.
$this->setExpectedIncorrectUsage( 'WP_Font_Library::register_font_collection' );
+
// Try to register a second collection with same slug.
- $collection2 = WP_Font_Library::register_font_collection( $config2 );
- $this->assertWPError( $collection2, 'A WP_Error should be returned.' );
+ WP_Font_Library::get_instance()->register_font_collection( 'my-collection-1', $mock_collection_data );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php
index 3c19a1d2089e7a..ddb0fa91c1d609 100644
--- a/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php
+++ b/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php
@@ -13,32 +13,24 @@
class Tests_Fonts_WpFontLibrary_UnregisterFontCollection extends WP_Font_Library_UnitTestCase {
public function test_should_unregister_font_collection() {
- // Registers two mock font collections.
- $config = array(
- 'slug' => 'mock-font-collection-1',
- 'name' => 'Mock Collection to be unregistered',
- 'description' => 'A mock font collection to be unregistered.',
- 'src' => 'my-collection-data.json',
+ $mock_collection_data = array(
+ 'name' => 'Test Collection',
+ 'font_families' => array( 'mock' ),
);
- WP_Font_Library::register_font_collection( $config );
- $config = array(
- 'slug' => 'mock-font-collection-2',
- 'name' => 'Mock Collection',
- 'description' => 'A mock font collection.',
- 'src' => 'my-mock-data.json',
- );
- WP_Font_Library::register_font_collection( $config );
+ // Registers two mock font collections.
+ WP_Font_Library::get_instance()->register_font_collection( 'mock-font-collection-1', $mock_collection_data );
+ WP_Font_Library::get_instance()->register_font_collection( 'mock-font-collection-2', $mock_collection_data );
// Unregister mock font collection.
- WP_Font_Library::unregister_font_collection( 'mock-font-collection-1' );
- $collections = WP_Font_Library::get_font_collections();
+ WP_Font_Library::get_instance()->unregister_font_collection( 'mock-font-collection-1' );
+ $collections = WP_Font_Library::get_instance()->get_font_collections();
$this->assertArrayNotHasKey( 'mock-font-collection-1', $collections, 'Font collection was not unregistered.' );
$this->assertArrayHasKey( 'mock-font-collection-2', $collections, 'Font collection was unregistered by mistake.' );
// Unregisters remaining mock font collection.
- WP_Font_Library::unregister_font_collection( 'mock-font-collection-2' );
- $collections = WP_Font_Library::get_font_collections();
+ WP_Font_Library::get_instance()->unregister_font_collection( 'mock-font-collection-2' );
+ $collections = WP_Font_Library::get_instance()->get_font_collections();
$this->assertArrayNotHasKey( 'mock-font-collection-2', $collections, 'Mock font collection was not unregistered.' );
// Checks that all font collections were unregistered.
@@ -46,9 +38,9 @@ public function test_should_unregister_font_collection() {
}
public function unregister_non_existing_collection() {
- // Unregisters non existing font collection.
- WP_Font_Library::unregister_font_collection( 'non-existing-collection' );
- $collections = WP_Font_Library::get_font_collections();
- $this->assertEmpty( $collections, 'Should not be registered collections.' );
+ // Unregisters non-existing font collection.
+ WP_Font_Library::get_instance()->unregister_font_collection( 'non-existing-collection' );
+ $collections = WP_Font_Library::get_instance()->get_font_collections();
+ $this->assertEmpty( $collections, 'No collections should be registered.' );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php b/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php
index ded19749b9123e..de0b02e63185ed 100644
--- a/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php
+++ b/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php
@@ -4,12 +4,18 @@
*
* @package WordPress
* @subpackage Font Library
- * *
+ *
+ * @group fonts
+ * @group font-library
+ *
* @covers WP_Font_Utils::get_font_face_slug
*/
class Tests_Fonts_WpFontUtils_GetFontFaceSlug extends WP_UnitTestCase {
/**
* @dataProvider data_get_font_face_slug_normalizes_values
+ *
+ * @param string[] $settings Settings to test.
+ * @param string $expected_slug Expected slug results.
*/
public function test_get_font_face_slug_normalizes_values( $settings, $expected_slug ) {
$slug = WP_Font_Utils::get_font_face_slug( $settings );
@@ -17,6 +23,11 @@ public function test_get_font_face_slug_normalizes_values( $settings, $expected_
$this->assertSame( $expected_slug, $slug );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_get_font_face_slug_normalizes_values() {
return array(
'Sets defaults' => array(
diff --git a/phpunit/tests/fonts/font-library/wpFontUtils/formatFontFamily.php b/phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFontFamily.php
similarity index 58%
rename from phpunit/tests/fonts/font-library/wpFontUtils/formatFontFamily.php
rename to phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFontFamily.php
index 53eaced4875207..71511331c65dcb 100644
--- a/phpunit/tests/fonts/font-library/wpFontUtils/formatFontFamily.php
+++ b/phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFontFamily.php
@@ -1,6 +1,6 @@
assertSame(
$expected,
- WP_Font_Utils::format_font_family(
+ WP_Font_Utils::sanitize_font_family(
$font_family
)
);
@@ -30,9 +30,9 @@ public function test_should_format_font_family( $font_family, $expected ) {
/**
* Data provider.
*
- * @return array[]
+ * @return array
*/
- public function data_should_format_font_family() {
+ public function data_should_sanitize_font_family() {
return array(
'data_families_with_spaces_and_numbers' => array(
'font_family' => 'Rock 3D , Open Sans,serif',
@@ -54,6 +54,10 @@ public function data_should_format_font_family() {
'font_family' => ' ',
'expected' => '',
),
+ 'data_font_family_with_whitespace_tags_new_lines' => array(
+ 'font_family' => " Rock 3D\n ",
+ 'expected' => '"Rock 3D"',
+ ),
);
}
}
diff --git a/phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFromSchema.php b/phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFromSchema.php
new file mode 100644
index 00000000000000..88983fe15a14ec
--- /dev/null
+++ b/phpunit/tests/fonts/font-library/wpFontUtils/sanitizeFromSchema.php
@@ -0,0 +1,310 @@
+assertSame( $result, $expected );
+ }
+
+ public function data_sanitize_from_schema() {
+ return array(
+ 'One level associative array' => array(
+ 'data' => array(
+ 'slug' => 'open - sans',
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json',
+ ),
+ 'schema' => array(
+ 'slug' => 'sanitize_title',
+ 'fontFamily' => 'sanitize_text_field',
+ 'src' => 'sanitize_url',
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sansalertxss',
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ ),
+ ),
+
+ 'Nested associative arrays' => array(
+ 'data' => array(
+ 'slug' => 'open - sans',
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json',
+ 'nested' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'nested2' => array(
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ),
+ ),
+ ),
+ 'schema' => array(
+ 'slug' => 'sanitize_title',
+ 'fontFamily' => 'sanitize_text_field',
+ 'src' => 'sanitize_url',
+ 'nested' => array(
+ 'key1' => 'sanitize_text_field',
+ 'key2' => 'sanitize_text_field',
+ 'nested2' => array(
+ 'key3' => 'sanitize_text_field',
+ 'key4' => 'sanitize_text_field',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sansalertxss',
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ 'nested' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'nested2' => array(
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ),
+ ),
+ ),
+ ),
+
+ 'Indexed arrays' => array(
+ 'data' => array(
+ 'slug' => 'oPeN SaNs',
+ 'enum' => array(
+ 'value1',
+ 'value2',
+ 'value3',
+ ),
+ ),
+ 'schema' => array(
+ 'slug' => 'sanitize_title',
+ 'enum' => array( 'sanitize_text_field' ),
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sans',
+ 'enum' => array( 'value1', 'value2', 'value3' ),
+ ),
+ ),
+
+ 'Nested indexed arrays' => array(
+ 'data' => array(
+ 'slug' => 'OPEN-SANS',
+ 'name' => 'Open Sans',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ ),
+ array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ ),
+ ),
+ ),
+ 'schema' => array(
+ 'slug' => 'sanitize_title',
+ 'name' => 'sanitize_text_field',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'sanitize_text_field',
+ 'src' => 'sanitize_url',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sans',
+ 'name' => 'Open Sans',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ ),
+ array(
+ 'fontFamily' => 'Open Sans, sans-serif',
+ 'src' => 'https://wordpress.org/example.json/stylescriptalert(xss)/script',
+ ),
+ ),
+ ),
+ ),
+
+ 'Custom sanitization function' => array(
+ 'data' => array(
+ 'key1' => 'abc123edf456ghi789',
+ 'key2' => 'value2',
+ ),
+ 'schema' => array(
+ 'key1' => function ( $value ) {
+ // Remove the six first character.
+ return substr( $value, 6 );
+ },
+ 'key2' => function ( $value ) {
+ // Capitalize the value.
+ return strtoupper( $value );
+ },
+ ),
+ 'expected' => array(
+ 'key1' => 'edf456ghi789',
+ 'key2' => 'VALUE2',
+ ),
+ ),
+
+ 'Null as schema value' => array(
+ 'data' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'nested' => array(
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ),
+ ),
+ 'schema' => array(
+ 'key1' => null,
+ 'key2' => 'sanitize_text_field',
+ 'nested' => null,
+ ),
+ 'expected' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'nested' => array(
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ),
+ ),
+ ),
+
+ 'Keys to remove' => array(
+ 'data' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'unwanted1' => 'value',
+ 'unwanted2' => 'value',
+ 'nestedAssociative' => array(
+ 'key5' => 'value5',
+ 'unwanted3' => 'value',
+ ),
+ 'nestedIndexed' => array(
+ array(
+ 'key6' => 'value7',
+ 'unwanted4' => 'value',
+ ),
+ array(
+ 'key6' => 'value7',
+ 'unwanted5' => 'value',
+ ),
+ ),
+
+ ),
+ 'schema' => array(
+ 'key1' => 'sanitize_text_field',
+ 'key2' => 'sanitize_text_field',
+ 'nestedAssociative' => array(
+ 'key5' => 'sanitize_text_field',
+ ),
+ 'nestedIndexed' => array(
+ array(
+ 'key6' => 'sanitize_text_field',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'nestedAssociative' => array(
+ 'key5' => 'value5',
+ ),
+ 'nestedIndexed' => array(
+ array(
+ 'key6' => 'value7',
+ ),
+ array(
+ 'key6' => 'value7',
+ ),
+ ),
+ ),
+ ),
+
+ 'With empty structure' => array(
+ 'data' => array(
+ 'slug' => 'open-sans',
+ 'nested' => array(
+ 'key1' => 'value',
+ 'nested2' => array(
+ 'key2' => 'value',
+ 'nested3' => array(
+ 'nested4' => array(),
+ ),
+ ),
+ ),
+ ),
+ 'schema' => array(
+ 'slug' => 'sanitize_title',
+ 'nested' => array(
+ 'key1' => 'sanitize_text_field',
+ 'nested2' => array(
+ 'key2' => 'sanitize_text_field',
+ 'nested3' => array(
+ 'key3' => 'sanitize_text_field',
+ 'nested4' => array(
+ 'key4' => 'sanitize_text_field',
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sans',
+ 'nested' => array(
+ 'key1' => 'value',
+ 'nested2' => array(
+ 'key2' => 'value',
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function test_sanitize_from_schema_with_invalid_data() {
+ $data = 'invalid data';
+ $schema = array(
+ 'key1' => 'sanitize_text_field',
+ 'key2' => 'sanitize_text_field',
+ );
+
+ $result = WP_Font_Utils::sanitize_from_schema( $data, $schema );
+
+ $this->assertSame( $result, array() );
+ }
+
+
+ public function test_sanitize_from_schema_with_invalid_schema() {
+ $data = array(
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ );
+ $schema = 'invalid schema';
+
+ $result = WP_Font_Utils::sanitize_from_schema( $data, $schema );
+
+ $this->assertSame( $result, array() );
+ }
+}
diff --git a/phpunit/tests/fonts/font-library/fontsDir.php b/phpunit/tests/fonts/font-library/wpFontsDir.php
similarity index 80%
rename from phpunit/tests/fonts/font-library/fontsDir.php
rename to phpunit/tests/fonts/font-library/wpFontsDir.php
index 5c13f1d120f9a5..a8f79888315bdf 100644
--- a/phpunit/tests/fonts/font-library/fontsDir.php
+++ b/phpunit/tests/fonts/font-library/wpFontsDir.php
@@ -8,14 +8,15 @@
* @group fonts
* @group font-library
*
- * @covers wp_get_font_dir
+ * @covers ::wp_get_font_dir
*/
class Tests_Fonts_WpFontDir extends WP_UnitTestCase {
- private $dir_defaults;
+ private static $dir_defaults;
- public function __construct() {
- parent::__construct();
- $this->dir_defaults = array(
+ public static function set_up_before_class() {
+ parent::set_up_before_class();
+
+ static::$dir_defaults = array(
'path' => path_join( WP_CONTENT_DIR, 'fonts' ),
'url' => content_url( 'fonts' ),
'subdir' => '',
@@ -27,7 +28,8 @@ public function __construct() {
public function test_fonts_dir() {
$font_dir = wp_get_font_dir();
- $this->assertEquals( $font_dir, $this->dir_defaults );
+
+ $this->assertSame( $font_dir, static::$dir_defaults );
}
public function test_fonts_dir_with_filter() {
@@ -57,14 +59,14 @@ function set_new_values( $defaults ) {
'error' => false,
);
- $this->assertEquals( $font_dir, $expected, 'The wp_get_font_dir() method should return the expected values.' );
-
// Remove the filter.
remove_filter( 'font_dir', 'set_new_values' );
+ $this->assertSame( $expected, $font_dir, 'The wp_get_font_dir() method should return the expected values.' );
+
// Gets the fonts dir.
$font_dir = wp_get_font_dir();
- $this->assertEquals( $font_dir, $this->dir_defaults, 'The wp_get_font_dir() method should return the default values.' );
+ $this->assertSame( static::$dir_defaults, $font_dir, 'The wp_get_font_dir() method should return the default values.' );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php
index f2140267672d2b..60f50e503fdbe4 100644
--- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php
+++ b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php
@@ -7,10 +7,12 @@
* @since 6.5.0
*
* @group restapi
+ * @group fonts
+ * @group font-library
*
* @coversDefaultClass WP_REST_Font_Collections_Controller
*/
-class WP_REST_Font_Collections_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class Tests_REST_WpRestFontCollectionsController extends WP_Test_REST_Controller_Testcase {
protected static $admin_id;
protected static $editor_id;
protected static $mock_file;
@@ -28,15 +30,9 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
)
);
$mock_file = wp_tempnam( 'my-collection-data-' );
- file_put_contents( $mock_file, '{"font_families": [ "mock" ], "categories": [ "mock" ] }' );
+ file_put_contents( $mock_file, '{"name": "Mock Collection", "font_families": [ "mock" ], "categories": [ "mock" ] }' );
- wp_register_font_collection(
- array(
- 'name' => 'My Collection',
- 'slug' => 'mock-col-slug',
- 'src' => $mock_file,
- )
- );
+ wp_register_font_collection( 'mock-col-slug', $mock_file );
}
public static function wpTearDownAfterClass() {
@@ -45,7 +41,6 @@ public static function wpTearDownAfterClass() {
wp_unregister_font_collection( 'mock-col-slug' );
}
-
/**
* @covers WP_REST_Font_Collections_Controller::register_routes
*/
@@ -54,8 +49,8 @@ public function test_register_routes() {
$this->assertCount( 1, $routes['/wp/v2/font-collections'], 'Rest server has not the collections path initialized.' );
$this->assertCount( 1, $routes['/wp/v2/font-collections/(?P[\/\w-]+)'], 'Rest server has not the collection path initialized.' );
- $this->assertArrayHasKey( 'GET', $routes['/wp/v2/font-collections'][0]['methods'], 'Rest server has not the GET method for collections intialized.' );
- $this->assertArrayHasKey( 'GET', $routes['/wp/v2/font-collections/(?P[\/\w-]+)'][0]['methods'], 'Rest server has not the GET method for collection intialized.' );
+ $this->assertArrayHasKey( 'GET', $routes['/wp/v2/font-collections'][0]['methods'], 'Rest server has not the GET method for collections initialized.' );
+ $this->assertArrayHasKey( 'GET', $routes['/wp/v2/font-collections/(?P[\/\w-]+)'][0]['methods'], 'Rest server has not the GET method for collection initialized.' );
}
/**
@@ -67,7 +62,26 @@ public function test_get_items() {
$response = rest_get_server()->dispatch( $request );
$content = $response->get_data();
$this->assertIsArray( $content );
- $this->assertEquals( 200, $response->get_status() );
+ $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.' );
}
/**
@@ -77,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->assertEquals( 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.' );
@@ -104,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
*/
@@ -112,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.' );
}
/**
@@ -159,13 +191,13 @@ public function test_get_item_schema() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$properties = $data['schema']['properties'];
- $this->assertCount( 5, $properties );
- $this->assertArrayHasKey( 'slug', $properties );
- $this->assertArrayHasKey( 'name', $properties );
- $this->assertArrayHasKey( 'description', $properties );
- $this->assertArrayHasKey( 'font_families', $properties );
- $this->assertArrayHasKey( 'categories', $properties );
+ $this->assertCount( 5, $properties, 'There should be 5 properties in the response data schema.' );
+ $this->assertArrayHasKey( 'slug', $properties, 'The slug property should exist in the response data schema.' );
+ $this->assertArrayHasKey( 'name', $properties, 'The name property should exist in the response data schema.' );
+ $this->assertArrayHasKey( 'description', $properties, 'The description property should exist in the response data schema.' );
+ $this->assertArrayHasKey( 'font_families', $properties, 'The slug font_families should exist in the response data schema.' );
+ $this->assertArrayHasKey( 'categories', $properties, 'The categories property should exist in the response data schema.' );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php
index d6a95814b205a2..273862e3047e88 100644
--- a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php
+++ b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php
@@ -7,10 +7,12 @@
* @since 6.5.0
*
* @group restapi
+ * @group fonts
+ * @group font-library
*
- * @coversDefaultClass WP_REST_Font_Faces_Controller_Test
+ * @coversDefaultClass WP_REST_Font_Faces_Controller
*/
-class WP_REST_Font_Faces_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class Tests_REST_WpRestFontFacesController extends WP_Test_REST_Controller_Testcase {
protected static $admin_id;
protected static $editor_id;
@@ -20,6 +22,8 @@ class WP_REST_Font_Faces_Controller_Test extends WP_Test_REST_Controller_Testcas
protected static $font_face_id1;
protected static $font_face_id2;
+ private static $post_ids_for_cleanup = array();
+
protected static $default_settings = array(
'fontFamily' => '"Open Sans"',
'fontWeight' => '400',
@@ -28,8 +32,8 @@ class WP_REST_Font_Faces_Controller_Test extends WP_Test_REST_Controller_Testcas
);
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
- self::$font_family_id = WP_REST_Font_Families_Controller_Test::create_font_family_post();
- self::$other_font_family_id = WP_REST_Font_Families_Controller_Test::create_font_family_post();
+ self::$font_family_id = Tests_REST_WpRestFontFamiliesController::create_font_family_post();
+ self::$other_font_family_id = Tests_REST_WpRestFontFamiliesController::create_font_family_post();
self::$font_face_id1 = self::create_font_face_post(
self::$font_family_id,
@@ -60,17 +64,32 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
'role' => 'editor',
)
);
+
+ self::$post_ids_for_cleanup = array();
}
public static function wpTearDownAfterClass() {
self::delete_user( self::$admin_id );
self::delete_user( self::$editor_id );
+
+ wp_delete_post( self::$font_family_id, true );
+ wp_delete_post( self::$other_font_family_id, true );
+ wp_delete_post( self::$font_face_id1, true );
+ wp_delete_post( self::$font_face_id2, true );
+ }
+
+ public function tear_down() {
+ foreach ( self::$post_ids_for_cleanup as $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
+ self::$post_ids_for_cleanup = array();
+ parent::tear_down();
}
public static function create_font_face_post( $parent_id, $settings = array() ) {
$settings = array_merge( self::$default_settings, $settings );
$title = WP_Font_Utils::get_font_face_slug( $settings );
- return self::factory()->post->create(
+ $post_id = self::factory()->post->create(
wp_slash(
array(
'post_type' => 'wp_font_face',
@@ -82,6 +101,10 @@ public static function create_font_face_post( $parent_id, $settings = array() )
)
)
);
+
+ self::$post_ids_for_cleanup[] = $post_id;
+
+ return $post_id;
}
/**
@@ -128,24 +151,45 @@ public function test_font_faces_no_autosave_routes() {
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_context_param
+ * @doesNotPerformAssertions
*/
public function test_context_param() {
- // Collection.
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
- $response = rest_get_server()->dispatch( $request );
- $data = $response->get_data();
- $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
- $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
- $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
+ // See test_get_context_param().
+ }
- // Single.
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id1 );
+ /**
+ * @dataProvider data_get_context_param
+ *
+ * @covers WP_REST_Font_Faces_Controller::get_context_param
+ *
+ * @param bool $single_route Whether to test a single route.
+ */
+ public function test_get_context_param( $single_route ) {
+ $route = '/wp/v2/font-families/' . self::$font_family_id . '/font-faces';
+ if ( $single_route ) {
+ $route .= '/' . self::$font_face_id1;
+ }
+
+ $request = new WP_REST_Request( 'OPTIONS', $route );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
- $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
- $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
+
+ $endpoint_data = $data['endpoints'][0];
+ $this->assertArrayNotHasKey( 'allow_batch', $endpoint_data, 'The allow_batch property should not exist in the endpoint data.' );
+ $this->assertSame( 'view', $endpoint_data['args']['context']['default'], 'The endpoint\'s args::context::default should be set to view.' );
+ $this->assertSame( array( 'view', 'embed', 'edit' ), $endpoint_data['args']['context']['enum'], 'The endpoint\'s args::context::enum should be set to [ view, embed, edit ].' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_get_context_param() {
+ return array(
+ 'Collection' => array( false ),
+ 'Single' => array( true ),
+ );
}
/**
@@ -157,11 +201,11 @@ public function test_get_items() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertCount( 2, $data );
- $this->assertArrayHasKey( '_links', $data[0] );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200' );
+ $this->assertCount( 2, $data, 'There should be 2 properties in the response data.' );
+ $this->assertArrayHasKey( '_links', $data[0], 'The _links property should exist in the response data 0.' );
$this->check_font_face_data( $data[0], self::$font_face_id2, $data[0]['_links'] );
- $this->assertArrayHasKey( '_links', $data[1] );
+ $this->assertArrayHasKey( '_links', $data[1], 'The _links property should exist in the response data 1.' );
$this->check_font_face_data( $data[1], self::$font_face_id1, $data[1]['_links'] );
}
@@ -172,11 +216,11 @@ public function test_get_items_no_permission() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response should return an error with a "rest_cannot_read" code and 401 status.' );
wp_set_current_user( self::$editor_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response should return an error with a "rest_cannot_read" code and 403 status.' );
}
/**
@@ -198,7 +242,7 @@ public function test_get_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->check_font_face_data( $data, self::$font_face_id1, $response->get_links() );
}
@@ -213,8 +257,9 @@ public function test_get_item_removes_extra_settings() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertArrayNotHasKey( 'extra', $data['font_face_settings'] );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertArrayHasKey( 'font_face_settings', $data, 'The font_face_settings property should exist in the response data.' );
+ $this->assertArrayNotHasKey( 'extra', $data['font_face_settings'], 'The extra property should exist in the font_face_settings data.' );
}
/**
@@ -230,6 +275,8 @@ public function test_get_item_malformed_post_content_returns_empty_settings() {
)
);
+ self::$post_ids_for_cleanup[] = $font_face_id;
+
$empty_settings = array(
'fontFamily' => '',
'src' => array(),
@@ -240,10 +287,9 @@ public function test_get_item_malformed_post_content_returns_empty_settings() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertSame( $empty_settings, $data['font_face_settings'] );
-
- wp_delete_post( $font_face_id, true );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertArrayHasKey( 'font_face_settings', $data, 'The font_face_settings property should exist in the response data.' );
+ $this->assertSame( $empty_settings, $data['font_face_settings'], 'The empty settings should exist in the font_face_settings data.' );
}
/**
@@ -264,11 +310,11 @@ public function test_get_item_no_permission() {
$request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id1 );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response should return an error with a "rest_cannot_read" code and 401 status.' );
wp_set_current_user( self::$editor_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response should return an error with a "rest_cannot_read" code and 403 status.' );
}
/**
@@ -291,7 +337,7 @@ public function test_get_item_valid_parent_id() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->assertSame( self::$font_family_id, $data['parent'], 'The returned parent id should match the font family id.' );
}
@@ -333,24 +379,23 @@ public function test_create_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
$this->check_font_face_data( $data, $data['id'], $response->get_links() );
$this->check_file_meta( $data['id'], array( $data['font_face_settings']['src'] ) );
$settings = $data['font_face_settings'];
unset( $settings['src'] );
$this->assertSame(
- $settings,
array(
'fontFamily' => '"Open Sans"',
'fontWeight' => '200',
'fontStyle' => 'normal',
- )
+ ),
+ $settings,
+ 'The font_face_settings data should match the expected data.'
);
$this->assertSame( self::$font_family_id, $data['parent'], 'The returned parent id should match the font family id.' );
-
- wp_delete_post( $data['id'], true );
}
/**
@@ -378,14 +423,12 @@ public function test_create_item_with_multiple_font_files() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
$this->check_font_face_data( $data, $data['id'], $response->get_links() );
$this->check_file_meta( $data['id'], $data['font_face_settings']['src'] );
$settings = $data['font_face_settings'];
- $this->assertCount( 4, $settings['src'] );
-
- wp_delete_post( $data['id'], true );
+ $this->assertCount( 4, $settings['src'], 'There should be 4 items in the font_face_settings::src data.' );
}
/**
@@ -451,10 +494,8 @@ public function test_create_item_with_url_src() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
$this->check_font_face_data( $data, $data['id'], $response->get_links() );
-
- wp_delete_post( $data['id'], true );
}
/**
@@ -487,12 +528,11 @@ public function test_create_item_with_all_properties() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
-
- $this->assertSame( 201, $response->get_status() );
- $this->assertArrayHasKey( 'font_face_settings', $data );
- $this->assertSame( $properties, $data['font_face_settings'] );
-
wp_delete_post( $data['id'], true );
+
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $this->assertArrayHasKey( 'font_face_settings', $data, 'The font_face_settings property should exist in the response data.' );
+ $this->assertSame( $properties, $data['font_face_settings'], 'The font_face_settings should match the expected properties.' );
}
/**
@@ -514,13 +554,13 @@ public function test_create_item_missing_parent() {
* @covers WP_REST_Font_Faces_Controller::create_item
*/
public function test_create_item_with_duplicate_properties() {
- $settings = array(
+ $settings = array(
'fontFamily' => '"Open Sans"',
'fontWeight' => '200',
'fontStyle' => 'italic',
'src' => home_url( '/wp-content/fonts/open-sans-italic-light.ttf' ),
);
- $font_face_id = self::create_font_face_post( self::$font_family_id, $settings );
+ self::create_font_face_post( self::$font_family_id, $settings );
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
@@ -528,12 +568,10 @@ public function test_create_item_with_duplicate_properties() {
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_duplicate_font_face', $response, 400 );
+ $this->assertErrorResponse( 'rest_duplicate_font_face', $response, 400, 'The response should return an error for "rest_duplicate_font_face" with 400 status.' );
$expected_message = 'A font face matching those settings already exists.';
$message = $response->as_error()->get_error_messages()[0];
- $this->assertSame( $expected_message, $message );
-
- wp_delete_post( $font_face_id, true );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
@@ -555,18 +593,19 @@ public function test_create_item_default_theme_json_version() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
+ wp_delete_post( $data['id'], true );
- $this->assertSame( 201, $response->get_status() );
- $this->assertArrayHasKey( 'theme_json_version', $data );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $this->assertArrayHasKey( 'theme_json_version', $data, 'The theme_json_version property should exist in the response data.' );
$this->assertSame( 2, $data['theme_json_version'], 'The default theme.json version should be 2.' );
-
- wp_delete_post( $data['id'], true );
}
/**
* @dataProvider data_create_item_invalid_theme_json_version
*
* @covers WP_REST_Font_Faces_Controller::create_item
+ *
+ * @param int $theme_json_version Version input to test.
*/
public function test_create_item_invalid_theme_json_version( $theme_json_version ) {
wp_set_current_user( self::$admin_id );
@@ -578,6 +617,11 @@ public function test_create_item_invalid_theme_json_version( $theme_json_version
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_create_item_invalid_theme_json_version() {
return array(
array( 1 ),
@@ -589,6 +633,8 @@ public function data_create_item_invalid_theme_json_version() {
* @dataProvider data_create_item_invalid_settings
*
* @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings
+ *
+ * @param mixed $settings Settings to test.
*/
public function test_create_item_invalid_settings( $settings ) {
wp_set_current_user( self::$admin_id );
@@ -601,6 +647,11 @@ public function test_create_item_invalid_settings( $settings ) {
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_create_item_invalid_settings() {
return array(
'Missing fontFamily' => array(
@@ -647,10 +698,10 @@ public function test_create_item_invalid_settings_json() {
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
+ $this->assertErrorResponse( 'rest_invalid_param', $response, 400, 'The response should return an error for "rest_invalid_param" with 400 status.' );
$expected_message = 'font_face_settings parameter must be a valid JSON string.';
$message = $response->as_error()->get_all_error_data()[0]['params']['font_face_settings'];
- $this->assertSame( $expected_message, $message );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
@@ -660,47 +711,138 @@ public function test_create_item_invalid_file_src() {
$files = $this->setup_font_file_upload( array( 'woff2' ) );
wp_set_current_user( self::$admin_id );
+ $src = 'invalid';
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
$request->set_param( 'theme_json_version', 2 );
$request->set_param(
'font_face_settings',
wp_json_encode(
- array_merge( self::$default_settings, array( 'src' => 'invalid' ) )
+ array_merge( self::$default_settings, array( 'src' => $src ) )
)
);
$request->set_file_params( $files );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
- $expected_message = 'File ' . array_keys( $files )[0] . ' must be used in font_face_settings[src].';
+ $this->assertErrorResponse( 'rest_invalid_param', $response, 400, 'The response should return an error for "rest_invalid_param" with 400 status.' );
+ $expected_message = 'font_face_settings[src] value "' . $src . '" must be a valid URL or file reference.';
+ $message = $response->as_error()->get_all_error_data()[0]['params']['font_face_settings'];
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
+ }
+
+ /**
+ * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings
+ */
+ public function test_create_item_missing_file_src() {
+ $files = $this->setup_font_file_upload( array( 'woff2', 'woff' ) );
+
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
+ $request->set_param( 'theme_json_version', 2 );
+ $request->set_param(
+ 'font_face_settings',
+ wp_json_encode(
+ array_merge( self::$default_settings, array( 'src' => array( array_keys( $files )[0] ) ) )
+ )
+ );
+ $request->set_file_params( $files );
+
+ $response = rest_get_server()->dispatch( $request );
+
+ $this->assertErrorResponse( 'rest_invalid_param', $response, 400, 'The response should return an error for "rest_invalid_param" with 400 status.' );
+ $expected_message = 'File ' . array_keys( $files )[1] . ' must be used in font_face_settings[src].';
$message = $response->as_error()->get_all_error_data()[0]['params']['font_face_settings'];
- $this->assertSame( $expected_message, $message );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
- * @dataProvider data_create_item_santize_font_family
+ * @dataProvider data_sanitize_font_face_settings
*
* @covers WP_REST_Font_Face_Controller::sanitize_font_face_settings
+ *
+ * @param string $settings Settings to test.
+ * @param string $expected Expected settings result.
*/
- public function test_create_item_santize_font_family( $font_family_setting, $expected ) {
- $settings = array_merge( self::$default_settings, array( 'fontFamily' => $font_family_setting ) );
+ public function test_create_item_sanitize_font_face_settings( $settings, $expected ) {
+ $settings = array_merge( self::$default_settings, $settings );
+ $expected = array_merge( self::$default_settings, $expected );
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' );
$request->set_param( 'font_face_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
+ wp_delete_post( $data['id'], true );
- $this->assertSame( 201, $response->get_status() );
- $this->assertSame( $expected, $data['font_face_settings']['fontFamily'] );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $this->assertSame( $expected, $data['font_face_settings'], 'The response font_face_settings should match.' );
}
- public function data_create_item_santize_font_family() {
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_sanitize_font_face_settings() {
return array(
- array( 'Libre Barcode 128 Text', '"Libre Barcode 128 Text"' ),
- array( 'B612 Mono', '"B612 Mono"' ),
- array( 'Open Sans, Noto Sans, sans-serif', '"Open Sans", "Noto Sans", sans-serif' ),
+ 'settings with tags, extra whitespace, new lines' => array(
+ 'settings' => array(
+ 'fontFamily' => " Open Sans\n ",
+ 'fontStyle' => " oblique 20deg 50deg\n ",
+ 'fontWeight' => " 200\n ",
+ 'src' => " https://example.com/ ",
+ 'fontStretch' => " expanded\n ",
+ 'ascentOverride' => " 70%\n ",
+ 'descentOverride' => " 30%\n ",
+ 'fontVariant' => " normal\n ",
+ 'fontFeatureSettings' => " \"swsh\" 2\n ",
+ 'fontVariationSettings' => " \"xhgt\" 0.7\n ",
+ 'lineGapOverride' => " 10%\n ",
+ 'sizeAdjust' => " 90%\n ",
+ 'unicodeRange' => " U+0025-00FF, U+4??\n ",
+ 'preview' => " https://example.com/ ",
+ ),
+ 'expected' => array(
+ 'fontFamily' => '"Open Sans"',
+ 'fontStyle' => 'oblique 20deg 50deg',
+ 'fontWeight' => '200',
+ 'src' => 'https://example.com//stylescriptalert(\'XSS\');/script%20%20%20%20%20%20',
+ 'fontStretch' => 'expanded',
+ 'ascentOverride' => '70%',
+ 'descentOverride' => '30%',
+ 'fontVariant' => 'normal',
+ 'fontFeatureSettings' => '"swsh" 2',
+ 'fontVariationSettings' => '"xhgt" 0.7',
+ 'lineGapOverride' => '10%',
+ 'sizeAdjust' => '90%',
+ 'unicodeRange' => 'U+0025-00FF, U+4??',
+ 'preview' => 'https://example.com//stylescriptalert(\'XSS\');/script%20%20%20%20%20%20',
+ ),
+ ),
+ 'multiword font family name with integer' => array(
+ 'settings' => array(
+ 'fontFamily' => 'Libre Barcode 128 Text',
+ ),
+ 'expected' => array(
+ 'fontFamily' => '"Libre Barcode 128 Text"',
+ ),
+ ),
+ 'multiword font family name' => array(
+ 'settings' => array(
+ 'fontFamily' => 'B612 Mono',
+ ),
+ 'expected' => array(
+ 'fontFamily' => '"B612 Mono"',
+ ),
+ ),
+ 'comma-separated font family names' => array(
+ 'settings' => array(
+ 'fontFamily' => 'Open Sans, Noto Sans, sans-serif',
+ ),
+ 'expected' => array(
+ 'fontFamily' => '"Open Sans", "Noto Sans", sans-serif',
+ ),
+ ),
);
}
@@ -709,6 +851,9 @@ public function data_create_item_santize_font_family() {
*/
// public function test_create_item_no_permission() {}
+ /**
+ * @covers WP_REST_Font_Faces_Controller::update_item
+ */
public function test_update_item() {
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id1 );
$response = rest_get_server()->dispatch( $request );
@@ -725,8 +870,8 @@ public function test_delete_item() {
$request->set_param( 'force', true );
$response = rest_get_server()->dispatch( $request );
- $this->assertSame( 200, $response->get_status() );
- $this->assertNull( get_post( $font_face_id ) );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 201.' );
+ $this->assertNull( get_post( $font_face_id ), 'The deleted post should not exist.' );
}
/**
@@ -739,15 +884,15 @@ public function test_delete_item_no_trash() {
// Attempt trashing.
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
+ $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501, 'The response should return an error for "rest_trash_not_supported" with 501 status.' );
$request->set_param( 'force', 'false' );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
+ $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501, 'When "force" is false, the response should return an error for "rest_trash_not_supported" with 501 status.' );
// Ensure the post still exists.
$post = get_post( $font_face_id );
- $this->assertNotEmpty( $post );
+ $this->assertNotEmpty( $post, 'The post should still exists.' );
}
/**
@@ -781,7 +926,7 @@ public function test_delete_item_invalid_parent_id() {
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$other_font_family_id . '/font-faces/' . self::$font_face_id1 );
$request->set_param( 'force', true );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_font_face_parent_id_mismatch', $response, 404 );
+ $this->assertErrorResponse( 'rest_font_face_parent_id_mismatch', $response, 404, 'The response should return an error for "rest_font_face_parent_id_mismatch" with 404 status.' );
$expected_message = 'The font face does not belong to the specified font family with id of "' . self::$other_font_family_id . '"';
$this->assertSame( $expected_message, $response->as_error()->get_error_messages()[0], 'The message must contain the correct parent ID.' );
@@ -796,12 +941,12 @@ public function test_delete_item_no_permissions() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_delete', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_delete', $response, 401, 'The response should return an error for "rest_cannot_delete" with 401 status for an invalid user.' );
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_delete', $response, 403, 'The response should return an error for "rest_cannot_delete" with 403 status for a user without permission.' );
}
/**
@@ -813,7 +958,7 @@ public function test_prepare_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->check_font_face_data( $data, self::$font_face_id2, $response->get_links() );
}
@@ -825,41 +970,79 @@ public function test_get_item_schema() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$properties = $data['schema']['properties'];
- $this->assertCount( 4, $properties );
- $this->assertArrayHasKey( 'id', $properties );
- $this->assertArrayHasKey( 'theme_json_version', $properties );
- $this->assertArrayHasKey( 'parent', $properties );
- $this->assertArrayHasKey( 'font_face_settings', $properties );
+ $this->assertCount( 4, $properties, 'There should be 4 properties in the schema::properties data.' );
+ $this->assertArrayHasKey( 'id', $properties, 'The id property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'theme_json_version', $properties, 'The id property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'parent', $properties, 'The id property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'font_face_settings', $properties, 'The id property should exist in the schema::properties data.' );
+ }
+
+ /**
+ * @covers WP_REST_Font_Faces_Controller::get_item_schema
+ */
+ public function test_get_item_schema_font_face_settings_should_all_have_sanitize_callbacks() {
+ $schema = ( new WP_REST_Font_Faces_Controller( 'wp_font_face' ) )->get_item_schema();
+ $font_face_settings_schema = $schema['properties']['font_face_settings'];
+
+ $this->assertArrayHasKey( 'properties', $font_face_settings_schema, 'font_face_settings schema is missing properties.' );
+ $this->assertIsArray( $font_face_settings_schema['properties'], 'font_face_settings properties should be an array.' );
+
+ // arg_options should be removed for each setting property.
+ foreach ( $font_face_settings_schema['properties'] as $property ) {
+ $this->assertArrayHasKey( 'arg_options', $property, 'Setting schema should have arg_options.' );
+ $this->assertArrayHasKey( 'sanitize_callback', $property['arg_options'], 'Setting schema should have a sanitize_callback.' );
+ $this->assertIsCallable( $property['arg_options']['sanitize_callback'], 'The sanitize_callback value should be callable.' );
+ }
+ }
+
+ /**
+ * @covers WP_REST_Font_Faces_Controller::get_public_item_schema
+ */
+ public function test_get_public_item_schema_should_not_have_arg_options() {
+ $schema = ( new WP_REST_Font_Faces_Controller( 'wp_font_face' ) )->get_public_item_schema();
+ $font_face_settings_schema = $schema['properties']['font_face_settings'];
+
+ $this->assertArrayHasKey( 'properties', $font_face_settings_schema, 'font_face_settings schema is missing properties.' );
+ $this->assertIsArray( $font_face_settings_schema['properties'], 'font_face_settings properties should be an array.' );
+
+ // arg_options should be removed for each setting property.
+ foreach ( $font_face_settings_schema['properties'] as $property ) {
+ $this->assertArrayNotHasKey( 'arg_options', $property, 'arg_options should be removed from the schema for each setting.' );
+ }
}
protected function check_font_face_data( $data, $post_id, $links ) {
- $post = get_post( $post_id );
+ self::$post_ids_for_cleanup[] = $post_id;
+ $post = get_post( $post_id );
- $this->assertArrayHasKey( 'id', $data );
- $this->assertSame( $post->ID, $data['id'] );
+ $this->assertArrayHasKey( 'id', $data, 'The id property should exist in response data.' );
+ $this->assertSame( $post->ID, $data['id'], 'The "id" from the response data should match the post ID.' );
- $this->assertArrayHasKey( 'parent', $data );
- $this->assertSame( $post->post_parent, $data['parent'] );
+ $this->assertArrayHasKey( 'parent', $data, 'The parent property should exist in response data.' );
+ $this->assertSame( $post->post_parent, $data['parent'], 'The "parent" from the response data should match the post parent.' );
- $this->assertArrayHasKey( 'theme_json_version', $data );
- $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'] );
+ $this->assertArrayHasKey( 'theme_json_version', $data, 'The theme_json_version property should exist in response data.' );
+ $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'], 'The "theme_json_version" from the response data should match WP_Theme_JSON::LATEST_SCHEMA.' );
- $this->assertArrayHasKey( 'font_face_settings', $data );
- $this->assertSame( $post->post_content, wp_json_encode( $data['font_face_settings'] ) );
+ $this->assertArrayHasKey( 'font_face_settings', $data, 'The font_face_settings property should exist in response data.' );
+ $this->assertSame( $post->post_content, wp_json_encode( $data['font_face_settings'] ), 'The encoded "font_face_settings" from the response data should match the post content.' );
- $this->assertNotEmpty( $links );
- $this->assertSame( rest_url( 'wp/v2/font-families/' . $post->post_parent . '/font-faces/' . $post->ID ), $links['self'][0]['href'] );
- $this->assertSame( rest_url( 'wp/v2/font-families/' . $post->post_parent . '/font-faces' ), $links['collection'][0]['href'] );
- $this->assertSame( rest_url( 'wp/v2/font-families/' . $post->post_parent ), $links['parent'][0]['href'] );
+ $this->assertNotEmpty( $links, 'The links should not be empty in the response data.' );
+ $expected = rest_url( 'wp/v2/font-families/' . $post->post_parent . '/font-faces/' . $post->ID );
+ $this->assertSame( $expected, $links['self'][0]['href'], 'The links URL from the response data should match the post\'s REST endpoint.' );
+ $expected = rest_url( 'wp/v2/font-families/' . $post->post_parent . '/font-faces' );
+ $this->assertSame( $expected, $links['collection'][0]['href'], 'The links collection URL from the response data should match the REST endpoint.' );
+ $expected = rest_url( 'wp/v2/font-families/' . $post->post_parent );
+ $this->assertSame( $expected, $links['parent'][0]['href'], 'The links for a parent URL from the response data should match the parent\'s REST endpoint.' );
}
- protected function check_file_meta( $font_face_id, $srcs ) {
+ protected function check_file_meta( $font_face_id, $src_attributes ) {
$file_meta = get_post_meta( $font_face_id, '_wp_font_face_file' );
- foreach ( $srcs as $src ) {
- $file_name = basename( $src );
+ foreach ( $src_attributes as $src_attribute ) {
+ $file_name = basename( $src_attribute );
$this->assertContains( $file_name, $file_meta, 'The uploaded font file path should be saved in the post meta.' );
}
}
diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php
index 601ff32e2b6c9e..94ad5eccd7e570 100644
--- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php
+++ b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php
@@ -7,10 +7,12 @@
* @since 6.5.0
*
* @group restapi
+ * @group fonts
+ * @group font-library
*
- * @coversDefaultClass WP_REST_Font_Families_Controller_Test
+ * @coversDefaultClass WP_REST_Font_Families_Controller
*/
-class WP_REST_Font_Families_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class Tests_REST_WpRestFontFamiliesController extends WP_Test_REST_Controller_Testcase {
protected static $admin_id;
protected static $editor_id;
@@ -20,6 +22,8 @@ class WP_REST_Font_Families_Controller_Test extends WP_Test_REST_Controller_Test
protected static $font_face_id1;
protected static $font_face_id2;
+ private static $post_ids_to_cleanup = array();
+
protected static $default_settings = array(
'name' => 'Open Sans',
'slug' => 'open-sans',
@@ -54,7 +58,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
'fontFamily' => 'Helvetica, Arial, sans-serif',
)
);
- self::$font_face_id1 = WP_REST_Font_Faces_Controller_Test::create_font_face_post(
+ self::$font_face_id1 = Tests_REST_WpRestFontFacesController::create_font_face_post(
self::$font_family_id1,
array(
'fontFamily' => '"Open Sans"',
@@ -63,7 +67,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
'src' => home_url( '/wp-content/fonts/open-sans-medium.ttf' ),
)
);
- self::$font_face_id2 = WP_REST_Font_Faces_Controller_Test::create_font_face_post(
+ self::$font_face_id2 = Tests_REST_WpRestFontFacesController::create_font_face_post(
self::$font_family_id1,
array(
'fontFamily' => '"Open Sans"',
@@ -72,16 +76,32 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
'src' => home_url( '/wp-content/fonts/open-sans-bold.ttf' ),
)
);
+
+ static::$post_ids_to_cleanup = array();
}
public static function wpTearDownAfterClass() {
self::delete_user( self::$admin_id );
self::delete_user( self::$editor_id );
+
+ wp_delete_post( self::$font_family_id1 );
+ wp_delete_post( self::$font_family_id2 );
+ wp_delete_post( self::$font_face_id1 );
+ wp_delete_post( self::$font_face_id2 );
+ }
+
+ public function tear_down() {
+ foreach ( static::$post_ids_to_cleanup as $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
+ static::$post_ids_to_cleanup = array();
+
+ parent::tear_down();
}
public static function create_font_family_post( $settings = array() ) {
$settings = array_merge( self::$default_settings, $settings );
- return self::factory()->post->create(
+ $post_id = self::factory()->post->create(
wp_slash(
array(
'post_type' => 'wp_font_family',
@@ -97,10 +117,14 @@ public static function create_font_family_post( $settings = array() ) {
)
)
);
+
+ static::$post_ids_to_cleanup[] = $post_id;
+
+ return $post_id;
}
/**
- * @covers WP_REST_Font_Faces_Controller::register_routes
+ * @covers WP_REST_Font_Families_Controller::register_routes
*/
public function test_register_routes() {
$routes = rest_get_server()->get_routes();
@@ -143,29 +167,49 @@ public function test_font_families_no_autosave_routes() {
}
/**
- * @covers WP_REST_Font_Families_Controller::get_context_param
+ * @doesNotPerformAssertions
*/
public function test_context_param() {
- // Collection.
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families' );
- $response = rest_get_server()->dispatch( $request );
- $data = $response->get_data();
- $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
- $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
- $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
+ // See test_get_context_param().
+ }
- // Single.
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id1 );
+ /**
+ * @dataProvider data_get_context_param
+ *
+ * @covers WP_REST_Font_Families_Controller::get_context_param
+ *
+ * @param bool $single_route Whether to test a single route.
+ */
+ public function test_get_context_param( $single_route ) {
+ $route = '/wp/v2/font-families';
+ if ( $single_route ) {
+ $route .= '/' . self::$font_family_id1;
+ }
+
+ $request = new WP_REST_Request( 'OPTIONS', $route );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
- $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
- $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
+
+ $endpoint_data = $data['endpoints'][0];
+ $this->assertArrayNotHasKey( 'allow_batch', $endpoint_data, 'The allow_batch property should not exist in the endpoint data.' );
+ $this->assertSame( 'view', $endpoint_data['args']['context']['default'], 'The endpoint\'s args::context::default should be set to view.' );
+ $this->assertSame( array( 'view', 'embed', 'edit' ), $endpoint_data['args']['context']['enum'], 'The endpoint\'s args::context::enum should be set to [ view, embed, edit ].' );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_get_context_param() {
+ return array(
+ 'Collection' => array( false ),
+ 'Single' => array( true ),
+ );
+ }
/**
- * @covers WP_REST_Font_Faces_Controller::get_items
+ * @covers WP_REST_Font_Families_Controller::get_items
*/
public function test_get_items() {
wp_set_current_user( self::$admin_id );
@@ -173,16 +217,16 @@ public function test_get_items() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertCount( 2, $data );
- $this->assertArrayHasKey( '_links', $data[0] );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertCount( 2, $data, 'There should be 2 properties in the response data.' );
+ $this->assertArrayHasKey( '_links', $data[0], 'The _links property should exist in the response data 0.' );
$this->check_font_family_data( $data[0], self::$font_family_id2, $data[0]['_links'] );
- $this->assertArrayHasKey( '_links', $data[1] );
+ $this->assertArrayHasKey( '_links', $data[1], 'The _links property should exist in the response data 1.' );
$this->check_font_family_data( $data[1], self::$font_family_id1, $data[1]['_links'] );
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_items
+ * @covers WP_REST_Font_Families_Controller::get_items
*/
public function test_get_items_by_slug() {
$font_family = get_post( self::$font_family_id2 );
@@ -193,28 +237,29 @@ public function test_get_items_by_slug() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertCount( 1, $data );
- $this->assertSame( $font_family->ID, $data[0]['id'] );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertCount( 1, $data, 'There should be 2 properties in the response data.' );
+ $this->assertArrayHasKey( 'id', $data[0], 'The id property should exist in the response data.' );
+ $this->assertSame( $font_family->ID, $data[0]['id'], 'The id should match the expected ID in the response data.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_items
+ * @covers WP_REST_Font_Families_Controller::get_items
*/
public function test_get_items_no_permission() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/wp/v2/font-families' );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response should return an error with a "rest_cannot_read" code and 401 status.' );
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'GET', '/wp/v2/font-families' );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response should return an error with a "rest_cannot_read" code and 403 status.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_item
+ * @covers WP_REST_Font_Families_Controller::get_item
*/
public function test_get_item() {
wp_set_current_user( self::$admin_id );
@@ -222,12 +267,12 @@ public function test_get_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->check_font_family_data( $data, self::$font_family_id1, $response->get_links() );
}
/**
- * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response
+ * @covers WP_REST_Font_Families_Controller::prepare_item_for_response
*/
public function test_get_item_embedded_font_faces() {
wp_set_current_user( self::$admin_id );
@@ -236,19 +281,19 @@ public function test_get_item_embedded_font_faces() {
$response = rest_get_server()->dispatch( $request );
$data = rest_get_server()->response_to_data( $response, true );
- $this->assertSame( 200, $response->get_status() );
- $this->assertArrayHasKey( '_embedded', $data );
- $this->assertArrayHasKey( 'font_faces', $data['_embedded'] );
- $this->assertCount( 2, $data['_embedded']['font_faces'] );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertArrayHasKey( '_embedded', $data, 'The _embedded property should exist in the response data.' );
+ $this->assertArrayHasKey( 'font_faces', $data['_embedded'], 'The font_faces property should exist in _embedded data.' );
+ $this->assertCount( 2, $data['_embedded']['font_faces'], 'There should be 2 font_faces in the _embedded data.' );
foreach ( $data['_embedded']['font_faces'] as $font_face ) {
- $this->assertArrayHasKey( 'id', $font_face );
+ $this->assertArrayHasKey( 'id', $font_face, 'The id property should exist in the _embedded font_face data.' );
$font_face_request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id1 . '/font-faces/' . $font_face['id'] );
$font_face_response = rest_get_server()->dispatch( $font_face_request );
$font_face_data = rest_get_server()->response_to_data( $font_face_response, true );
- $this->assertSame( $font_face_data, $font_face );
+ $this->assertSame( $font_face_data, $font_face, 'The embedded font_face data should match when the data from a single request.' );
}
}
@@ -263,10 +308,8 @@ public function test_get_item_removes_extra_settings() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertArrayNotHasKey( 'fontFace', $data['font_family_settings'] );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertArrayNotHasKey( 'fontFace', $data['font_family_settings'], 'The fontFace property should not exist in the font_family_settings data.' );
}
/**
@@ -281,6 +324,8 @@ public function test_get_item_malformed_post_content_returns_empty_settings() {
)
);
+ static::$post_ids_to_cleanup[] = $font_family_id;
+
$empty_settings = array(
'name' => '',
// Slug will default to the post id.
@@ -294,14 +339,12 @@ public function test_get_item_malformed_post_content_returns_empty_settings() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertSame( $empty_settings, $data['font_family_settings'] );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertSame( $empty_settings, $data['font_family_settings'], 'The empty settings should exist in the font_family_settings data.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_item
+ * @covers WP_REST_Font_Families_Controller::get_item
*/
public function test_get_item_invalid_font_family_id() {
wp_set_current_user( self::$admin_id );
@@ -311,22 +354,22 @@ public function test_get_item_invalid_font_family_id() {
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_item
+ * @covers WP_REST_Font_Families_Controller::get_item
*/
public function test_get_item_no_permission() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id1 );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response should return an error with a "rest_cannot_read" code and 401 status.' );
wp_set_current_user( self::$editor_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response should return an error with a "rest_cannot_read" code and 403 status.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::create_item
+ * @covers WP_REST_Font_Families_Controller::create_item
*/
public function test_create_item() {
$settings = array_merge( self::$default_settings, array( 'slug' => 'open-sans-2' ) );
@@ -338,18 +381,16 @@ public function test_create_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
$this->check_font_family_data( $data, $data['id'], $response->get_links() );
$reponse_settings = $data['font_family_settings'];
- $this->assertSame( $settings, $reponse_settings );
- $this->assertEmpty( $data['font_faces'] );
-
- wp_delete_post( $data['id'], true );
+ $this->assertSame( $settings, $reponse_settings, 'The expected settings should exist in the font_family_settings data.' );
+ $this->assertEmpty( $data['font_faces'], 'The font_faces should be empty or not exist in the response data.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_request
+ * @covers WP_REST_Font_Families_Controller::validate_create_font_face_request
*/
public function test_create_item_default_theme_json_version() {
$settings = array_merge( self::$default_settings, array( 'slug' => 'open-sans-2' ) );
@@ -360,17 +401,19 @@ public function test_create_item_default_theme_json_version() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
- $this->assertArrayHasKey( 'theme_json_version', $data );
- $this->assertSame( 2, $data['theme_json_version'], 'The default theme.json version should be 2.' );
+ static::$post_ids_to_cleanup[] = $data['id'];
- wp_delete_post( $data['id'], true );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $this->assertArrayHasKey( 'theme_json_version', $data, 'The theme_json_version property should exist in the response data.' );
+ $this->assertSame( 2, $data['theme_json_version'], 'The default theme.json version should be 2.' );
}
/**
* @dataProvider data_create_item_invalid_theme_json_version
*
- * @covers WP_REST_Font_Faces_Controller::create_item
+ * @covers WP_REST_Font_Families_Controller::create_item
+ *
+ * @param int $theme_json_version Version to test.
*/
public function test_create_item_invalid_theme_json_version( $theme_json_version ) {
wp_set_current_user( self::$admin_id );
@@ -382,6 +425,11 @@ public function test_create_item_invalid_theme_json_version( $theme_json_version
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_create_item_invalid_theme_json_version() {
return array(
array( 1 ),
@@ -392,7 +440,9 @@ public function data_create_item_invalid_theme_json_version() {
/**
* @dataProvider data_create_item_with_default_preview
*
- * @covers WP_REST_Font_Faces_Controller::sanitize_font_family_settings
+ * @covers WP_REST_Font_Families_Controller::sanitize_font_family_settings
+ *
+ * @param array $settings Settings to test.
*/
public function test_create_item_with_default_preview( $settings ) {
wp_set_current_user( self::$admin_id );
@@ -402,14 +452,19 @@ public function test_create_item_with_default_preview( $settings ) {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 201, $response->get_status() );
- $response_settings = $data['font_family_settings'];
- $this->assertArrayHasKey( 'preview', $response_settings );
- $this->assertSame( '', $response_settings['preview'] );
+ static::$post_ids_to_cleanup[] = $data['id'];
- wp_delete_post( $data['id'], true );
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $response_settings = $data['font_family_settings'];
+ $this->assertArrayHasKey( 'preview', $response_settings, 'The preview property should exist in the font_family_settings data.' );
+ $this->assertSame( '', $response_settings['preview'], 'The preview data should be an empty string.' );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_create_item_with_default_preview() {
$default_settings = array(
'name' => 'Open Sans',
@@ -426,10 +481,90 @@ public function data_create_item_with_default_preview() {
);
}
+ /**
+ * @dataProvider data_sanitize_font_family_settings
+ *
+ * @covers WP_REST_Font_Families_Controller::sanitize_font_family_settings
+ *
+ * @param string $settings Font family settings to test.
+ * @param string $expected Expected settings result.
+ */
+ public function test_create_item_santize_font_family_settings( $settings, $expected ) {
+ $settings = array_merge( self::$default_settings, $settings );
+ $expected = array_merge( self::$default_settings, $expected );
+
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' );
+ $request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+
+ static::$post_ids_to_cleanup[] = $data['id'];
+
+ $this->assertSame( 201, $response->get_status(), 'The response status should be 201.' );
+ $this->assertSame( $expected, $data['font_family_settings'], 'The response font_family_settings should match.' );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
+ public function data_sanitize_font_family_settings() {
+ return array(
+ 'settings with tags, extra whitespace, new lines' => array(
+ 'settings' => array(
+ 'name' => " Opening Sans\n ",
+ 'slug' => " OPENing SanS \n ",
+ 'fontFamily' => " Opening Sans\n ",
+ 'preview' => " https://example.com/ ",
+ ),
+ 'expected' => array(
+ 'name' => 'Opening Sans',
+ 'slug' => 'opening-sans-alertxss',
+ 'fontFamily' => '"Opening Sans"',
+ 'preview' => "https://example.com//stylescriptalert('XSS');/script%20%20%20%20%20%20",
+ ),
+ ),
+ 'multiword font family name with integer' => array(
+ 'settings' => array(
+ 'slug' => 'libre-barcode-128-text',
+ 'fontFamily' => 'Libre Barcode 128 Text',
+ ),
+ 'expected' => array(
+ 'slug' => 'libre-barcode-128-text',
+ 'fontFamily' => '"Libre Barcode 128 Text"',
+ ),
+ ),
+ 'multiword font family name' => array(
+ 'settings' => array(
+ 'slug' => 'b612-mono',
+ 'fontFamily' => 'B612 Mono',
+ ),
+ 'expected' => array(
+ 'slug' => 'b612-mono',
+ 'fontFamily' => '"B612 Mono"',
+ ),
+ ),
+ 'comma-separated font family names' => array(
+ 'settings' => array(
+ 'slug' => 'open-sans-noto-sans',
+ 'fontFamily' => 'Open Sans, Noto Sans, sans-serif',
+ ),
+ 'expected' => array(
+ 'slug' => 'open-sans-noto-sans',
+ 'fontFamily' => '"Open Sans", "Noto Sans", sans-serif',
+ ),
+ ),
+ );
+ }
+
/**
* @dataProvider data_create_item_invalid_settings
*
- * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings
+ * @covers WP_REST_Font_Families_Controller::validate_create_font_face_settings
+ *
+ * @param array $settings Settings to test.
*/
public function test_create_item_invalid_settings( $settings ) {
wp_set_current_user( self::$admin_id );
@@ -441,6 +576,11 @@ public function test_create_item_invalid_settings( $settings ) {
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_create_item_invalid_settings() {
return array(
'Missing name' => array(
@@ -484,10 +624,10 @@ public function test_create_item_invalid_settings_json() {
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
+ $this->assertErrorResponse( 'rest_invalid_param', $response, 400, 'The response should return an error for "rest_invalid_param" with 400 status.' );
$expected_message = 'font_family_settings parameter must be a valid JSON string.';
$message = $response->as_error()->get_all_error_data()[0]['params']['font_family_settings'];
- $this->assertSame( $expected_message, $message );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
@@ -501,14 +641,14 @@ public function test_create_item_with_duplicate_slug() {
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_duplicate_font_family', $response, 400 );
+ $this->assertErrorResponse( 'rest_duplicate_font_family', $response, 400, 'The response should return an error for "rest_duplicate_font_family" with 400 status.' );
$expected_message = 'A font family with slug "helvetica" already exists.';
$message = $response->as_error()->get_error_messages()[0];
- $this->assertSame( $expected_message, $message );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::create_item
+ * @covers WP_REST_Font_Families_Controller::create_item
*/
public function test_create_item_no_permission() {
$settings = array_merge( self::$default_settings, array( 'slug' => 'open-sans-2' ) );
@@ -516,7 +656,7 @@ public function test_create_item_no_permission() {
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families' );
$request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_create', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_create', $response, 401, 'The response should return an error for "rest_cannot_create" with 401 status.' );
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families' );
@@ -532,7 +672,7 @@ public function test_create_item_no_permission() {
)
);
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_create', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_create', $response, 403, 'The response should return an error for "rest_cannot_create" with 403 status.' );
}
/**
@@ -556,7 +696,7 @@ public function test_update_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->check_font_family_data( $data, $font_family_id, $response->get_links() );
$expected_settings = array(
@@ -565,14 +705,15 @@ public function test_update_item() {
'fontFamily' => $settings['fontFamily'],
'preview' => $settings['preview'],
);
- $this->assertSame( $expected_settings, $data['font_family_settings'] );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertSame( $expected_settings, $data['font_family_settings'], 'The response font_family_settings should match expected settings.' );
}
/**
* @dataProvider data_update_item_individual_settings
+ *
* @covers WP_REST_Font_Families_Controller::update_item
+ *
+ * @param array $settings Settings to test.
*/
public function test_update_item_individual_settings( $settings ) {
wp_set_current_user( self::$admin_id );
@@ -583,15 +724,18 @@ public function test_update_item_individual_settings( $settings ) {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$key = key( $settings );
$value = current( $settings );
- $this->assertArrayHasKey( $key, $data['font_family_settings'] );
- $this->assertSame( $value, $data['font_family_settings'][ $key ] );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertArrayHasKey( $key, $data['font_family_settings'], 'The expected key should exist in the font_family_settings data.' );
+ $this->assertSame( $value, $data['font_family_settings'][ $key ], 'The font_family_settings data should match.' );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_update_item_individual_settings() {
return array(
array( array( 'name' => 'Opened Sans' ) ),
@@ -602,38 +746,39 @@ public function data_update_item_individual_settings() {
);
}
- /**
- * @dataProvider data_update_item_santize_font_family
+ /**
+ * @dataProvider data_sanitize_font_family_settings
*
- * @covers WP_REST_Font_Families_Controller::sanitize_font_face_settings
+ * @covers WP_REST_Font_Families_Controller::sanitize_font_family_settings
+ *
+ * @param string $settings Font family settings to test.
+ * @param string $expected Expected settings result.
*/
- public function test_update_item_santize_font_family( $font_family_setting, $expected ) {
+ public function test_update_item_santize_font_family_settings( $settings, $expected ) {
+ // Unset/modify slug from the data provider, since we're updating rather than creating.
+ unset( $settings['slug'] );
+ $initial_settings = array( 'slug' => 'open-sans-update' );
+ $expected = array_merge( self::$default_settings, $expected, $initial_settings );
+
wp_set_current_user( self::$admin_id );
+ $font_family_id = self::create_font_family_post( $initial_settings );
+ static::$post_ids_to_cleanup[] = $font_family_id;
- $font_family_id = self::create_font_family_post();
- $request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . $font_family_id );
- $request->set_param( 'font_family_settings', wp_json_encode( array( 'fontFamily' => $font_family_setting ) ) );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . $font_family_id );
+ $request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
- $this->assertSame( $expected, $data['font_family_settings']['fontFamily'] );
-
- wp_delete_post( $font_family_id, true );
- }
-
- public function data_update_item_santize_font_family() {
- return array(
- array( 'Libre Barcode 128 Text', '"Libre Barcode 128 Text"' ),
- array( 'B612 Mono', '"B612 Mono"' ),
- array( 'Open Sans, Noto Sans, sans-serif', '"Open Sans", "Noto Sans", sans-serif' ),
- );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertSame( $expected, $data['font_family_settings'], 'The response font_family_settings should match.' );
}
/**
* @dataProvider data_update_item_invalid_settings
*
- * @covers WP_REST_Font_Faces_Controller::update_item
+ * @covers WP_REST_Font_Families_Controller::update_item
+ *
+ * @param array $settings Settings to test.
*/
public function test_update_item_empty_settings( $settings ) {
wp_set_current_user( self::$admin_id );
@@ -646,6 +791,11 @@ public function test_update_item_empty_settings( $settings ) {
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
+ /**
+ * Data provider.
+ *
+ * @return array
+ */
public function data_update_item_invalid_settings() {
return array(
'Empty name' => array(
@@ -664,7 +814,7 @@ public function data_update_item_invalid_settings() {
}
/**
- * @covers WP_REST_Font_Faces_Controller::update_item
+ * @covers WP_REST_Font_Families_Controller::update_item
*/
public function test_update_item_update_slug_not_allowed() {
wp_set_current_user( self::$admin_id );
@@ -675,14 +825,14 @@ public function test_update_item_update_slug_not_allowed() {
);
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
+ $this->assertErrorResponse( 'rest_invalid_param', $response, 400, 'The response should return an error for "rest_invalid_param" with 400 status.' );
$expected_message = 'font_family_settings[slug] cannot be updated.';
$message = $response->as_error()->get_all_error_data()[0]['params']['font_family_settings'];
- $this->assertSame( $expected_message, $message );
+ $this->assertSame( $expected_message, $message, 'The response error message should match.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::update_item
+ * @covers WP_REST_Font_Families_Controller::update_item
*/
public function test_update_item_invalid_font_family_id() {
$settings = array_diff_key( self::$default_settings, array( 'slug' => '' ) );
@@ -691,11 +841,11 @@ public function test_update_item_invalid_font_family_id() {
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
$request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 );
+ $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404, 'The response should return an error for "rest_post_invalid_id" with 404 status.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::update_item
+ * @covers WP_REST_Font_Families_Controller::update_item
*/
public function test_update_item_no_permission() {
$settings = array_diff_key( self::$default_settings, array( 'slug' => '' ) );
@@ -704,18 +854,18 @@ public function test_update_item_no_permission() {
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id1 );
$request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_edit', $response, 401, 'The response should return an error for "rest_cannot_edit" with 401 status for an invalid user.' );
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id1 );
$request->set_param( 'font_family_settings', wp_json_encode( $settings ) );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 );
+ $this->assertErrorResponse( 'rest_cannot_edit', $response, 403, 'The response should return an error for "rest_cannot_edit" with 403 status for a user without permission.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::delete_item
+ * @covers WP_REST_Font_Families_Controller::delete_item
*/
public function test_delete_item() {
wp_set_current_user( self::$admin_id );
@@ -724,12 +874,12 @@ public function test_delete_item() {
$request['force'] = true;
$response = rest_get_server()->dispatch( $request );
- $this->assertSame( 200, $response->get_status() );
- $this->assertNull( get_post( $font_family_id ) );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
+ $this->assertNull( get_post( $font_family_id ), 'The post should not exist after deleting.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::delete_item
+ * @covers WP_REST_Font_Families_Controller::delete_item
*/
public function test_delete_item_no_trash() {
wp_set_current_user( self::$admin_id );
@@ -738,21 +888,19 @@ public function test_delete_item_no_trash() {
// Attempt trashing.
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . $font_family_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
+ $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501, 'The response should return an error for "rest_trash_not_supported" with 501 status.' );
$request->set_param( 'force', 'false' );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
+ $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501, 'When "force" is false, the response should return an error for "rest_trash_not_supported" with 501 status.' );
// Ensure the post still exists.
$post = get_post( $font_family_id );
- $this->assertNotEmpty( $post );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertNotEmpty( $post, 'The post should still exist.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::delete_item
+ * @covers WP_REST_Font_Families_Controller::delete_item
*/
public function test_delete_item_invalid_font_family_id() {
wp_set_current_user( self::$admin_id );
@@ -762,7 +910,7 @@ public function test_delete_item_invalid_font_family_id() {
}
/**
- * @covers WP_REST_Font_Faces_Controller::delete_item
+ * @covers WP_REST_Font_Families_Controller::delete_item
*/
public function test_delete_item_no_permissions() {
$font_family_id = self::create_font_family_post();
@@ -770,18 +918,16 @@ public function test_delete_item_no_permissions() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . $font_family_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_delete', $response, 401 );
+ $this->assertErrorResponse( 'rest_cannot_delete', $response, 401, 'The response should return an error for "rest_cannot_delete" with 401 status for an invalid user.' );
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . $font_family_id );
$response = rest_get_server()->dispatch( $request );
- $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
-
- wp_delete_post( $font_family_id, true );
+ $this->assertErrorResponse( 'rest_cannot_delete', $response, 403, 'The response should return an error for "rest_cannot_delete" with 403 status for a user without permission.' );
}
/**
- * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response
+ * @covers WP_REST_Font_Families_Controller::prepare_item_for_response
*/
public function test_prepare_item() {
wp_set_current_user( self::$admin_id );
@@ -789,35 +935,70 @@ public function test_prepare_item() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$this->check_font_family_data( $data, self::$font_family_id2, $response->get_links() );
}
/**
- * @covers WP_REST_Font_Faces_Controller::get_item_schema
+ * @covers WP_REST_Font_Families_Controller::get_item_schema
*/
public function test_get_item_schema() {
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
- $this->assertSame( 200, $response->get_status() );
+ $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
$properties = $data['schema']['properties'];
- $this->assertCount( 4, $properties );
- $this->assertArrayHasKey( 'id', $properties );
- $this->assertArrayHasKey( 'theme_json_version', $properties );
- $this->assertArrayHasKey( 'font_faces', $properties );
- $this->assertArrayHasKey( 'font_family_settings', $properties );
+ $this->assertCount( 4, $properties, 'There should be 4 properties in the schema::properties data.' );
+ $this->assertArrayHasKey( 'id', $properties, 'The id property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'theme_json_version', $properties, 'The theme_json_version property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'font_faces', $properties, 'The font_faces property should exist in the schema::properties data.' );
+ $this->assertArrayHasKey( 'font_family_settings', $properties, 'The font_family_settings property should exist in the schema::properties data.' );
+ }
+
+ /**
+ * @covers WP_REST_Font_Families_Controller::get_item_schema
+ */
+ public function test_get_item_schema_font_family_settings_should_all_have_sanitize_callbacks() {
+ $schema = ( new WP_REST_Font_Families_Controller( 'wp_font_family' ) )->get_item_schema();
+ $font_family_settings_schema = $schema['properties']['font_family_settings'];
+
+ $this->assertArrayHasKey( 'properties', $font_family_settings_schema, 'font_family_settings schema is missing properties.' );
+ $this->assertIsArray( $font_family_settings_schema['properties'], 'font_family_settings properties should be an array.' );
+
+ // arg_options should be removed for each setting property.
+ foreach ( $font_family_settings_schema['properties'] as $property ) {
+ $this->assertArrayHasKey( 'arg_options', $property, 'Setting schema should have arg_options.' );
+ $this->assertArrayHasKey( 'sanitize_callback', $property['arg_options'], 'Setting schema should have a sanitize_callback.' );
+ $this->assertIsCallable( $property['arg_options']['sanitize_callback'], 'That sanitize_callback value should be callable.' );
+ }
+ }
+
+ /**
+ * @covers WP_REST_Font_Families_Controller::get_public_item_schema
+ */
+ public function test_get_public_item_schema_should_not_have_arg_options() {
+ $schema = ( new WP_REST_Font_Families_Controller( 'wp_font_family' ) )->get_public_item_schema();
+ $font_family_settings_schema = $schema['properties']['font_family_settings'];
+
+ $this->assertArrayHasKey( 'properties', $font_family_settings_schema, 'font_family_settings schema is missing properties.' );
+ $this->assertIsArray( $font_family_settings_schema['properties'], 'font_family_settings properties should be an array.' );
+
+ // arg_options should be removed for each setting property.
+ foreach ( $font_family_settings_schema['properties'] as $property ) {
+ $this->assertArrayNotHasKey( 'arg_options', $property, 'arg_options should be removed from the schema for each setting.' );
+ }
}
protected function check_font_family_data( $data, $post_id, $links ) {
- $post = get_post( $post_id );
+ static::$post_ids_to_cleanup[] = $post_id;
+ $post = get_post( $post_id );
- $this->assertArrayHasKey( 'id', $data );
- $this->assertSame( $post->ID, $data['id'] );
+ $this->assertArrayHasKey( 'id', $data, 'The id property should exist in response data.' );
+ $this->assertSame( $post->ID, $data['id'], 'The "id" from the response data should match the post ID.' );
- $this->assertArrayHasKey( 'theme_json_version', $data );
- $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'] );
+ $this->assertArrayHasKey( 'theme_json_version', $data, 'The theme_json_version property should exist in response data.' );
+ $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'], 'The "theme_json_version" from the response data should match WP_Theme_JSON::LATEST_SCHEMA.' );
$font_face_ids = get_children(
array(
@@ -828,13 +1009,13 @@ protected function check_font_family_data( $data, $post_id, $links ) {
'orderby' => 'ID',
)
);
- $this->assertArrayHasKey( 'font_faces', $data );
+ $this->assertArrayHasKey( 'font_faces', $data, 'The font_faces property should exist in the response data.' );
foreach ( $font_face_ids as $font_face_id ) {
- $this->assertContains( $font_face_id, $data['font_faces'] );
+ $this->assertContains( $font_face_id, $data['font_faces'], 'The ID is in the font_faces data.' );
}
- $this->assertArrayHasKey( 'font_family_settings', $data );
+ $this->assertArrayHasKey( 'font_family_settings', $data, 'The font_family_settings property should exist in the response data.' );
$settings = $data['font_family_settings'];
$expected_settings = array(
'name' => $post->post_title,
@@ -842,11 +1023,13 @@ protected function check_font_family_data( $data, $post_id, $links ) {
'fontFamily' => $settings['fontFamily'],
'preview' => $settings['preview'],
);
- $this->assertSame( $expected_settings, $settings );
+ $this->assertSame( $expected_settings, $settings, 'The font_family_settings should match.' );
- $this->assertNotEmpty( $links );
- $this->assertSame( rest_url( 'wp/v2/font-families/' . $post->ID ), $links['self'][0]['href'] );
- $this->assertSame( rest_url( 'wp/v2/font-families' ), $links['collection'][0]['href'] );
+ $this->assertNotEmpty( $links, 'The links should not be empty in the response data.' );
+ $expected = rest_url( 'wp/v2/font-families/' . $post->ID );
+ $this->assertSame( $expected, $links['self'][0]['href'], 'The links URL from the response data should match the post\'s REST endpoint.' );
+ $expected = rest_url( 'wp/v2/font-families' );
+ $this->assertSame( $expected, $links['collection'][0]['href'], 'The links collection URL from the response data should match the REST endpoint.' );
if ( ! $font_face_ids ) {
return;
@@ -855,12 +1038,13 @@ protected function check_font_family_data( $data, $post_id, $links ) {
// Check font_face links, if present.
$this->assertArrayHasKey( 'font_faces', $links );
foreach ( $links['font_faces'] as $index => $link ) {
- $this->assertSame( rest_url( 'wp/v2/font-families/' . $post->ID . '/font-faces/' . $font_face_ids[ $index ] ), $link['href'] );
+ $expected = rest_url( 'wp/v2/font-families/' . $post->ID . '/font-faces/' . $font_face_ids[ $index ] );
+ $this->assertSame( $expected, $link['href'], 'The links for a font faces URL from the response data should match the REST endpoint.' );
$embeddable = isset( $link['attributes']['embeddable'] )
? $link['attributes']['embeddable']
: $link['embeddable'];
- $this->assertTrue( $embeddable );
+ $this->assertTrue( $embeddable, 'The embeddable should be true.' );
}
}
}