diff --git a/changelog.txt b/changelog.txt index b24b8294744d6..193fe8dfe70f6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,22 @@ == Changelog == += 17.5.1 = + +## Changelog + +### Bug Fixes + +- (Preferences)(hotfix)(17.5) Hotfix for missing preferences in the `core` scope([58031](https://github.com/WordPress/gutenberg/pull/58031)) + +## Contributors + +The following contributors merged PRs in this release: + +@youknowriad @fullofcaffeine + + + + = 17.5.0 = ## Changelog diff --git a/docs/contributors/code/back-merging-to-wp-core.md b/docs/contributors/code/back-merging-to-wp-core.md new file mode 100644 index 0000000000000..2b1ec77df1e55 --- /dev/null +++ b/docs/contributors/code/back-merging-to-wp-core.md @@ -0,0 +1,31 @@ +# Back-merging code to WordPress Core + +For major releases of the WordPress software, Gutenberg features need to be merged into WordPress Core. Typically this involves taking changes made in `.php` files within the Gutenberg repository and making the equivalent updates in the WP Core codebase. + +## Files/Directories + +Changes to files within the following files/directories will typically require back-merging to WP Core: + +- `lib/` +- `phpunit/` + +## Ignored directories/files + +The following directories/files do _not_ require back-merging to WP Core: + +- `lib/load.php` - Plugin specific code. +- `lib/experiments-page.php` - experiments are Plugin specific. +- `packages/block-library` - this is handled automatically during the packages sync process. +- `packages/e2e-tests/plugins` - PHP files related to e2e tests only. Mostly fixture data generators. +- `phpunit/blocks` - the code is maintained in Gutenberg so the test should be as well. + +Please note this list is not exhaustive. + +## Pull Request Criteria + +In general, all PHP code committed to the Gutenberg repository since the date of the final Gutenberg release that was included in [the _last_ stable WP Core release](https://developer.wordpress.org/block-editor/contributors/versions-in-wordpress/) should be considered for back merging to WP Core. + +There are however certain exceptions to that rule. PRs with the following criteria do _not_ require back-merging to WP Core: + +- Does not contain changes to PHP code. +- Has label `Backport from WordPress Core` - this code is already in WP Core. diff --git a/docs/getting-started/fundamentals/block-wrapper.md b/docs/getting-started/fundamentals/block-wrapper.md index 1f2404eca9b03..cf588cf33cf6f 100644 --- a/docs/getting-started/fundamentals/block-wrapper.md +++ b/docs/getting-started/fundamentals/block-wrapper.md @@ -2,7 +2,7 @@ Each block's markup is wrapped by a container HTML tag that needs to have the proper attributes to fully work in the Block Editor and to reflect the proper block's style settings when rendered in the Block Editor and the front end. As developers, we have full control over the block's markup, and WordPress provides the tools to add the attributes that need to exist on the wrapper to our block's markup. -Ensuring proper attributes to the block wrapper is especially important when using custom styling or features like `supports`. +Ensuring proper attributes to the block wrapper is especially important when using custom styling or features like `supports`.
The use of supports generates a set of properties that need to be manually added to the wrapping element of the block so they're properly stored as part of the block data. @@ -10,8 +10,8 @@ The use of supports generates a set of properties that need to be m A block can have three sets of markup defined, each one of them with a specific target and purpose: -- The one for the **Block Editor**, defined through a `edit` React component passed to [`registerBlockType`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/#registerblocktype) when registering the block in the client. -- The one used to **save the block in the DB**, defined through a `save` function passed to `registerBlockType` when registering the block in the client. +- The one for the **Block Editor**, defined through a `edit` React component passed to [`registerBlockType`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/#registerblocktype) when registering the block in the client. +- The one used to **save the block in the DB**, defined through a `save` function passed to `registerBlockType` when registering the block in the client. - This markup will be returned to the front end on request if no dynamic render has been defined for the block. - The one used to **dynamically render the markup of the block** returned to the front end on request, defined through the `render_callback` on [`register_block_type`](https://developer.wordpress.org/reference/functions/register_block_type/) or the [`render`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#render) PHP file in `block.json` - If defined, this server-side generated markup will be returned to the front end, ignoring the markup stored in DB. @@ -21,13 +21,13 @@ For the [`edit` React component and the `save` function](https://developer.wordp ## The Edit component's markup -The [`useBlockProps()`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops) hook available on the [`@wordpress/block-editor`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor) allows passing the required attributes for the Block Editor to the `edit` block's outer wrapper. +The [`useBlockProps()`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops) hook available on the [`@wordpress/block-editor`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor) allows passing the required attributes for the Block Editor to the `edit` block's outer wrapper. Among other things, the `useBlockProps()` hook takes care of including in this wrapper: -- An `id` for the block's markup -- Some accesibility and `data-` attributes +- An `id` for the block's markup +- Some accessibility and `data-` attributes - Classes and inline styles reflecting custom settings, which include by default: - - The `wp-block` class + - The `wp-block` class - A class that contains the name of the block with its namespace For example, for the following piece of code of a block's registration in the client... @@ -43,18 +43,18 @@ _(see the [code above](https://github.com/WordPress/block-development-examples/b ...the markup of the block in the Block Editor could look like this: ```html -

Hello World - Block Editor

@@ -87,16 +87,16 @@ _(see the [code above](https://github.com/WordPress/block-development-examples/b

Hello World – Frontend

``` -Any additional classes and attributes for the `save` function of the block should be passed as an argument of `useBlockProps.save()` (see [example](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/stylesheets-79a4c3/src/save.js)). +Any additional classes and attributes for the `save` function of the block should be passed as an argument of `useBlockProps.save()` (see [example](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/stylesheets-79a4c3/src/save.js)). When you add `supports` for any feature, the proper classes get added to the object returned by the `useBlockProps.save()` hook. ```html

Hello World

``` @@ -105,10 +105,10 @@ _(check the [example](https://github.com/WordPress/block-development-examples/tr ## The server-side render markup -Any markup in the server-side render definition for the block can use the [`get_block_wrapper_attributes()`](https://developer.wordpress.org/reference/functions/get_block_wrapper_attributes/) function to generate the string of attributes required to reflect the block settings (see [example](https://github.com/WordPress/block-development-examples/blob/f68640f42d993f0866d1879f67c73910285ca114/plugins/block-dynamic-rendering-64756b/src/render.php#L11)). +Any markup in the server-side render definition for the block can use the [`get_block_wrapper_attributes()`](https://developer.wordpress.org/reference/functions/get_block_wrapper_attributes/) function to generate the string of attributes required to reflect the block settings (see [example](https://github.com/WordPress/block-development-examples/blob/f68640f42d993f0866d1879f67c73910285ca114/plugins/block-dynamic-rendering-64756b/src/render.php#L11)). ```php

>

-``` \ No newline at end of file +``` diff --git a/docs/how-to-guides/block-tutorial/extending-the-query-loop-block.md b/docs/how-to-guides/block-tutorial/extending-the-query-loop-block.md index 9be4f3a993d20..d3628d991f872 100644 --- a/docs/how-to-guides/block-tutorial/extending-the-query-loop-block.md +++ b/docs/how-to-guides/block-tutorial/extending-the-query-loop-block.md @@ -208,13 +208,13 @@ export const withBookQueryControls = ( BlockEdit ) => ( props ) => { // function to handle that. return isMyBooksVariation( props ) ? ( <> - + { /** Our custom component */ } ) : ( - + ); }; diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index bee68530eeb23..ab6bb52b6b3b2 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -42,7 +42,7 @@ Create and save content to reuse across your site. Update the pattern, and the c - **Name:** core/block - **Category:** reusable - **Supports:** ~~customClassName~~, ~~html~~, ~~inserter~~, ~~renaming~~ -- **Attributes:** ref +- **Attributes:** overrides, ref ## Button diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 7b0bd386daaf4..b03e905d166bc 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -409,6 +409,19 @@ _Returns_ - `WPBlock[]`: Block objects. +### getBlocksByName + +Returns all blocks that match a blockName. Results include nested blocks. + +_Parameters_ + +- _state_ `Object`: Global application state. +- _blockName_ `?string`: Optional block name, if not specified, returns an empty array. + +_Returns_ + +- `Array`: Array of clientIds of blocks with name equal to blockName. + ### getBlockSelectionEnd Returns the current block selection end. This value may be null, and it may represent either a singular block selection or multi-selection end. A selection is singular if its start and end match. diff --git a/docs/reference-guides/data/data-core-notices.md b/docs/reference-guides/data/data-core-notices.md index e11e6f226169f..d36098429811d 100644 --- a/docs/reference-guides/data/data-core-notices.md +++ b/docs/reference-guides/data/data-core-notices.md @@ -277,7 +277,7 @@ export const ExampleComponent = () => { const notices = useSelect( ( select ) => select( noticesStore ).getNotices() ); - const { removeNotices } = useDispatch( noticesStore ); + const { removeAllNotices } = useDispatch( noticesStore ); return ( <>
    diff --git a/docs/reference-guides/filters/block-filters.md b/docs/reference-guides/filters/block-filters.md index 4c7e3df7cec12..35a041052889c 100644 --- a/docs/reference-guides/filters/block-filters.md +++ b/docs/reference-guides/filters/block-filters.md @@ -19,11 +19,11 @@ _Example_: ```php { return ( props ) => { return ( <> - + My custom control @@ -352,14 +352,14 @@ On the server, you can filter the list of blocks shown in the inserter using the post ) ) { return array( 'core/paragraph', 'core/heading' ); } return $allowed_block_types; } -add_filter( 'allowed_block_types_all', 'filter_allowed_block_types_when_post_provided', 10, 2 ); +add_filter( 'allowed_block_types_all', 'wpdocs_filter_allowed_block_types_when_post_provided', 10, 2 ); ``` ## Managing block categories @@ -374,7 +374,7 @@ It is possible to filter the list of default block categories using the `block_c post ) ) { array_push( $block_categories, @@ -388,7 +388,7 @@ function filter_block_categories_when_post_provided( $block_categories, $editor_ return $block_categories; } -add_filter( 'block_categories_all', 'filter_block_categories_when_post_provided', 10, 2 ); +add_filter( 'block_categories_all', 'wpdocs_filter_block_categories_when_post_provided', 10, 2 ); ``` ### `wp.blocks.updateCategory` diff --git a/docs/reference-guides/filters/editor-filters.md b/docs/reference-guides/filters/editor-filters.md index 59f6d7ef8213f..943e161a1df49 100644 --- a/docs/reference-guides/filters/editor-filters.md +++ b/docs/reference-guides/filters/editor-filters.md @@ -84,14 +84,14 @@ _Example:_ post ) ) { $editor_settings['maxUploadFileSize'] = 12345; } return $editor_settings; } -add_filter( 'block_editor_settings_all', 'filter_block_editor_settings_when_post_provided', 10, 2 ); +add_filter( 'block_editor_settings_all', 'wpdocs_filter_block_editor_settings_when_post_provided', 10, 2 ); ``` #### `block_editor_rest_api_preload_paths` @@ -104,14 +104,14 @@ _Example:_ post ) ) { array_push( $preload_paths, array( '/wp/v2/blocks', 'OPTIONS' ) ); } return $preload_paths; } -add_filter( 'block_editor_rest_api_preload_paths', 'filter_block_editor_rest_api_preload_paths_when_post_provided', 10, 2 ); +add_filter( 'block_editor_rest_api_preload_paths', 'wpdocs_filter_block_editor_rest_api_preload_paths_when_post_provided', 10, 2 ); ``` ### Available default editor settings diff --git a/docs/reference-guides/filters/global-styles-filters.md b/docs/reference-guides/filters/global-styles-filters.md index 7d3b6be95768b..59bbbfcd5921d 100644 --- a/docs/reference-guides/filters/global-styles-filters.md +++ b/docs/reference-guides/filters/global-styles-filters.md @@ -14,7 +14,7 @@ _Example:_ This is how to pass a new color palette for the theme and disable the text color UI: ```php -function filter_theme_json_theme( $theme_json ){ +function wpdocs_filter_theme_json_theme( $theme_json ){ $new_data = array( 'version' => 2, 'settings' => array( @@ -38,5 +38,5 @@ function filter_theme_json_theme( $theme_json ){ return $theme_json->update_with( $new_data ); } -add_filter( 'wp_theme_json_data_theme', 'filter_theme_json_theme' ); +add_filter( 'wp_theme_json_data_theme', 'wpdocs_filter_theme_json_theme' ); ``` diff --git a/docs/reference-guides/filters/parser-filters.md b/docs/reference-guides/filters/parser-filters.md index 3acfb2489182d..7adc68cc0bf7e 100644 --- a/docs/reference-guides/filters/parser-filters.md +++ b/docs/reference-guides/filters/parser-filters.md @@ -26,11 +26,11 @@ class EmptyParser { } } -function my_plugin_select_empty_parser( $prev_parser_class ) { +function wpdocs_select_empty_parser( $prev_parser_class ) { return 'EmptyParser'; } -add_filter( 'block_parser_class', 'my_plugin_select_empty_parser', 10, 1 ); +add_filter( 'block_parser_class', 'wpdocs_select_empty_parser', 10, 1 ); ``` > **Note**: At the present time it's not possible to replace the client-side parser. diff --git a/gutenberg.php b/gutenberg.php index febd63e0be030..e3ef3d1612722 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.3 * Requires PHP: 7.0 - * Version: 17.5.0 + * Version: 17.5.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/block-supports/pattern.php b/lib/block-supports/pattern.php deleted file mode 100644 index 0c5868c1fea0c..0000000000000 --- a/lib/block-supports/pattern.php +++ /dev/null @@ -1,46 +0,0 @@ - array( 'content' ), - 'core/heading' => array( 'content' ), - 'core/image' => array( 'url', 'title', 'alt' ), - 'core/button' => array( 'url', 'text' ), - ); - $pattern_support = array_key_exists( $block_type->name, $allowed_blocks ); - - if ( $pattern_support ) { - if ( ! $block_type->uses_context ) { - $block_type->uses_context = array(); - } - - if ( ! in_array( 'pattern/overrides', $block_type->uses_context, true ) ) { - $block_type->uses_context[] = 'pattern/overrides'; - } - } - } - - // Register the block support. - WP_Block_Supports::get_instance()->register( - 'pattern', - array( - 'register_attribute' => 'gutenberg_register_pattern_support', - ) - ); -} diff --git a/lib/block-supports/shadow.php b/lib/block-supports/shadow.php index 4a28c98b79325..87258930faf10 100644 --- a/lib/block-supports/shadow.php +++ b/lib/block-supports/shadow.php @@ -53,9 +53,8 @@ function gutenberg_apply_shadow_support( $block_type, $block_attributes ) { $shadow_block_styles = array(); - $preset_shadow = array_key_exists( 'shadow', $block_attributes ) ? "var:preset|shadow|{$block_attributes['shadow']}" : null; - $custom_shadow = isset( $block_attributes['style']['shadow'] ) ? $block_attributes['style']['shadow'] : null; - $shadow_block_styles['shadow'] = $preset_shadow ? $preset_shadow : $custom_shadow; + $custom_shadow = $block_attributes['style']['shadow'] ?? null; + $shadow_block_styles['shadow'] = $custom_shadow; $attributes = array(); $styles = gutenberg_style_engine_get_styles( $shadow_block_styles ); diff --git a/lib/blocks.php b/lib/blocks.php index d5283afeb7f99..e1d4622a0f23d 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -440,27 +440,6 @@ function gutenberg_legacy_wp_block_post_meta( $value, $object_id, $meta_key, $si add_filter( 'default_post_metadata', 'gutenberg_legacy_wp_block_post_meta', 10, 4 ); -/** - * Registers the metadata block attribute for all block types. - * - * @param array $args Array of arguments for registering a block type. - * @return array $args - */ -function gutenberg_register_metadata_attribute( $args ) { - // Setup attributes if needed. - if ( ! isset( $args['attributes'] ) || ! is_array( $args['attributes'] ) ) { - $args['attributes'] = array(); - } - - if ( ! array_key_exists( 'metadata', $args['attributes'] ) ) { - $args['attributes']['metadata'] = array( - 'type' => 'object', - ); - } - - return $args; -} -add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' ); /** * Strips all HTML from the content of footnotes, and sanitizes the ID. diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index a063ab34d9069..f4266a7ef66dd 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1010,7 +1010,7 @@ protected static function get_blocks_metadata() { if ( $duotone_support ) { $root_selector = wp_get_block_css_selector( $block_type ); - $duotone_selector = WP_Theme_JSON_Gutenberg::scope_selector( $root_selector, $duotone_support ); + $duotone_selector = static::scope_selector( $root_selector, $duotone_support ); } } @@ -1185,7 +1185,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' $setting_nodes[ $root_settings_key ]['selector'] = $options['root_selector']; } if ( false !== $root_style_key ) { - $setting_nodes[ $root_style_key ]['selector'] = $options['root_selector']; + $style_nodes[ $root_style_key ]['selector'] = $options['root_selector']; } } diff --git a/lib/compat/wordpress-6.5/block-patterns.php b/lib/compat/wordpress-6.5/block-patterns.php index f43acda2a1035..cce97cb19c690 100644 --- a/lib/compat/wordpress-6.5/block-patterns.php +++ b/lib/compat/wordpress-6.5/block-patterns.php @@ -49,7 +49,7 @@ function gutenberg_register_taxonomy_patterns() { 'singular_name' => _x( 'Pattern Category', 'taxonomy singular name' ), 'add_new_item' => __( 'Add New Category' ), 'add_or_remove_items' => __( 'Add or remove pattern categories' ), - 'back_to_items' => __( '← Go to pattern categories' ), + 'back_to_items' => __( '← Go to Pattern Categories' ), 'choose_from_most_used' => __( 'Choose from the most used pattern categories' ), 'edit_item' => __( 'Edit Pattern Category' ), 'item_link' => __( 'Pattern Category Link' ), diff --git a/lib/compat/wordpress-6.5/blocks.php b/lib/compat/wordpress-6.5/blocks.php index b6890c14dc1f9..f5a9968f9210b 100644 --- a/lib/compat/wordpress-6.5/blocks.php +++ b/lib/compat/wordpress-6.5/blocks.php @@ -22,3 +22,26 @@ function gutenberg_register_block_type_args_shim( $args ) { if ( ! method_exists( 'WP_Block_Type', 'get_variations' ) ) { add_filter( 'register_block_type_args', 'gutenberg_register_block_type_args_shim' ); } + + +/** + * Registers the metadata block attribute for all block types. + * + * @param array $args Array of arguments for registering a block type. + * @return array $args + */ +function gutenberg_register_metadata_attribute( $args ) { + // Setup attributes if needed. + if ( ! isset( $args['attributes'] ) || ! is_array( $args['attributes'] ) ) { + $args['attributes'] = array(); + } + + if ( ! array_key_exists( 'metadata', $args['attributes'] ) ) { + $args['attributes']['metadata'] = array( + 'type' => 'object', + ); + } + + return $args; +} +add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' ); diff --git a/lib/experimental/block-bindings/class-wp-block-bindings.php b/lib/experimental/block-bindings/class-wp-block-bindings.php index 7eb443dd367a6..fedc652688a24 100644 --- a/lib/experimental/block-bindings/class-wp-block-bindings.php +++ b/lib/experimental/block-bindings/class-wp-block-bindings.php @@ -101,7 +101,7 @@ public function replace_html( $block_content, $block_name, $block_attr, $source_ foreach ( $selector_attribute_names as $name ) { $selector_attrs[ $name ] = $block_reader->get_attribute( $name ); } - $selector_markup = "<$selector>" . esc_html( $source_value ) . ""; + $selector_markup = "<$selector>" . wp_kses_post( $source_value ) . ""; $amended_content = new WP_HTML_Tag_Processor( $selector_markup ); $amended_content->next_tag(); foreach ( $selector_attrs as $attribute_key => $attribute_value ) { diff --git a/lib/experimental/block-bindings/index.php b/lib/experimental/block-bindings/index.php index dc9a6c9b96957..cf9cc318b1c92 100644 --- a/lib/experimental/block-bindings/index.php +++ b/lib/experimental/block-bindings/index.php @@ -10,10 +10,8 @@ // Register the sources. $gutenberg_experiments = get_option( 'gutenberg-experiments' ); if ( $gutenberg_experiments ) { - if ( array_key_exists( 'gutenberg-pattern-partial-syncing', $gutenberg_experiments ) ) { - require_once __DIR__ . '/sources/pattern.php'; - } if ( array_key_exists( 'gutenberg-block-bindings', $gutenberg_experiments ) ) { + require_once __DIR__ . '/sources/pattern.php'; require_once __DIR__ . '/sources/post-meta.php'; } } diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 93b65f95fc61a..4c22601fa9102 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -78,13 +78,9 @@ function wp_enqueue_block_view_script( $block_name, $args ) { } } - - - $gutenberg_experiments = get_option( 'gutenberg-experiments' ); if ( $gutenberg_experiments && ( - array_key_exists( 'gutenberg-block-bindings', $gutenberg_experiments ) || - array_key_exists( 'gutenberg-pattern-partial-syncing', $gutenberg_experiments ) + array_key_exists( 'gutenberg-block-bindings', $gutenberg_experiments ) ) ) { require_once __DIR__ . '/block-bindings/index.php'; diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 729376cf030dd..24590c5cf3eb0 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -33,10 +33,6 @@ function gutenberg_enable_experiments() { if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' ); } - - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-pattern-partial-syncing', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalPatternPartialSyncing = true', 'before' ); - } } add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/lib/experimental/fonts-api/bc-layer/class-gutenberg-fonts-api-bc-layer.php b/lib/experimental/fonts-api/bc-layer/class-gutenberg-fonts-api-bc-layer.php deleted file mode 100644 index 8c85976987523..0000000000000 --- a/lib/experimental/fonts-api/bc-layer/class-gutenberg-fonts-api-bc-layer.php +++ /dev/null @@ -1,96 +0,0 @@ -variation_property_defaults = apply_filters( 'wp_webfont_variation_defaults', $this->variation_property_defaults ); - - /** - * Fires when the WP_Webfonts instance is initialized. - * - * @since X.X.X - * - * @param WP_Web_Fonts $wp_webfonts WP_Web_Fonts instance (passed by reference). - */ - do_action_ref_array( 'wp_default_webfonts', array( &$this ) ); - } - - /** - * Get the list of registered providers. - * - * @since X.X.X - * - * @return array $providers { - * An associative array of registered providers, keyed by their unique ID. - * - * @type string $provider_id => array { - * An associate array of provider's class name and fonts. - * - * @type string $class_name Fully qualified name of the provider's class. - * @type string[] $fonts An array of enqueued font handles for this provider. - * } - * } - */ - public function get_providers() { - return $this->providers; - } - - /** - * Register a provider. - * - * @since X.X.X - * - * @param string $provider_id The provider's unique ID. - * @param string $class_name The provider class name. - * @return bool True if successfully registered, else false. - */ - public function register_provider( $provider_id, $class_name ) { - if ( empty( $provider_id ) || empty( $class_name ) || ! class_exists( $class_name ) ) { - return false; - } - - $this->providers[ $provider_id ] = array( - 'class' => $class_name, - 'fonts' => array(), - ); - return true; - } - - /** - * Get the list of all registered font family handles. - * - * @since X.X.X - * - * @return string[] - */ - public function get_registered_font_families() { - $font_families = array(); - foreach ( $this->registered as $handle => $obj ) { - if ( $obj->extra['is_font_family'] ) { - $font_families[] = $handle; - } - } - return $font_families; - } - - /** - * Get the list of all registered font families and their variations. - * - * @since X.X.X - * - * @return string[] - */ - public function get_registered() { - return array_keys( $this->registered ); - } - - /** - * Get the list of enqueued font families and their variations. - * - * @since X.X.X - * - * @return array[] - */ - public function get_enqueued() { - return $this->queue; - } - - /** - * Registers a font family. - * - * @since X.X.X - * - * @param string $font_family Font family name to register. - * @return string|null Font family handle when registration successes. Null on failure. - */ - public function add_font_family( $font_family ) { - $font_family_handle = WP_Fonts_Utils::convert_font_family_into_handle( $font_family ); - if ( ! $font_family_handle ) { - return null; - } - - if ( isset( $this->registered[ $font_family_handle ] ) ) { - return $font_family_handle; - } - - $registered = $this->add( $font_family_handle, false ); - if ( ! $registered ) { - return null; - } - - $this->add_data( $font_family_handle, 'font-properties', array( 'font-family' => $font_family ) ); - $this->add_data( $font_family_handle, 'is_font_family', true ); - - return $font_family_handle; - } - - /** - * Removes a font family and all registered variations. - * - * @since X.X.X - * - * @param string $font_family_handle The font family to remove. - */ - public function remove_font_family( $font_family_handle ) { - if ( ! isset( $this->registered[ $font_family_handle ] ) ) { - return; - } - - $variations = $this->registered[ $font_family_handle ]->deps; - - foreach ( $variations as $variation ) { - $this->remove( $variation ); - } - - $this->remove( $font_family_handle ); - } - - /** - * Add a variation to an existing family or register family if none exists. - * - * @since X.X.X - * - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - * @param string $variation_handle Optional. The variation's handle. When none is provided, the - * handle will be dynamically generated. - * Default empty string. - * @return string|null Variation handle on success. Else null. - */ - public function add_variation( $font_family_handle, array $variation, $variation_handle = '' ) { - if ( ! WP_Fonts_Utils::is_defined( $font_family_handle ) ) { - trigger_error( 'Font family handle must be a non-empty string.' ); - return null; - } - - // When there is a variation handle, check it. - if ( '' !== $variation_handle && ! WP_Fonts_Utils::is_defined( $variation_handle ) ) { - trigger_error( 'Variant handle must be a non-empty string.' ); - return null; - } - - // Register the font family when it does not yet exist. - if ( ! isset( $this->registered[ $font_family_handle ] ) ) { - if ( ! $this->add_font_family( $font_family_handle ) ) { - return null; - } - } - - $variation = $this->validate_variation( $variation ); - - // Variation validation failed. - if ( ! $variation ) { - return null; - } - - // When there's no variation handle, attempt to create one. - if ( '' === $variation_handle ) { - $variation_handle = WP_Fonts_Utils::convert_variation_into_handle( $font_family_handle, $variation ); - if ( is_null( $variation_handle ) ) { - return null; - } - } - - // Bail out if the variant is already registered. - if ( $this->is_variation_registered( $font_family_handle, $variation_handle ) ) { - return $variation_handle; - } - - $variation_src = array_key_exists( 'src', $variation ) ? $variation['src'] : false; - $result = $this->add( $variation_handle, $variation_src ); - - // Bail out if the registration failed. - if ( ! $result ) { - return null; - } - - $this->add_data( $variation_handle, 'font-properties', $variation ); - $this->add_data( $variation_handle, 'is_font_family', false ); - - // Add the font variation as a dependency to the registered font family. - $this->add_dependency( $font_family_handle, $variation_handle ); - - $this->providers[ $variation['provider'] ]['fonts'][] = $variation_handle; - - return $variation_handle; - } - - /** - * Removes a variation. - * - * @since X.X.X - * - * @param string $font_family_handle The font family for this variation. - * @param string $variation_handle The variation's handle to remove. - */ - public function remove_variation( $font_family_handle, $variation_handle ) { - if ( isset( $this->registered[ $variation_handle ] ) ) { - $this->remove( $variation_handle ); - } - - if ( ! $this->is_variation_registered( $font_family_handle, $variation_handle ) ) { - return; - } - - // Remove the variation as a dependency from its font family. - $this->registered[ $font_family_handle ]->deps = array_values( - array_diff( - $this->registered[ $font_family_handle ]->deps, - array( $variation_handle ) - ) - ); - } - - /** - * Checks if the variation is registered. - * - * @since X.X.X - * - * @param string $font_family_handle The font family's handle for this variation. - * @param string $variation_handle Variation's handle. - * @return bool True when registered to the given font family. Else false. - */ - private function is_variation_registered( $font_family_handle, $variation_handle ) { - if ( ! isset( $this->registered[ $font_family_handle ] ) ) { - return false; - } - - return in_array( $variation_handle, $this->registered[ $font_family_handle ]->deps, true ); - } - - /** - * Adds a variation as a dependency to the given font family. - * - * @since X.X.X - * - * @param string $font_family_handle The font family's handle for this variation. - * @param string $variation_handle The variation's handle. - */ - private function add_dependency( $font_family_handle, $variation_handle ) { - $this->registered[ $font_family_handle ]->deps[] = $variation_handle; - } - - /** - * Validates and sanitizes a variation. - * - * @since X.X.X - * - * @param array $variation Variation properties to add. - * @return false|array Validated variation on success. Else, false. - */ - private function validate_variation( $variation ) { - $variation = wp_parse_args( $variation, $this->variation_property_defaults ); - - // Check the font-family. - if ( empty( $variation['font-family'] ) || ! is_string( $variation['font-family'] ) ) { - trigger_error( 'Webfont font-family must be a non-empty string.' ); - return false; - } - - // Local fonts need a "src". - if ( 'local' === $variation['provider'] ) { - // Make sure that local fonts have 'src' defined. - if ( empty( $variation['src'] ) || ( ! is_string( $variation['src'] ) && ! is_array( $variation['src'] ) ) ) { - trigger_error( 'Webfont src must be a non-empty string or an array of strings.' ); - return false; - } - } elseif ( ! isset( $this->providers[ $variation['provider'] ] ) ) { - trigger_error( sprintf( 'The provider "%s" is not registered', $variation['provider'] ) ); - return false; - } elseif ( ! class_exists( $this->providers[ $variation['provider'] ]['class'] ) ) { - trigger_error( sprintf( 'The provider class "%s" does not exist', $variation['provider'] ) ); - return false; - } - - // Validate the 'src' property. - if ( ! empty( $variation['src'] ) ) { - foreach ( (array) $variation['src'] as $src ) { - if ( empty( $src ) || ! is_string( $src ) ) { - trigger_error( 'Each webfont src must be a non-empty string.' ); - return false; - } - } - } - - // Check the font-weight. - if ( ! is_string( $variation['font-weight'] ) && ! is_int( $variation['font-weight'] ) ) { - trigger_error( 'Webfont font-weight must be a properly formatted string or integer.' ); - return false; - } - - // Check the font-display. - if ( ! in_array( $variation['font-display'], array( 'auto', 'block', 'fallback', 'swap', 'optional' ), true ) ) { - $variation['font-display'] = 'fallback'; - } - - $valid_props = array( - 'ascent-override', - 'descent-override', - 'font-display', - 'font-family', - 'font-stretch', - 'font-style', - 'font-weight', - 'font-variant', - 'font-feature-settings', - 'font-variation-settings', - 'line-gap-override', - 'size-adjust', - 'src', - 'unicode-range', - - // Exceptions. - 'provider', - ); - - foreach ( $variation as $prop => $value ) { - if ( ! in_array( $prop, $valid_props, true ) ) { - unset( $variation[ $prop ] ); - } - } - - return $variation; - } - - /** - * Processes the items and dependencies. - * - * Processes the items passed to it or the queue, and their dependencies. - * - * @since X.X.X - * - * @param string|string[]|bool $handles Optional. Items to be processed: queue (false), - * single item (string), or multiple items (array of strings). - * Default false. - * @param int|false $group Optional. Group level: level (int), no group (false). - * - * @return array|string[] Array of web font handles that have been processed. - * An empty array if none were processed. - */ - public function do_items( $handles = false, $group = false ) { - $handles = $this->prepare_handles_for_printing( $handles ); - - if ( empty( $handles ) ) { - return $this->done; - } - - $this->all_deps( $handles ); - if ( empty( $this->to_do ) ) { - return $this->done; - } - - $this->to_do_keyed_handles = array_flip( $this->to_do ); - - foreach ( $this->get_providers() as $provider_id => $provider ) { - // Alert and skip if the provider class does not exist. - if ( ! class_exists( $provider['class'] ) ) { - /* translators: %s is the provider name. */ - trigger_error( - sprintf( - 'Class "%s" not found for "%s" web font provider', - $provider['class'], - $provider_id - ) - ); - continue; - } - - $this->do_item( $provider_id, $group ); - } - - $this->process_font_families_after_printing( $handles ); - - return $this->done; - } - - /** - * Prepares the given handles for printing. - * - * @since X.X.X - * - * @param string|string[]|bool $handles Optional. Handles to prepare. - * Default false. - * @return array Array of handles. - */ - private function prepare_handles_for_printing( $handles = false ) { - if ( false !== $handles ) { - $handles = $this->validate_handles( $handles ); - // Bail out when invalid. - if ( empty( $handles ) ) { - return array(); - } - } - - // Use the enqueued queue. - if ( empty( $handles ) ) { - if ( empty( $this->queue ) ) { - return array(); - } - $handles = $this->queue; - } - - return $handles; - } - - /** - * Validates handle(s) to ensure each is a non-empty string. - * - * @since X.X.X - * - * @param string|string[] $handles Handles to prepare. - * @return string[]|null Array of handles on success. Else null. - */ - private function validate_handles( $handles ) { - // Validate each element is a non-empty string handle. - $handles = array_filter( (array) $handles, array( WP_Fonts_Utils::class, 'is_defined' ) ); - - if ( empty( $handles ) ) { - trigger_error( 'Handles must be a non-empty string or array of non-empty strings' ); - return null; - } - - return $handles; - } - - /** - * Invokes each provider to process and print its styles. - * - * @since X.X.X - * - * @see WP_Dependencies::do_item() - * - * @param string $provider_id The provider to process. - * @param int|false $group Not used. - * @return bool - */ - public function do_item( $provider_id, $group = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - // Bail out if the provider is not registered. - if ( ! isset( $this->providers[ $provider_id ] ) ) { - return false; - } - - $font_handles = $this->get_enqueued_fonts_for_provider( $provider_id ); - if ( empty( $font_handles ) ) { - return false; - } - - $properties_by_font = $this->get_font_properties_for_provider( $font_handles ); - if ( empty( $properties_by_font ) ) { - return false; - } - - // Invoke provider to print its styles. - $provider = $this->get_provider_instance( $provider_id ); - $provider->set_fonts( $properties_by_font ); - $provider->print_styles(); - - // Clean up. - $this->update_queues_for_printed_fonts( $font_handles ); - - return true; - } - - /** - * Retrieves a list of enqueued web font variations for a provider. - * - * @since X.X.X - * - * @param string $provider_id The provider to process. - * @return array[] Webfonts organized by providers. - */ - private function get_enqueued_fonts_for_provider( $provider_id ) { - $providers = $this->get_providers(); - - if ( empty( $providers[ $provider_id ] ) ) { - return array(); - } - - return array_intersect( - $providers[ $provider_id ]['fonts'], - $this->to_do - ); - } - - /** - * Gets a list of font properties for each of the given font handles. - * - * @since X.X.X - * - * @param array $font_handles Font handles to get properties. - * @return array A list of fonts with each font's properties. - */ - private function get_font_properties_for_provider( array $font_handles ) { - $font_properties = array(); - - foreach ( $font_handles as $font_handle ) { - $properties = $this->get_data( $font_handle, 'font-properties' ); - if ( ! $properties ) { - continue; - } - $font_properties[ $font_handle ] = $properties; - } - - return $font_properties; - } - - /** - * Gets the instance of the provider from the WP_Webfonts::$provider_instance store. - * - * @since X.X.X - * - * @param string $provider_id The provider to get. - * @return object Instance of the provider. - */ - private function get_provider_instance( $provider_id ) { - if ( ! isset( $this->provider_instances[ $provider_id ] ) ) { - $this->provider_instances[ $provider_id ] = new $this->providers[ $provider_id ]['class'](); - } - return $this->provider_instances[ $provider_id ]; - } - - /** - * Update queues for the given printed fonts. - * - * @since X.X.X - * - * @param array $font_handles Font handles to get properties. - */ - private function update_queues_for_printed_fonts( array $font_handles ) { - foreach ( $font_handles as $font_handle ) { - $this->set_as_done( $font_handle ); - $this->remove_from_to_do_queues( $font_handle ); - } - } - - /** - * Processes the font families after printing the variations. - * - * For each queued font family: - * - * a. if any of their variations were printed, the font family is added to the `done` list. - * b. removes each from the to_do queues. - * - * @since X.X.X - * - * @param array $handles Handles to process. - */ - private function process_font_families_after_printing( array $handles ) { - foreach ( $handles as $handle ) { - if ( - ! $this->get_data( $handle, 'is_font_family' ) || - ! isset( $this->to_do_keyed_handles[ $handle ] ) - ) { - continue; - } - $font_family = $this->registered[ $handle ]; - - // Add the font family to `done` list if any of its variations were printed. - if ( ! empty( $font_family->deps ) ) { - $processed = array_intersect( $font_family->deps, $this->done ); - if ( ! empty( $processed ) ) { - $this->set_as_done( $handle ); - } - } - - $this->remove_from_to_do_queues( $handle ); - } - } - - /** - * Removes the handle from the `to_do` and `to_do_keyed_handles` lists. - * - * @since X.X.X - * - * @param string $handle Handle to remove. - */ - private function remove_from_to_do_queues( $handle ) { - unset( - $this->to_do[ $this->to_do_keyed_handles[ $handle ] ], - $this->to_do_keyed_handles[ $handle ] - ); - } - - /** - * Sets the given handle to done by adding it to the `done` list. - * - * @since X.X.X - * - * @param string $handle Handle to set as done. - */ - private function set_as_done( $handle ) { - if ( ! is_array( $this->done ) ) { - $this->done = array(); - } - $this->done[] = $handle; - } - - /** - * Converts the font family and its variations into theme.json structural format. - * - * @since X.X.X - * - * @param string $font_family_handle Font family to convert. - * @return array Webfonts in theme.json structural format. - */ - public function to_theme_json( $font_family_handle ) { - if ( ! isset( $this->registered[ $font_family_handle ] ) ) { - return array(); - } - - $font_family_name = $this->registered[ $font_family_handle ]->extra['font-properties']['font-family']; - $theme_json_format = array( - 'fontFamily' => str_contains( $font_family_name, ' ' ) ? "'{$font_family_name}'" : $font_family_name, - 'name' => $font_family_name, - 'slug' => $font_family_handle, - 'fontFace' => array(), - ); - - foreach ( $this->registered[ $font_family_handle ]->deps as $variation_handle ) { - if ( ! isset( $this->registered[ $variation_handle ] ) ) { - continue; - } - - $variation_obj = $this->registered[ $variation_handle ]; - $variation_properties = array( 'origin' => 'gutenberg_wp_fonts_api' ); - foreach ( $variation_obj->extra['font-properties'] as $property_name => $property_value ) { - $property_in_camelcase = lcfirst( str_replace( '-', '', ucwords( $property_name, '-' ) ) ); - $variation_properties[ $property_in_camelcase ] = $property_value; - } - $theme_json_format['fontFace'][ $variation_obj->handle ] = $variation_properties; - } - - return $theme_json_format; - } -} diff --git a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider-local.php b/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider-local.php deleted file mode 100644 index b6fd5d78a1435..0000000000000 --- a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider-local.php +++ /dev/null @@ -1,284 +0,0 @@ -style_tag_atts = array( 'type' => 'text/css' ); - } - } - - /** - * Gets the `@font-face` CSS styles for locally-hosted font files. - * - * This method does the following processing tasks: - * 1. Orchestrates an optimized `src` (with format) for browser support. - * 2. Generates the `@font-face` for all its webfonts. - * - * For example, when given these webfonts: - * - * array( - * 'source-serif-pro.normal.200 900' => array( - * 'provider' => 'local', - * 'font_family' => 'Source Serif Pro', - * 'font_weight' => '200 900', - * 'font_style' => 'normal', - * 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ), - * 'source-serif-pro.italic.400 900' => array( - * 'provider' => 'local', - * 'font_family' => 'Source Serif Pro', - * 'font_weight' => '200 900', - * 'font_style' => 'italic', - * 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), - * ), - * ) - * - * - * the following `@font-face` styles are generated and returned: - * - * - * @font-face{ - * font-family:"Source Serif Pro"; - * font-style:normal; - * font-weight:200 900; - * font-stretch:normal; - * src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2'); - * } - * @font-face{ - * font-family:"Source Serif Pro"; - * font-style:italic; - * font-weight:200 900; - * font-stretch:normal; - * src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2'); - * } - * - * - * @since X.X.X - * - * @return string The `@font-face` CSS. - */ - public function get_css() { - $css = ''; - - foreach ( $this->webfonts as $webfont ) { - // Order the webfont's `src` items to optimize for browser support. - $webfont = $this->order_src( $webfont ); - - // Build the @font-face CSS for this webfont. - $css .= '@font-face{' . $this->build_font_face_css( $webfont ) . '}'; - } - - return $css; - } - - /** - * Order `src` items to optimize for browser support. - * - * @since 6.0.0 - * - * @param array $webfont Webfont to process. - * @return array - */ - private function order_src( array $webfont ) { - if ( ! is_array( $webfont['src'] ) ) { - $webfont['src'] = (array) $webfont['src']; - } - - $src = array(); - $src_ordered = array(); - - foreach ( $webfont['src'] as $url ) { - // Add data URIs first. - if ( str_starts_with( trim( $url ), 'data:' ) ) { - $src_ordered[] = array( - 'url' => $url, - 'format' => 'data', - ); - continue; - } - $format = pathinfo( $url, PATHINFO_EXTENSION ); - $src[ $format ] = $url; - } - - // Add woff2. - if ( ! empty( $src['woff2'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff2'], - 'format' => 'woff2', - ); - } - - // Add woff. - if ( ! empty( $src['woff'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff'], - 'format' => 'woff', - ); - } - - // Add ttf. - if ( ! empty( $src['ttf'] ) ) { - $src_ordered[] = array( - 'url' => $src['ttf'], - 'format' => 'truetype', - ); - } - - // Add eot. - if ( ! empty( $src['eot'] ) ) { - $src_ordered[] = array( - 'url' => $src['eot'], - 'format' => 'embedded-opentype', - ); - } - - // Add otf. - if ( ! empty( $src['otf'] ) ) { - $src_ordered[] = array( - 'url' => $src['otf'], - 'format' => 'opentype', - ); - } - $webfont['src'] = $src_ordered; - - return $webfont; - } - - /** - * Builds the font-family's CSS. - * - * @since 6.0.0 - * - * @param array $webfont Webfont to process. - * @return string This font-family's CSS. - */ - private function build_font_face_css( array $webfont ) { - $css = ''; - - // Wrap font-family in quotes if it contains spaces - // and is not already wrapped in quotes. - if ( - str_contains( $webfont['font-family'], ' ' ) && - ! str_contains( $webfont['font-family'], '"' ) && - ! str_contains( $webfont['font-family'], "'" ) - ) { - $webfont['font-family'] = '"' . $webfont['font-family'] . '"'; - } - - foreach ( $webfont as $key => $value ) { - - // Skip "provider", since it's for internal API use, - // and not a valid CSS property. - if ( 'provider' === $key ) { - continue; - } - - // Compile the "src" parameter. - if ( 'src' === $key ) { - $value = $this->compile_src( $webfont['font-family'], $value ); - } - - // If font-variation-settings is an array, convert it to a string. - if ( 'font-variation-settings' === $key && is_array( $value ) ) { - $value = $this->compile_variations( $value ); - } - - if ( ! empty( $value ) ) { - $css .= "$key:$value;"; - } - } - - return $css; - } - - /** - * Compiles the `src` into valid CSS. - * - * @since 6.0.0 - * - * @param string $font_family Font family. - * @param array $value Value to process. - * @return string The CSS. - */ - private function compile_src( $font_family, array $value ) { - $src = "local($font_family)"; - - foreach ( $value as $item ) { - - if ( str_starts_with( $item['url'], get_site_url() ) ) { - $item['url'] = wp_make_link_relative( $item['url'] ); - } - - $src .= ( 'data' === $item['format'] ) - ? ", url({$item['url']})" - : ", url('{$item['url']}') format('{$item['format']}')"; - } - return $src; - } - - /** - * Compiles the font variation settings. - * - * @since 6.0.0 - * - * @param array $font_variation_settings Array of font variation settings. - * @return string The CSS. - */ - private function compile_variations( array $font_variation_settings ) { - $variations = ''; - - foreach ( $font_variation_settings as $key => $value ) { - $variations .= "$key $value"; - } - - return $variations; - } -} diff --git a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider.php b/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider.php deleted file mode 100644 index 5b7f5ece335b1..0000000000000 --- a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-provider.php +++ /dev/null @@ -1,66 +0,0 @@ -webfonts = $this->fonts; - } - - /** - * This method is here to wire WP_Fonts_Provider::do_item() to this - * deprecated class to ensure the fonts get set. - * - * @param array[] $fonts Fonts to be processed. - */ - public function set_fonts( array $fonts ) { - parent::set_fonts( $fonts ); - $this->webfonts = $this->fonts; - } -} diff --git a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-utils.php b/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-utils.php deleted file mode 100644 index d0242acf9a2b6..0000000000000 --- a/lib/experimental/fonts-api/bc-layer/class-wp-webfonts-utils.php +++ /dev/null @@ -1,85 +0,0 @@ -wp_fonts = ! empty( $wp_fonts ) ? $wp_fonts : wp_fonts(); - } - - /** - * Gets the font slug. - * - * @since X.X.X - * @deprecated Use WP_Fonts_Utils::convert_font_family_into_handle() or WP_Fonts_Utils::get_font_family_from_variation(). - * - * @param array|string $to_convert The value to convert into a slug. Expected as the web font's array - * or a font-family as a string. - * @return string|false The font slug on success, or false if the font-family cannot be determined. - */ - public static function get_font_slug( $to_convert ) { - $message = is_array( $to_convert ) - ? 'Use WP_Fonts_Utils::get_font_family_from_variation() to get the font family from an array and then WP_Fonts_Utils::convert_font_family_into_handle() to convert the font-family name into a handle' - : 'Use WP_Fonts_Utils::convert_font_family_into_handle() to convert the font-family name into a handle'; - _deprecated_function( __METHOD__, 'GB 14.9.1', $message ); - - if ( empty( $to_convert ) ) { - return false; - } - - $font_family_name = is_array( $to_convert ) - ? WP_Fonts_Utils::get_font_family_from_variation( $to_convert ) - : $to_convert; - - $slug = false; - if ( ! empty( $font_family_name ) ) { - $slug = WP_Fonts_Utils::convert_font_family_into_handle( $font_family_name ); - } - - return $slug; - } - - /** - * Initializes the API. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_fonts(). - */ - public static function init() { - _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_fonts()' ); - } - - /** - * Get the list of all registered font family handles. - * - * @since X.X.X - * @deprecated GB 15.8.0 Use wp_fonts()->get_registered_font_families(). - * - * @return string[] - */ - public function get_registered_font_families() { - _deprecated_function( __METHOD__, 'GB 15.8.0', 'wp_fonts()->get_registered_font_families()' ); - - return $this->wp_fonts->get_registered_font_families(); - } - - /** - * Gets the list of registered fonts. - * - * @since 6.0.0 - * @deprecated 14.9.1 Use wp_fonts()->get_registered(). - * - * @return array[] - */ - public function get_registered_webfonts() { - _deprecated_function( __METHOD__, '14.9.1', 'wp_fonts()->get_registered()' ); - - return $this->_get_registered_webfonts(); - } - - /** - * Gets the list of enqueued fonts. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_fonts()->get_enqueued(). - * - * @return array[] - */ - public function get_enqueued_webfonts() { - _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_fonts()->get_enqueued()' ); - - return $this->wp_fonts->queue; - } - - /** - * Gets the list of all fonts. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_fonts()->get_registered(). - * - * @return array[] - */ - public function get_all_webfonts() { - _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_fonts()->get_registered()' ); - - return $this->_get_registered_webfonts(); - } - - /** - * Registers a webfont. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_register_fonts(). - * - * @param array $webfont Web font to register. - * @param string $font_family_handle Optional. Font family handle for the given variation. - * Default empty string. - * @param string $variation_handle Optional. Handle for the variation to register. - * @param bool $silence_deprecation Optional. Silences the deprecation notice. For internal use. - * @return string|false The font family slug if successfully registered, else false. - */ - public function register_webfont( array $webfont, $font_family_handle = '', $variation_handle = '', $silence_deprecation = false ) { - if ( ! $silence_deprecation ) { - _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_register_fonts()' ); - } - - // Bail out if no variation passed as there's not to register. - if ( empty( $webfont ) ) { - return false; - } - - // Restructure definition: keyed by font-family and array of variations. - $font = array( $webfont ); - if ( WP_Fonts_Utils::is_defined( $font_family_handle ) ) { - $font = array( $font_family_handle => $font ); - } else { - $font = Gutenberg_Fonts_API_BC_Layer::migrate_deprecated_structure( $font, true ); - $font_family_handle = array_key_first( $font ); - } - - if ( empty( $font ) || empty( $font_family_handle ) ) { - return false; - } - - // If the variation handle was passed, add it as variation key. - if ( WP_Fonts_Utils::is_defined( $variation_handle ) ) { - $font[ $font_family_handle ] = array( $variation_handle => $font[ $font_family_handle ][0] ); - } - - // Register with the Fonts API. - $handle = wp_register_fonts( $font ); - if ( empty( $handle ) ) { - return false; - } - return array_pop( $handle ); - } - - /** - * Enqueue a font-family that has been already registered. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_enqueue_fonts(). - * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. - */ - public function enqueue_webfont( $font_family_name ) { - _deprecated_function( __METHOD__, 'GB 14.9.1', 'wp_enqueue_fonts()' ); - - wp_enqueue_fonts( array( $font_family_name ) ); - return true; - } - - /** - * Gets the registered webfonts in the original web font property structure keyed by each handle. - * - * @return array[] - */ - private function _get_registered_webfonts() { - $font_families = array(); - $registered = array(); - - // Find the registered font families. - foreach ( $this->wp_fonts->registered as $handle => $obj ) { - if ( ! $obj->extra['is_font_family'] ) { - continue; - } - - if ( ! isset( $registered[ $handle ] ) ) { - $registered[ $handle ] = array(); - } - - $font_families[ $handle ] = $obj->deps; - } - - // Build the return array structure. - foreach ( $font_families as $font_family_handle => $variations ) { - foreach ( $variations as $variation_handle ) { - $variation_obj = $this->wp_fonts->registered[ $variation_handle ]; - - $registered[ $font_family_handle ][ $variation_handle ] = $variation_obj->extra['font-properties']; - } - } - - return $registered; - } -} diff --git a/lib/experimental/fonts-api/bc-layer/webfonts-deprecations.php b/lib/experimental/fonts-api/bc-layer/webfonts-deprecations.php deleted file mode 100644 index 7a3a7bd013eea..0000000000000 --- a/lib/experimental/fonts-api/bc-layer/webfonts-deprecations.php +++ /dev/null @@ -1,260 +0,0 @@ - array[] $variations { - * An array of web font variations for this font-family. - * Each variation has the following structure. - * - * @type array $variation { - * @type string $provider The provider ID. Default 'local'. - * @type string $font-style The font-style property. Default 'normal'. - * @type string $font-weight The font-weight property. Default '400'. - * @type string $font-display The font-display property. Default 'fallback'. - * } - * } - * } - * @return string[] Array of registered font family handles. - */ - function wp_register_webfonts( array $webfonts ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_register_fonts()' ); - - $webfonts = Gutenberg_Fonts_API_BC_Layer::migrate_deprecated_structure( $webfonts ); - - return wp_register_fonts( $webfonts ); - } -} - -if ( ! function_exists( 'wp_register_webfont' ) ) { - /** - * Registers a single webfont. - * - * Example of how to register Source Serif Pro font with font-weight range of 200-900: - * - * If the font file is contained within the theme: - * - * - * wp_register_webfont( - * array( - * 'provider' => 'local', - * 'font-family' => 'Source Serif Pro', - * 'font-weight' => '200 900', - * 'font-style' => 'normal', - * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), - * ) - * ); - * - * - * @since 6.0.0 - * @deprecated 14.9.1 Use wp_register_fonts(). - * - * @param array $webfont Webfont to be registered. - * @return string|false The font family slug if successfully registered, else false. - */ - function wp_register_webfont( array $webfont ) { - _deprecated_function( __FUNCTION__, '14.9.1', 'wp_register_fonts()' ); - - return wp_fonts()->register_webfont( $webfont, '', '', false ); - } -} - -if ( ! function_exists( 'wp_enqueue_webfonts' ) ) { - /** - * Enqueues one or more font family and all of its variations. - * - * @since X.X.X - * @since GB 15.1 Use wp_enqueue_fonts() ihstead. - * - * @param string[] $font_families Font family(ies) to enqueue. - */ - function wp_enqueue_webfonts( array $font_families ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_enqueue_fonts()' ); - - wp_enqueue_fonts( $font_families ); - } -} - -if ( ! function_exists( 'wp_enqueue_webfont' ) ) { - /** - * Enqueue a single font family that has been registered beforehand. - * - * Example of how to enqueue Source Serif Pro font: - * - * - * wp_enqueue_webfont( 'Source Serif Pro' ); - * - * - * Font families should be enqueued from the `init` hook or later. - * - * @since 6.0.0 - * @deprecated GB 14.9.1 Use wp_enqueue_fonts() instead. - * - * @param string $font_family_name The font family name to be enqueued. - * @return bool True if successfully enqueued, else false. - */ - function wp_enqueue_webfont( $font_family_name ) { - _deprecated_function( __FUNCTION__, 'GB 14.9.1', 'wp_enqueue_fonts()' ); - - wp_enqueue_fonts( array( $font_family_name ) ); - return true; - } -} - -if ( ! function_exists( 'wp_enqueue_webfont_variations' ) ) { - /** - * Enqueues a specific set of web font variations. - * - * @since X.X.X - * @deprecated GB 15.1 Use wp_enqueue_font_variations() instead. - * - * @param string|string[] $variation_handles Variation handle (string) or handles (array of strings). - */ - function wp_enqueue_webfont_variations( $variation_handles ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_enqueue_font_variations()' ); - - wp_enqueue_font_variations( $variation_handles ); - } -} - -if ( ! function_exists( 'wp_deregister_webfont_variation' ) ) { - /** - * Deregisters a font variation. - * - * @since GB 14.9.1 - * @deprecated GB 15.1 Use wp_deregister_font_variation() instead. - * - * @param string $font_family_handle The font family for this variation. - * @param string $variation_handle The variation's handle to remove. - */ - function wp_deregister_webfont_variation( $font_family_handle, $variation_handle ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_deregister_font_variation()' ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - } -} - -if ( ! function_exists( 'wp_get_webfont_providers' ) ) { - /** - * Gets all registered providers. - * - * Return an array of providers, each keyed by their unique - * ID (i.e. the `$id` property in the provider's object) with - * an instance of the provider (object): - * ID => provider instance - * - * Each provider contains the business logic for how to - * process its specific font service (i.e. local or remote) - * and how to generate the `@font-face` styles for its service. - * - * @since X.X.X - * @deprecated GB 14.9.1 Use wp_fonts()->get_providers(). - * - * @return string[] All registered providers, each keyed by their unique ID. - */ - function wp_get_webfont_providers() { - _deprecated_function( __FUNCTION__, '14.9.1', 'wp_fonts()->get_providers()' ); - - $providers = array(); - foreach ( wp_fonts()->get_providers() as $id => $config ) { - $providers[ $id ] = $config['class']; - } - - return $providers; - } -} - -if ( ! function_exists( 'wp_register_webfont_provider' ) ) { - /** - * Registers a custom font service provider. - * - * A webfont provider contains the business logic for how to - * interact with a remote font service and how to generate - * the `@font-face` styles for that remote service. - * - * How to register a custom font service provider: - * 1. Load its class file into memory before registration. - * 2. Pass the class' name to this function. - * - * For example, for a class named `My_Custom_Font_Service_Provider`: - * ``` - * wp_register_webfont_provider( My_Custom_Font_Service_Provider::class ); - * ``` - * - * @since X.X.X - * @deprecated GB 15.1 Use wp_register_font_provider() instead. - * - * @param string $name The provider's name. - * @param string $classname The provider's class name. - * The class should be a child of `WP_Webfonts_Provider`. - * See {@see WP_Webfonts_Provider}. - * - * @return bool True if successfully registered, else false. - */ - function wp_register_webfont_provider( $name, $classname ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_register_font_provider' ); - - return wp_register_font_provider( $name, $classname ); - } -} - -if ( ! function_exists( 'wp_print_webfonts' ) ) { - /** - * Invokes each provider to process and print its styles. - * - * @since GB 14.9.1 - * @deprecated GB 15.1 Use wp_print_fonts() instead. - * - * @param string|string[]|false $handles Optional. Items to be processed: queue (false), - * single item (string), or multiple items (array of strings). - * Default false. - * @return array|string[] Array of web font handles that have been processed. - * An empty array if none were processed. - */ - function wp_print_webfonts( $handles = false ) { - _deprecated_function( __FUNCTION__, 'GB 15.1', 'wp_print_fonts' ); - - return wp_print_fonts( $handles ); - } -} diff --git a/lib/experimental/fonts-api/class-wp-fonts-provider-local.php b/lib/experimental/fonts-api/class-wp-fonts-provider-local.php deleted file mode 100644 index 019861a9ae191..0000000000000 --- a/lib/experimental/fonts-api/class-wp-fonts-provider-local.php +++ /dev/null @@ -1,236 +0,0 @@ -style_tag_atts = array( 'type' => 'text/css' ); - } - } - - /** - * Gets the `@font-face` CSS styles for locally-hosted font files. - * - * This method does the following processing tasks: - * 1. Orchestrates an optimized `src` (with format) for browser support. - * 2. Generates the `@font-face` for all its fonts. - * - * @since X.X.X - * - * @return string The `@font-face` CSS. - */ - public function get_css() { - $css = ''; - - foreach ( $this->fonts as $font ) { - // Order the font's `src` items to optimize for browser support. - $font = $this->order_src( $font ); - - // Build the @font-face CSS for this font. - $css .= '@font-face{' . $this->build_font_face_css( $font ) . '}'; - } - - return $css; - } - - /** - * Order `src` items to optimize for browser support. - * - * @since X.X.X - * - * @param array $font Font to process. - * @return array - */ - private function order_src( array $font ) { - if ( ! is_array( $font['src'] ) ) { - $font['src'] = (array) $font['src']; - } - - $src = array(); - $src_ordered = array(); - - foreach ( $font['src'] as $url ) { - // Add data URIs first. - if ( str_starts_with( trim( $url ), 'data:' ) ) { - $src_ordered[] = array( - 'url' => $url, - 'format' => 'data', - ); - continue; - } - $format = pathinfo( $url, PATHINFO_EXTENSION ); - $src[ $format ] = $url; - } - - // Add woff2. - if ( ! empty( $src['woff2'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff2'], - 'format' => 'woff2', - ); - } - - // Add woff. - if ( ! empty( $src['woff'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff'], - 'format' => 'woff', - ); - } - - // Add ttf. - if ( ! empty( $src['ttf'] ) ) { - $src_ordered[] = array( - 'url' => $src['ttf'], - 'format' => 'truetype', - ); - } - - // Add eot. - if ( ! empty( $src['eot'] ) ) { - $src_ordered[] = array( - 'url' => $src['eot'], - 'format' => 'embedded-opentype', - ); - } - - // Add otf. - if ( ! empty( $src['otf'] ) ) { - $src_ordered[] = array( - 'url' => $src['otf'], - 'format' => 'opentype', - ); - } - $font['src'] = $src_ordered; - - return $font; - } - - /** - * Builds the font-family's CSS. - * - * @since X.X.X - * - * @param array $font Font to process. - * @return string This font-family's CSS. - */ - private function build_font_face_css( array $font ) { - $css = ''; - - // Wrap font-family in quotes if it contains spaces - // and is not already wrapped in quotes. - if ( - str_contains( $font['font-family'], ' ' ) && - ! str_contains( $font['font-family'], '"' ) && - ! str_contains( $font['font-family'], "'" ) - ) { - $font['font-family'] = '"' . $font['font-family'] . '"'; - } - - foreach ( $font as $key => $value ) { - - // Skip "provider", since it's for internal API use, - // and not a valid CSS property. - if ( 'provider' === $key ) { - continue; - } - - // Compile the "src" parameter. - if ( 'src' === $key ) { - $value = $this->compile_src( $value ); - } - - // If font-variation-settings is an array, convert it to a string. - if ( 'font-variation-settings' === $key && is_array( $value ) ) { - $value = $this->compile_variations( $value ); - } - - if ( ! empty( $value ) ) { - $css .= "$key:$value;"; - } - } - - return $css; - } - - /** - * Compiles the `src` into valid CSS. - * - * @since X.X.X - * - * @param array $value Value to process. - * @return string The CSS. - */ - private function compile_src( array $value ) { - $src = ''; - - foreach ( $value as $item ) { - $src .= ( 'data' === $item['format'] ) - ? ", url({$item['url']})" - : ", url('{$item['url']}') format('{$item['format']}')"; - } - - $src = ltrim( $src, ', ' ); - return $src; - } - - /** - * Compiles the font variation settings. - * - * @since X.X.X - * - * @param array $font_variation_settings Array of font variation settings. - * @return string The CSS. - */ - private function compile_variations( array $font_variation_settings ) { - $variations = ''; - - foreach ( $font_variation_settings as $key => $value ) { - $variations .= "$key $value"; - } - - return $variations; - } -} diff --git a/lib/experimental/fonts-api/class-wp-fonts-provider.php b/lib/experimental/fonts-api/class-wp-fonts-provider.php deleted file mode 100644 index b59c59d6b8938..0000000000000 --- a/lib/experimental/fonts-api/class-wp-fonts-provider.php +++ /dev/null @@ -1,129 +0,0 @@ -fonts = $fonts; - } - - /** - * Prints the generated styles. - * - * @since X.X.X - */ - public function print_styles() { - printf( - $this->get_style_element(), - $this->get_css() - ); - } - - /** - * Gets the `@font-face` CSS for the provider's fonts. - * - * This method is where the provider does it processing to build the - * needed `@font-face` CSS for all of its fonts. Specifics of how - * this processing is done is contained in each provider. - * - * @since X.X.X - * - * @return string The `@font-face` CSS. - */ - abstract public function get_css(); - - /** - * Gets the `\n"; - } - - /** - * Gets the defined \n", - $font_faces['merriweather-200-900-normal'] - ), - ), - 'print Source Serif Pro for local provider' => array( - 'setup' => array( - 'provider' => array( 'local' => $providers['local'] ), - 'provider_handles' => array( 'local' => $local_font_handles ), - 'registered' => $local_fonts, - 'enqueued' => array( 'source-serif-pro' ), - ), - 'expected_done' => array( 'source-serif-pro', 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ), - 'expected_output' => sprintf( - "\n", - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - 'print all fonts for local provider' => array( - 'setup' => array( - 'provider' => array( 'local' => $providers['local'] ), - 'provider_handles' => array( 'local' => $local_font_handles ), - 'registered' => $local_fonts, - 'enqueued' => array( 'merriweather', 'source-serif-pro' ), - ), - 'expected_done' => array( - 'merriweather', - 'merriweather-200-900-normal', - 'source-serif-pro', - 'Source Serif Pro-300-normal', - 'Source Serif Pro-900-italic', - ), - 'expected_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - - // All providers registered with multiple fonts. - - 'print font1 when all providers registered' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font1' ) ) ), - 'expected_done' => array( 'font1', 'font1-300-normal', 'font1-300-italic', 'font1-900-normal' ), - 'expected_output' => sprintf( - '%s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'] - ), - ), - 'print all mock fonts when all providers registered' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font1', 'font2', 'font3' ) ) ), - 'expected_done' => array( - 'font1', - 'font1-300-normal', - 'font1-300-italic', - 'font1-900-normal', - 'font2', - 'font2-200-900-normal', - 'font2-200-900-italic', - 'font3', - 'font3-bold-normal', - ), - 'expected_output' => sprintf( - '%s; %s; %s; %s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'], - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'], - $font_faces['font3-bold-normal'] - ), - ), - 'print merriweather when all providers registered' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather' ) ) ), - 'expected_done' => array( 'merriweather', 'merriweather-200-900-normal' ), - 'expected_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'] - ), - ), - 'print all local fonts when all providers registered' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather', 'source-serif-pro' ) ) ), - 'expected_done' => array( - 'merriweather', - 'merriweather-200-900-normal', - 'source-serif-pro', - 'Source Serif Pro-300-normal', - 'Source Serif Pro-900-italic', - ), - 'expected_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - - 'print all fonts for all providers' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => $all_variation_handles ) ), - 'expected_done' => $all_variation_handles, - 'expected_output' => - sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ) . - sprintf( - '%s; %s; %s; %s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'], - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'], - $font_faces['font3-bold-normal'] - ), - ), - - // Specific variations enqueued. - // Validates that only these specific variations print once. - - 'specific variation: 1 local' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal' ) ) ), - 'expected_done' => array( 'merriweather-200-900-normal' ), - 'expected_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'] - ), - ), - 'specific variation: 1 local from different font families' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ) ) ), - 'expected_done' => array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ), - 'expected_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - 'specific variation: 1 local and 1 mock' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'merriweather-200-900-normal', 'font2-200-900-normal' ) ) ), - 'expected_done' => array( 'merriweather-200-900-normal', 'font2-200-900-normal' ), - 'expected_output' => sprintf( - "\n" . - '%s\n', - $font_faces['merriweather-200-900-normal'], - $font_faces['font2-200-900-normal'] - ), - ), - 'specific variation: 1 mock and 1 local' => array( - 'setup' => array_merge( $setup_all, array( 'enqueued' => array( 'font2-200-900-normal', 'Source Serif Pro-300-normal' ) ) ), - 'expected_done' => array( 'font2-200-900-normal', 'Source Serif Pro-300-normal' ), - 'expected_output' => sprintf( - "\n" . - '%s\n', - $font_faces['Source Serif Pro-300-normal'], - $font_faces['font2-200-900-normal'] - ), - ), - ); - } - - protected function get_data_registry() { - return array( - 'lato' => array(), - 'merriweather' => array( - 'merriweather-200-900-normal' => array( - 'font-family' => 'Merriweather', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', - ), - ), - 'Source Serif Pro' => array( - 'Source Serif Pro-300-normal' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '300', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'Source Serif Pro-900-italic' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'font-display' => 'fallback', - ), - ), - 'my-font' => array( - 'my-font-300-normal' => array( - 'font-family' => 'My Font', - 'font-weight' => '300', - 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', - ), - 'my-font-300-italic' => array( - 'font-family' => 'My Font', - 'font-weight' => '300', - 'font-style' => 'italic', - 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', - ), - 'my-font-900-normal' => array( - 'font-family' => 'My Font', - 'font-weight' => '900', - 'src' => 'https://example.com/assets/fonts/my-font.ttf.woff2', - ), - ), - ); - } - - /** - * Gets the provider definitions. - * - * @since X.X.X - * - * @param string $provider_id Optional. Provider ID to get. Default empty string. - * @return array - */ - protected function get_provider_definitions( $provider_id = '' ) { - $providers = array( - 'mock' => array( - 'id' => 'mock', - 'class' => Mock_Provider::class, - ), - 'local' => array( - 'id' => 'local', - 'class' => WP_Fonts_Provider_Local::class, - ), - ); - - if ( '' === $provider_id ) { - return $providers; - } - - if ( isset( $providers[ $provider_id ] ) ) { - return $providers[ $provider_id ]; - } - - return array( - 'id' => $provider_id, - 'class' => '', - ); - } - - /** - * Gets font definitions for both local and mock providers. - * - * @since X.X.X - * - * @return array|string[][][] - */ - protected function get_registered_fonts() { - return array_merge( - $this->get_registered_local_fonts(), - $this->get_registered_mock_fonts() - ); - } - - /** - * Returns an array of font-face styles that matches the font definitions - * in get_registered_local_fonts() and get_registered_mock_fonts(). - * - * @since X.X.X - * - * @return string[] - */ - protected function get_registered_fonts_css() { - return array( - 'merriweather-200-900-normal' => << << << 'font1-300-normal', - 'font1-300-italic' => 'font1-300-italic', - 'font1-900-normal' => 'font1-900-normal', - 'font2-200-900-normal' => 'font2-200-900-normal', - 'font2-200-900-italic' => 'font2-200-900-italic', - 'font3-bold-normal' => 'font3-bold-normal', - ); - } - - /** - * Gets font definitions for local provider. - * - * @since X.X.X - * - * @return string[][][] - */ - protected function get_registered_local_fonts() { - return array( - 'lato' => array(), - 'merriweather' => array( - 'merriweather-200-900-normal' => array( - 'provider' => 'local', - 'font-family' => 'Merriweather', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', - ), - ), - 'Source Serif Pro' => array( - 'Source Serif Pro-300-normal' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '300', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'Source Serif Pro-900-italic' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '900', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'font-display' => 'fallback', - ), - ), - ); - } - - /** - * Gets font definitions for mock provider. - * - * @since X.X.X - * - * @return string[][][] - */ - protected function get_registered_mock_fonts() { - return array( - 'font1' => array( - 'font1-300-normal' => array( - 'provider' => 'mock', - 'font-family' => 'Font 1', - 'font-weight' => '300', - 'font-style' => 'normal', - 'font-display' => 'fallback', - ), - 'font1-300-italic' => array( - 'provider' => 'mock', - 'font-family' => 'Font 1', - 'font-weight' => '300', - 'font-style' => 'italic', - 'font-display' => 'fallback', - ), - 'font1-900-normal' => array( - 'provider' => 'mock', - 'font-family' => 'Font 1', - 'font-weight' => '900', - 'font-style' => 'normal', - 'font-display' => 'fallback', - ), - ), - 'font2' => array( - 'font2-200-900-normal' => array( - 'provider' => 'mock', - 'font-family' => 'Font 2', - 'font-weight' => '200 900', - 'font-style' => 'normal', - 'font-display' => 'fallback', - ), - 'font2-200-900-italic' => array( - 'provider' => 'mock', - 'font-family' => 'Font 2', - 'font-weight' => '200 900', - 'font-style' => 'italic', - 'font-display' => 'fallback', - ), - ), - 'font3' => array( - 'font3-bold-normal' => array( - 'provider' => 'mock', - 'font-family' => 'Font 3', - 'font-weight' => 'bold', - 'font-style' => 'normal', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - ), - ), - ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_print_user_selected_fonts() { - $global_styles = $this->get_mock_user_selected_fonts_global_styles(); - $font_faces = $this->get_registered_fonts_css(); - - return array( - 'print font1' => array( - 'global_styles' => $global_styles['font1'], - 'expected_done' => array( - 'font1-300-normal', - 'font1-300-italic', - 'font1-900-normal', - 'font1', - ), - 'expected_output' => sprintf( - '%s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'] - ), - ), - 'print font2' => array( - 'global_styles' => $global_styles['font2'], - 'expected_done' => array( 'font2-200-900-normal', 'font2-200-900-italic', 'font2' ), - 'expected_output' => sprintf( - '%s; %s\n', - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'] - ), - ), - 'print font3' => array( - 'global_styles' => $global_styles['font3'], - 'expected_done' => array( 'font3', 'font3-bold-normal' ), - 'expected_output' => sprintf( - '%s\n', - $font_faces['font3-bold-normal'] - ), - ), - 'print all fonts' => array( - 'global_styles' => $global_styles['all'], - 'expected_done' => array( - 'font1-300-normal', - 'font1-300-italic', - 'font1-900-normal', - 'font1', - 'font2-200-900-normal', - 'font2-200-900-italic', - 'font2', - 'font3-bold-normal', - 'font3', - ), - 'expected_output' => sprintf( - '%s; %s; %s; %s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'], - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'], - $font_faces['font3-bold-normal'] - ), - ), - 'print all valid fonts' => array( - 'global_styles' => $global_styles['all with invalid element'], - 'expected_done' => array( - 'font1-300-normal', - 'font1-300-italic', - 'font1-900-normal', - 'font1', - 'font2-200-900-normal', - 'font2-200-900-italic', - 'font2', - 'font3-bold-normal', - 'font3', - ), - 'expected_output' => sprintf( - '%s; %s; %s; %s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'], - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'], - $font_faces['font3-bold-normal'] - ), - ), - ); - } - - /** - * Gets user-selected fonts for global styles for the mock provider. - * - * @since X.X.X - * - * @return array - */ - protected function get_mock_user_selected_fonts_global_styles() { - return array( - 'font1' => array( - 'elements' => array( - 'heading' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'normal', - 'fontWeight' => '300', - ), - ), - 'caption' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'italic', - 'fontWeight' => '300', - ), - ), - ), - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ), - 'font2' => array( - 'elements' => array( - 'heading' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font2', - 'fontStyle' => 'normal', - 'fontWeight' => '200-900', - ), - ), - 'button' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font2', - 'fontStyle' => 'italic', - 'fontWeight' => '200-900', - ), - ), - ), - ), - 'font3' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font3', - 'fontStyle' => 'normal', - 'fontWeight' => 'bold', - ), - ), - 'all' => array( - 'elements' => array( - 'link' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'italic', - 'fontWeight' => '300', - ), - ), - 'heading' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ), - 'caption' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'italic', - 'fontWeight' => '300', - ), - ), - 'button' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font2', - 'fontStyle' => 'normal', - 'fontWeight' => '200-900', - ), - ), - ), - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font3', - 'fontStyle' => 'normal', - 'fontWeight' => 'bold', - ), - ), - 'all with invalid element' => array( - 'elements' => array( - 'link' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'italic', - 'fontWeight' => '300', - ), - ), - 'heading' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ), - 'caption' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'italic', - 'fontWeight' => '300', - ), - ), - 'button' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font2', - 'fontStyle' => 'normal', - 'fontWeight' => '200-900', - ), - ), - 'invalid' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font2', - 'fontStyle' => 'italic', - 'fontWeight' => '200-900', - ), - ), - ), - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font3', - 'fontStyle' => 'normal', - 'fontWeight' => 'bold', - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpDeregisterFontFamily.php b/phpunit/tests/fonts-api/wpDeregisterFontFamily.php deleted file mode 100644 index 59db2e2abc826..0000000000000 --- a/phpunit/tests/fonts-api/wpDeregisterFontFamily.php +++ /dev/null @@ -1,74 +0,0 @@ -set_up_mock( 'remove_font_family' ); - $mock->expects( $this->once() ) - ->method( 'remove_font_family' ) - ->with( - $this->identicalTo( $font_family_handle ) - ); - - wp_deregister_font_family( $font_family_handle ); - } - - /** - * Integration test for enqueuing before registering a font family and all of its variations. - * - * @dataProvider data_font_family_handles - * - * @param string $font_family Font family to test. - */ - public function test_should_deregister_before_registration( $font_family ) { - wp_deregister_font_family( $font_family ); - - $this->assertIsArray( $this->get_registered(), 'Registration queue should be an array' ); - $this->assertEmpty( $this->get_registered(), 'Registration queue should be empty after deregistering' ); - } - - /** - * Integration test for deregistering a font family and all of its variations. - * - * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations - * - * @param string $font_family Font family to test. - * @param array $inputs Font family(ies) and variations to pre-register. - * @param array $registered_handles Expected handles after registering. - * @param array $expected Array of expected handles. - */ - public function test_deregister_after_registration( $font_family, array $inputs, array $registered_handles, array $expected ) { - foreach ( $inputs as $handle => $variations ) { - $this->setup_register( $handle, $variations ); - } - // Test the before state, just to make sure. - $this->assertSame( $registered_handles, $this->get_registered_handles(), 'Font family and variations should be registered before deregistering' ); - - wp_deregister_font_family( $font_family ); - - // Test after deregistering. - $this->assertIsArray( $this->get_registered_handles(), 'Registration queue should be an array' ); - $this->assertSame( $expected, $this->get_registered_handles(), 'Registration queue should match after deregistering' ); - } -} diff --git a/phpunit/tests/fonts-api/wpDeregisterFontVariation.php b/phpunit/tests/fonts-api/wpDeregisterFontVariation.php deleted file mode 100644 index 78b4bf51f758a..0000000000000 --- a/phpunit/tests/fonts-api/wpDeregisterFontVariation.php +++ /dev/null @@ -1,298 +0,0 @@ -wp_fonts = wp_fonts(); - $this->fonts_to_register = $this->get_registered_local_fonts(); - } - - /** - * Sets up the unit test by mocking the WP_Dependencies object using stdClass and - * registering each font family directly to the WP_Webfonts::$registered property - * and its variations to the mocked $deps property. - */ - private function setup_unit_test() { - $this->setup_registration_mocks( $this->fonts_to_register, $this->wp_fonts ); - } - - /** - * Sets up the integration test by properly registering each font family and its variations - * by using the WP_Webfonts::add() and WP_Webfonts::add_variation() methods. - */ - private function setup_integration_test() { - foreach ( $this->fonts_to_register as $font_family_handle => $variations ) { - $this->setup_register( $font_family_handle, $variations, $this->wp_fonts ); - } - } - - /** - * Testing the test setup to ensure it works. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - */ - public function test_mocked_setup( $font_family_handle, $variation_handle ) { - $this->setup_unit_test(); - - $this->assertArrayHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be in the registered queue before removal' ); - $this->assertContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should be in its font family deps before removal' ); - } - - /** - * Unit test for deregistering a font-family's variation using mock of WP_Webfonts. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family to test. - * @param string $variation_handle Variation's handle to test. - */ - public function test_should_deregister_when_mocked( $font_family_handle, $variation_handle ) { - $mock = $this->set_up_mock( 'remove_variation' ); - $mock->expects( $this->once() ) - ->method( 'remove_variation' ) - ->with( - $this->identicalTo( $font_family_handle, $variation_handle ) - ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - } - - /** - * Unit test. - * - * @dataProvider data_should_do_nothing - * - * @param string $font_family Font family name. - * @param string $font_family_handle Font family handle. - * @param string $variation_handle Variation handle to remove. - */ - public function test_unit_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) { - // Set up the test. - unset( $this->fonts_to_register[ $font_family ] ); - $this->setup_unit_test(); - $registered_queue = $this->wp_fonts->registered; - - // Run the tests. - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $font_family_handle, $this->wp_fonts->registered, 'Font family should not be registered' ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertSame( $registered_queue, $this->wp_fonts->registered, 'Registered queue should not have changed' ); - } - - /** - * Integration test. - * - * @dataProvider data_should_do_nothing - * - * @param string $font_family Font family name. - * @param string $font_family_handle Font family handle. - * @param string $variation_handle Variation handle to remove. - */ - public function test_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) { - // Set up the test. - unset( $this->fonts_to_register[ $font_family ] ); - $this->setup_integration_test(); - $registered_queue = $this->wp_fonts->get_registered(); - - // Run the tests. - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $font_family_handle, $this->wp_fonts->registered, 'Font family should not be registered' ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertSameSets( $registered_queue, $this->wp_fonts->get_registered(), 'Registered queue should not have changed' ); - } - - /** - * Data provider for testing removal of variations. - * - * @return array - */ - public function data_should_do_nothing() { - return array( - 'Font with 1 variation' => array( - 'font_family' => 'merriweather', - 'font_family_handle' => 'merriweather', - 'variation_handle' => 'merriweather-200-900-normal', - ), - 'Font with multiple variations' => array( - 'font_family' => 'Source Serif Pro', - 'font_family_handle' => 'source-serif-pro', - 'variation_handle' => 'Source Serif Pro-300-normal', - ), - ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - $this->setup_remove_variation_from_registered( $variation_handle ); - - // Run the tests. - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - $this->setup_remove_variation_from_registered( $variation_handle ); - - // Run the tests. - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - - $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be not be in registered queue' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - - $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' ); - - wp_deregister_font_variation( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be not be in registered queue' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Remove the variation handle from the font family's deps. - * - * @param string $font_family_handle Font family. - * @param string $variation_handle The variation handle to remove. - */ - private function setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ) { - foreach ( $this->wp_fonts->registered[ $font_family_handle ]->deps as $index => $vhandle ) { - if ( $variation_handle !== $vhandle ) { - continue; - } - unset( $this->wp_fonts->registered[ $font_family_handle ]->deps[ $index ] ); - break; - } - } - - /** - * Removes the variation from the WP_Webfonts::$registered queue. - * - * @param string $variation_handle The variation handle to remove. - */ - private function setup_remove_variation_from_registered( $variation_handle ) { - unset( $this->wp_fonts->registered[ $variation_handle ] ); - } -} diff --git a/phpunit/tests/fonts-api/wpEnqueueFontVariations.php b/phpunit/tests/fonts-api/wpEnqueueFontVariations.php deleted file mode 100644 index 6e026d99f5889..0000000000000 --- a/phpunit/tests/fonts-api/wpEnqueueFontVariations.php +++ /dev/null @@ -1,82 +0,0 @@ -set_up_mock( 'enqueue' ); - $mock->expects( $this->once() ) - ->method( 'enqueue' ) - ->with( - $this->identicalTo( $handles ) - ); - - wp_enqueue_font_variations( $handles ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_variation_handles() { - return array( - '1 variation handle' => array( 'merriweather-200-900-normal' ), - 'multiple same font family handles' => array( array( 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ) ), - 'handles from different font families' => array( array( 'merriweather-200-900-normal', 'Source Serif Pro-900-italic' ) ), - ); - } - - /** - * Integration test for enqueuing one or more specific variations. - * - * @dataProvider data_enqueue_variations - * - * @param string|string[] $handles Variation handles to test. - * @param array $expected Expected queue. - */ - public function test_should_enqueue_after_registration( $handles, array $expected ) { - foreach ( $this->get_data_registry() as $handle => $variations ) { - $this->setup_register( $handle, $variations ); - } - - wp_enqueue_font_variations( $handles ); - $this->assertEmpty( $this->get_queued_before_register(), '"queued_before_register" queue should be empty' ); - $this->assertSame( $expected, $this->get_enqueued_handles(), 'Queue should contain the given handles' ); - } - - /** - * Integration test for enqueuing before registering one or more specific variations. - * - * @dataProvider data_enqueue_variations - * - * @param string|string[] $handles Variation handles to test. - * @param array $not_used Not used. - * @param array $expected Expected "queued_before_register" queue. - */ - public function test_should_enqueue_before_registration( $handles, array $not_used, array $expected ) { - wp_enqueue_font_variations( $handles ); - - $this->assertSame( $expected, $this->get_queued_before_register(), '"queued_before_register" queue should contain the given handles' ); - $this->assertEmpty( $this->get_enqueued_handles(), 'Queue should be empty' ); - } -} diff --git a/phpunit/tests/fonts-api/wpEnqueueFonts.php b/phpunit/tests/fonts-api/wpEnqueueFonts.php deleted file mode 100644 index c00fb0f2dfdae..0000000000000 --- a/phpunit/tests/fonts-api/wpEnqueueFonts.php +++ /dev/null @@ -1,122 +0,0 @@ -set_up_mock( 'enqueue' ); - $mock->expects( $this->once() ) - ->method( 'enqueue' ) - ->with( - $this->identicalTo( $expected_handles ) - ); - - wp_enqueue_fonts( $font_families ); - } - - /** - * Integration test for enqueuing a font family and all of its variations. - * - * @dataProvider data_should_enqueue - * - * @param string[] $font_families Font families to test. - * @param string[] $expected_handles Expected handles passed to WP_Fonts::enqueue(). - */ - public function test_should_enqueue_after_registration( $font_families, $expected_handles ) { - // Register the font-families. - foreach ( $this->get_data_registry() as $handle => $variations ) { - $this->setup_register( $handle, $variations ); - } - - wp_enqueue_fonts( $font_families ); - - $this->assertEmpty( $this->get_queued_before_register(), '"queued_before_register" queue should be empty' ); - $this->assertSame( $expected_handles, $this->get_enqueued_handles(), 'Queue should contain the given font family(ies)' ); - } - - /** - * Integration test for enqueuing before registering a font family and all of its variations. - * - * @dataProvider data_should_enqueue - * - * @param string[] $font_families Font families to test. - * @param string[] $expected_handles Expected handles passed to WP_Fonts::enqueue(). - */ - public function test_should_enqueue_before_registration( $font_families, $expected_handles ) { - wp_enqueue_fonts( $font_families ); - - // Set up what "queued_before_register" queue should be. - $expected = array(); - foreach ( $expected_handles as $handle ) { - $expected[ $handle ] = null; - } - $this->assertSame( $expected, $this->get_queued_before_register(), '"queued_before_register" queue should contain the given font family(ies)' ); - $this->assertEmpty( $this->get_enqueued_handles(), 'Queue should be empty' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_enqueue() { - return array( - '1: single word handle' => array( - 'font_families' => array( 'lato' ), - 'expected_handles' => array( 'lato' ), - ), - '1: multiple word handle' => array( - 'font_families' => array( 'source-serif-pro' ), - 'expected_handles' => array( 'source-serif-pro' ), - ), - '1: single word name' => array( - 'font_families' => array( 'Merriweather' ), - 'expected_handles' => array( 'merriweather' ), - ), - '1: multiple word name' => array( - 'font_families' => array( 'My Font' ), - 'expected_handles' => array( 'my-font' ), - ), - '>1: single word handle' => array( - 'font_families' => array( 'lato', 'merriweather' ), - 'expected_handles' => array( 'lato', 'merriweather' ), - ), - '>1: multiple word handle' => array( - 'font_families' => array( 'source-serif-pro', 'my-font' ), - 'expected_handles' => array( 'source-serif-pro', 'my-font' ), - ), - '>1: single word name' => array( - 'font_families' => array( 'Lato', 'Merriweather' ), - 'expected_handles' => array( 'lato', 'merriweather' ), - ), - '>1: multiple word name' => array( - 'font_families' => array( 'My Font', 'Source Serif Pro' ), - 'expected_handles' => array( 'my-font', 'source-serif-pro' ), - ), - '>1: mixture of word handles and names' => array( - 'font_families' => array( 'Source Serif Pro', 'Merriweather', 'my-font', 'Lato' ), - 'expected_handles' => array( 'source-serif-pro', 'merriweather', 'my-font', 'lato' ), - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts.php b/phpunit/tests/fonts-api/wpFonts.php deleted file mode 100644 index 3b33e58edf299..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts.php +++ /dev/null @@ -1,38 +0,0 @@ -assertInstanceOf( WP_Fonts::class, wp_fonts() ); - } - - public function test_global_set() { - global $wp_fonts; - $this->assertNull( $wp_fonts ); - $instance = wp_fonts(); - $this->assertInstanceOf( WP_Fonts::class, $wp_fonts ); - $this->assertSame( $instance, $wp_fonts ); - } - - public function test_local_provider_is_automatically_registered() { - $expected = array( - 'local' => array( - 'class' => 'WP_Fonts_Provider_Local', - 'fonts' => array(), - ), - ); - $this->assertSame( $expected, wp_fonts()->get_providers() ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/add.php b/phpunit/tests/fonts-api/wpFonts/add.php deleted file mode 100644 index 85d3d98c0f355..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/add.php +++ /dev/null @@ -1,44 +0,0 @@ -assertTrue( $wp_fonts->add( $handle, false ), 'Registering a handle should return true' ); - $this->assertCount( 1, $wp_fonts->registered ); - $this->assertArrayHasKey( $handle, $wp_fonts->registered, 'Font family handle should be in the registry after registration' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_handles() { - return array( - 'name: multiple' => array( 'Source Serif Pro' ), - 'handle: multiple' => array( 'source-serif-pro' ), - 'name: single' => array( 'Merriweather' ), - 'handle: single' => array( 'merriweather' ), - 'handle: variation' => array( 'my-custom-font-200-900-normal' ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/addFontFamily.php b/phpunit/tests/fonts-api/wpFonts/addFontFamily.php deleted file mode 100644 index 69b6bccf5ae35..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/addFontFamily.php +++ /dev/null @@ -1,65 +0,0 @@ -add_font_family( $font_family ); - - $this->assertSame( $expected, $font_family_handle, 'Registering a font-family should return its handle' ); - $this->assertCount( 1, $wp_fonts->registered ); - $this->assertArrayHasKey( $font_family_handle, $wp_fonts->registered, 'Font family handle should be in the registry after registration' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_handles() { - return array( - 'name: multiple' => array( - 'font_family' => 'Source Serif Pro', - 'expected' => 'source-serif-pro', - ), - 'handle: multiple' => array( - 'font_family' => 'source-serif-pro', - 'expected' => 'source-serif-pro', - ), - 'name: single' => array( - 'font_family' => 'Merriweather', - 'expected' => 'merriweather', - ), - 'handle: single' => array( - 'font_family' => 'merriweather', - 'expected' => 'merriweather', - ), - 'handle: variation' => array( - 'font_family' => 'my-custom-font-200-900-normal', - 'expected' => 'my-custom-font-200-900-normal', - ), - 'name: multiple font-families' => array( - 'font_family' => 'Source Serif Pro, Merriweather', - 'expected' => 'source-serif-pro', - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/addVariation.php b/phpunit/tests/fonts-api/wpFonts/addVariation.php deleted file mode 100644 index 5b6d93bcc99fe..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/addVariation.php +++ /dev/null @@ -1,149 +0,0 @@ -add( $font_family_handle, false ); - - $variation_handle = $wp_fonts->add_variation( $font_family_handle, $variation, $variation_handle ); - $this->assertSame( $expected, $variation_handle, 'Registering a variation should return its handle' ); - $this->assertArrayHasKey( $variation_handle, $wp_fonts->registered, 'Variation handle should be in the registry after registration' ); - $this->assertSame( array( $expected ), $this->get_variations( $font_family_handle, $wp_fonts ), 'Variation should be registered to font family' ); - } - - /** - * @dataProvider data_valid_variation - * - * @param string|bool $expected Expected results. - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - * @param string $variation_handle Optional. The variation's handle. - */ - public function test_should_not_reregister_font_family( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { - $wp_fonts = new WP_Fonts(); - $wp_fonts->add( $font_family_handle, false ); - - $variation_handle = $wp_fonts->add_variation( $font_family_handle, $variation, $variation_handle ); - - // Font family should appear only once in the registered queue. - $expected = array( $font_family_handle, $variation_handle ); - $this->assertSame( $expected, array_keys( $wp_fonts->registered ), 'Font family should not be re-registered after registering a variation' ); - } - - /** - * @dataProvider data_valid_variation - * - * @param string|bool $expected Expected results. - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - * @param string $variation_handle Optional. The variation's handle. - */ - public function test_should_not_reregister_variation( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { - $wp_fonts = new WP_Fonts(); - $wp_fonts->add( $font_family_handle, false ); - - // Set up the test. - $variation_handle = $wp_fonts->add_variation( $font_family_handle, $variation, $variation_handle ); - - // Run the test. - $variant_handle_on_reregister = $wp_fonts->add_variation( $font_family_handle, $variation, $variation_handle ); - $this->assertSame( $expected, $variant_handle_on_reregister, 'Variation should be registered to font family' ); - $this->assertSame( $variation_handle, $variant_handle_on_reregister, 'Variation should return the previously registered variant handle' ); - $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_fonts ), 'Variation should only be registered once' ); - - $this->assertCount( 2, $wp_fonts->registered ); - $this->assertArrayHasKey( $variation_handle, $wp_fonts->registered, 'Variation handle should be in the registry after registration' ); - } - - /** - * @dataProvider data_valid_variation - * - * @param string|bool $expected Expected results. - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - * @param string $variation_handle Optional. The variation's handle. - */ - public function test_should_register_font_family_and_variation( $expected, $font_family_handle, array $variation, $variation_handle = '' ) { - $wp_fonts = new WP_Fonts(); - - $variation_handle = $wp_fonts->add_variation( $font_family_handle, $variation, $variation_handle ); - $this->assertSame( $expected, $variation_handle, 'Variation should return its registered handle' ); - - // Extra checks to ensure both are registered. - $this->assertCount( 2, $wp_fonts->registered ); - $this->assertArrayHasKey( $font_family_handle, $wp_fonts->registered, 'Font family handle should be in the registry after registration' ); - $this->assertArrayHasKey( $variation_handle, $wp_fonts->registered, 'Variation handle should be in the registry after registration' ); - $this->assertSame( array( $variation_handle ), $this->get_variations( $font_family_handle, $wp_fonts ), 'Variation should be registered to the font family' ); - } - - /** - * @dataProvider data_font_family_handle_undefined - * - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - */ - public function test_should_not_register_font_family_or_variant( $font_family_handle, array $variation ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Font family handle must be a non-empty string.' ); - - $wp_fonts = new WP_Fonts(); - $wp_fonts->add_variation( $font_family_handle, $variation ); - - $this->assertEmpty( $wp_fonts->registered, 'Registered queue should be empty' ); - $this->assertEmpty( $this->get_variations( $font_family_handle, $wp_fonts ), 'Variation should not be registered to the font family' ); - } - - /** - * @dataProvider data_font_family_undefined_in_variation - * @dataProviders data_unable_determine_variation_handle - * - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - * @param string $expected_message Expected notice message. - */ - public function test_should_not_register_variation_when_font_family_not_defined( $font_family_handle, array $variation, $expected_message ) { - $this->expectNotice(); - $this->expectNoticeMessage( $expected_message ); - - $wp_fonts = new WP_Fonts(); - $this->assertNull( $wp_fonts->add_variation( $font_family_handle, $variation ) ); - } - - /** - * @dataProvider data_unable_determine_variation_handle - * - * @param string $font_family_handle The font family's handle for this variation. - * @param array $variation An array of variation properties to add. - */ - public function test_should_register_font_family_when_variant_fails_to_register( $font_family_handle, array $variation ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' ); - - $wp_fonts = new WP_Fonts(); - $wp_fonts->add_variation( $font_family_handle, $variation ); - - $this->assertCount( 1, $wp_fonts->registered ); - $this->assertArrayHasKey( $font_family_handle, $wp_fonts->registered ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/dequeue.php b/phpunit/tests/fonts-api/wpFonts/dequeue.php deleted file mode 100644 index 6cd11dc55b937..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/dequeue.php +++ /dev/null @@ -1,72 +0,0 @@ -dequeue( $handles ); - $this->assertEmpty( $this->get_queued_before_register( $wp_fonts ), 'Prequeue should be empty' ); - $this->assertEmpty( $wp_fonts->queue, 'Queue should be empty' ); - } - - /** - * Integration test for dequeuing from queue. It first registers and then enqueues before dequeuing. - * - * @dataProvider data_enqueue - * @dataProvider data_enqueue_variations - * - * @param string|string[] $handles Handles to test. - */ - public function test_should_dequeue_from_queue( $handles ) { - $wp_fonts = new WP_Fonts(); - - // Register and enqueue. - foreach ( $this->get_data_registry() as $handle => $variations ) { - $this->setup_register( $handle, $variations, $wp_fonts ); - } - $wp_fonts->enqueue( $handles ); - - // To make sure the handles are in the queue before dequeuing. - $this->assertNotEmpty( $wp_fonts->queue, 'Queue not be empty before dequeueing' ); - - // Run the test. - $wp_fonts->dequeue( $handles ); - $this->assertEmpty( $wp_fonts->queue, 'Queue should be empty after dequeueing' ); - } - - /** - * Integration test for dequeuing from prequeue. It enqueues first. - * - * @dataProvider data_enqueue - * @dataProvider data_enqueue_variations - * - * @param string|string[] $handles Handles to test. - */ - public function test_should_dequeue_from_prequeue( $handles ) { - $wp_fonts = new WP_Fonts(); - $wp_fonts->enqueue( $handles ); - $this->assertNotEmpty( $this->get_queued_before_register( $wp_fonts ), 'Prequeue not be empty before dequeueing' ); - - $wp_fonts->dequeue( $handles ); - $this->assertEmpty( $this->get_queued_before_register( $wp_fonts ), 'Prequeue should be empty after dequeueing' ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/doItem.php b/phpunit/tests/fonts-api/wpFonts/doItem.php deleted file mode 100644 index a92bd244d1f2a..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/doItem.php +++ /dev/null @@ -1,336 +0,0 @@ -wp_fonts = new WP_Fonts(); - } - - public function test_should_return_false_when_provider_not_registered() { - $this->assertFalse( $this->wp_fonts->do_item( 'provider_not_registered' ) ); - } - - /** - * @dataProvider data_provider_definitions - * - * @param array $provider Provider to mock. - */ - public function test_should_return_false_when_no_fonts_enqueued_for_provider( array $provider ) { - $this->setup_provider_property_mock( $this->wp_fonts, $provider ); - $this->assertFalse( $this->wp_fonts->do_item( $provider['id'] ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_provider_definitions() { - $providers = $this->get_provider_definitions(); - - return array( - 'mock' => array( $providers['mock'] ), - 'local' => array( $providers['local'] ), - ); - } - - /** - * Test the test set up to ensure the `Tests_Fonts_WpFonts_DoItem_::setup_provider_property_mock()` - * method works as expected. - */ - public function test_mocking_providers_property() { - $font_handles = array( 'font1', 'font2', 'font3' ); - $expected = array( - 'mock' => array( - 'class' => Mock_Provider::class, - 'fonts' => $font_handles, - ), - ); - - $this->setup_provider_property_mock( $this->wp_fonts, $this->get_provider_definitions( 'mock' ), $font_handles ); - $actual = $this->property['WP_Fonts::$providers']->getValue( $this->wp_fonts ); - $this->assertSame( $expected, $actual ); - } - - /** - * Test the private method WP_Fonts::get_enqueued_fonts_for_provider(). - * - * Why? This test validates the right fonts are returned for use within - * WP_Fonts::do_item(). - * - * @dataProvider data_get_enqueued_fonts_for_provider - * - * @param array $font_handles Array of handles for the provider. - * @param array $to_do Handles to set for the WP_Fonts::$to_do property. - * @param array $expected Expected result. - */ - public function test_get_enqueued_fonts_for_provider( $font_handles, $to_do, $expected ) { - // Set up the `to_do` property. - $this->wp_fonts->to_do = $to_do; - - // Open the method's visibility for testing. - $get_enqueued_fonts_for_provider = $this->get_reflection_method( 'get_enqueued_fonts_for_provider' ); - - // Mock the WP_Fonts::$property to set up the test. - $this->setup_provider_property_mock( $this->wp_fonts, $this->get_provider_definitions( 'mock' ), $font_handles ); - - $actual = $get_enqueued_fonts_for_provider->invoke( $this->wp_fonts, 'mock' ); - $this->assertSameSets( $expected, $actual ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_get_enqueued_fonts_for_provider() { - return array( - 'to_do queue is empty' => array( - 'font_handles ' => array( 'font1', 'font2', 'font3' ), - 'to_do' => array(), - 'expected' => array(), - ), - 'fonts not in to_do queue' => array( - 'font_handles ' => array( 'font1', 'font2', 'font3' ), - 'to_do' => array( 'font12', 'font13' ), - 'expected' => array(), - ), - '2 of the provider fonts in to_do queue' => array( - 'font_handles ' => array( 'font11', 'font12', 'font13' ), - 'to_do' => array( 'font11', 'font13' ), - 'expected' => array( 'font11', 'font13' ), - ), - 'do all of the provider fonts' => array( - 'font_handles ' => array( 'font21', 'font22', 'font23' ), - 'to_do' => array( 'font21', 'font22', 'font23' ), - 'expected' => array( 'font21', 'font22', 'font23' ), - ), - ); - } - - /** - * Test the private method WP_Fonts::get_font_properties_for_provider(). - * - * Why? This test validates the right font properties are returned for use within - * WP_Fonts::do_item(). - * - * @dataProvider data_get_font_properties_for_provider - * - * @param array $font_handles Web fonts for testing. - * @param array $expected Expected result. - */ - public function test_get_font_properties_for_provider( $font_handles, $expected ) { - // Set up the fonts for WP_Dependencies:get_data(). - $fonts = $this->get_registered_fonts(); - // Set all variations to 'mock' provider. - - // Mock the WP_Fonts::$property to set up the test. - $this->setup_provider_property_mock( $this->wp_fonts, $this->get_provider_definitions( 'mock' ), $font_handles ); - $this->setup_registration_mocks( $fonts, $this->wp_fonts ); - - // Open the method's visibility for testing. - $method = $this->get_reflection_method( 'get_font_properties_for_provider' ); - - $actual = $method->invoke( $this->wp_fonts, $font_handles ); - $this->assertSame( $expected, $actual ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_get_font_properties_for_provider() { - $fonts = $this->get_registered_fonts(); - - return array( - 'handles not registered' => array( - 'font_handles' => array( 'font-not-registered1', 'font-not-registered2', 'font-not-registered3' ), - 'expected' => array(), - ), - 'registered and non-registered handles' => array( - 'font_handles' => array( 'Source Serif Pro-300-normal', 'not-registered-handle', 'Source Serif Pro-900-italic' ), - 'expected' => array( - 'Source Serif Pro-300-normal' => $fonts['Source Serif Pro']['Source Serif Pro-300-normal'], - 'Source Serif Pro-900-italic' => $fonts['Source Serif Pro']['Source Serif Pro-900-italic'], - ), - ), - 'font-family handles, ie no "font-properties" extra data' => array( - 'font_handles' => array( 'font1', 'font2', 'merriweather' ), - 'expected' => array(), - ), - ); - } - - /** - * @dataProvider data_print_enqueued_fonts - * - * @param array $provider Define provider. - * @param array $fonts Fonts to register and enqueue. - * @param array $expected Expected results. - */ - public function test_should_trigger_provider_when_mocked( array $provider, array $fonts, array $expected ) { - $this->setup_print_deps( $provider, $fonts ); - - $provider_mock = $this->setup_object_mock( array( 'set_fonts', 'print_styles' ), $provider['class'] ); - - // Test the provider's methods are invoked. - $provider_mock->expects( $this->once() )->method( 'set_fonts' )->with( $this->identicalTo( $expected['set_fonts'] ) ); - $provider_mock->expects( $this->once() )->method( 'print_styles' ); - - // Set up the WP_Fonts::$provider_instances property. - $provider_instances = $this->get_reflection_property( 'provider_instances' ); - $provider_instances->setValue( $this->wp_fonts, array( $provider['id'] => $provider_mock ) ); - - // Test the method successfully processes the provider. - $this->expectOutputString( '' ); - $this->assertTrue( $this->wp_fonts->do_item( $provider['id'] ), 'WP_Fonts::do_item() should return true' ); - } - - /** - * Integration test. - * - * @dataProvider data_print_enqueued_fonts - * - * @param array $provider Define provider. - * @param array $fonts Fonts to register and enqueue. - * @param array $expected Expected results. - */ - public function test_should_print( array $provider, array $fonts, array $expected ) { - $this->setup_print_deps( $provider, $fonts ); - - // Test the method successfully processes the provider. - $this->expectOutputString( $expected['printed_output'] ); - $this->assertTrue( $this->wp_fonts->do_item( $provider['id'] ), 'WP_Fonts::do_item() should return true' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_print_enqueued_fonts() { - $mock = $this->get_registered_mock_fonts(); - $local = $this->get_registered_local_fonts(); - $font_faces = $this->get_registered_fonts_css(); - - return array( - 'mock' => array( - 'provider' => $this->get_provider_definitions( 'mock' ), - 'fonts' => $mock, - 'expected' => array( - 'set_fonts' => array_merge( $mock['font1'], $mock['font2'], $mock['font3'] ), - 'printed_output' => sprintf( - '%s; %s; %s; %s; %s; %s\n', - $font_faces['font1-300-normal'], - $font_faces['font1-300-italic'], - $font_faces['font1-900-normal'], - $font_faces['font2-200-900-normal'], - $font_faces['font2-200-900-italic'], - $font_faces['font3-bold-normal'] - ), - ), - ), - 'local' => array( - 'provider' => $this->get_provider_definitions( 'local' ), - 'fonts' => $local, - 'expected' => array( - 'set_fonts' => array_merge( $local['merriweather'], $local['Source Serif Pro'] ), - 'printed_output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - ), - ); - } - - /** - * Integration test. - * - * @dataProvider data_not_print_enqueued_fonts - * - * @param array $provider Define provider. - * @param array $fonts Fonts to register and enqueue. - * @param array $expected Not used. - * @param array $to_do_queue Value to set in the WP_Fonts::$to_do queue. - */ - public function test_should_not_print_when_to_do_queue_empty( array $provider, array $fonts, $expected, $to_do_queue ) { - $this->setup_print_deps( $provider, $fonts, $to_do_queue ); - - // Test the method successfully processes the provider. - $this->expectOutputString( '' ); - $this->assertFalse( $this->wp_fonts->do_item( $provider['id'] ), 'WP_Fonts::do_item() should return false' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_not_print_enqueued_fonts() { - $mock = $this->get_registered_mock_fonts(); - $local = $this->get_registered_local_fonts(); - - return array( - 'mock provider when to_do queue is empty' => array( - 'provider' => $this->get_provider_definitions( 'mock' ), - 'fonts' => $mock, - 'expected' => array(), - 'to_do_queue' => array(), - ), - 'local provider when to_do queue is empty' => array( - 'provider' => $this->get_provider_definitions( 'local' ), - 'fonts' => $local, - 'expected' => array(), - 'to_do_queue' => array(), - ), - 'fonts not in to_do queue' => array( - 'provider' => $this->get_provider_definitions( 'mock' ), - 'fonts' => $mock, - 'expected' => array(), - 'to_do_queue' => array(), - ), - ); - } - - /** - * Sets up the print dependencies. - * - * @param array $provider Provider id and class. - * @param array $fonts Fonts to register and enqueue. - * @param array|null $to_do_queue Set the WP_Fonts:$to_do queue. - */ - private function setup_print_deps( $provider, $fonts, $to_do_queue = null ) { - // Set up the fonts for WP_Dependencies:get_data(). - $mocks = $this->setup_registration_mocks( $fonts, $this->wp_fonts ); - $handles = array_keys( $mocks ); - $this->setup_provider_property_mock( $this->wp_fonts, $provider, $handles ); - - // Set up the `WP_Fonts::$to_do` and `WP_Fonts::$to_do_keyed_handles` properties. - if ( null === $to_do_queue ) { - $to_do_queue = $handles; - } - - $this->wp_fonts->to_do = $to_do_queue; - $to_do_keyed_handles = $this->get_reflection_property( 'to_do_keyed_handles' ); - $to_do_keyed_handles->setValue( $this->wp_fonts, array_flip( $to_do_queue ) ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/doItems.php b/phpunit/tests/fonts-api/wpFonts/doItems.php deleted file mode 100644 index 8a29e7463406a..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/doItems.php +++ /dev/null @@ -1,196 +0,0 @@ -wp_fonts = new WP_Fonts(); - } - - public function test_should_not_process_when_no_providers_registered() { - $this->setup_deps( array( 'enqueued' => 'font1' ) ); - - $done = $this->wp_fonts->do_items(); - - $this->assertSame( array(), $done, 'WP_Fonts::do_items() should return an empty array' ); - $this->assertSame( array(), $this->wp_fonts->to_do, 'WP_Fonts::$to_do should be an empty array' ); - } - - /** - * @dataProvider data_invalid_handles - * - * @param mixed $handles Handles to test. - */ - public function test_should_throw_notice_when_invalid_handles( $handles ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Handles must be a non-empty string or array of non-empty strings' ); - - $this->wp_fonts->do_items( $handles ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_invalid_handles() { - return array( - 'null' => array( null ), - 'empty array' => array( array() ), - 'empty string' => array( '' ), - 'array of empty strings' => array( array( '', '' ) ), - 'array of mixed falsey values' => array( array( '', false, null, array() ) ), - ); - } - - public function test_should_throw_notice_when_provider_class_not_found() { - $this->expectNotice(); - $this->expectNoticeMessage( 'Class "Provider_Does_Not_Exist" not found for "doesnotexist" font provider' ); - - $setup = array( - 'provider' => array( - 'doesnotexist' => array( - 'id' => 'doesnotexist', - 'class' => 'Provider_Does_Not_Exist', - ), - ), - 'provider_handles' => array( 'doesnotexist' => array( 'font1' ) ), - 'registered' => array( - 'doesnotexist' => array( - 'font1' => array( - 'font1-300-normal' => array( - 'provider' => 'doesnotexist', - 'font-weight' => '300', - 'font-style' => 'normal', - 'font-display' => 'fallback', - ), - ), - ), - ), - 'enqueued' => array( 'font1', 'font1-300-normal' ), - ); - $this->setup_deps( $setup ); - - $this->wp_fonts->do_items(); - } - - /** - * @dataProvider data_print_enqueued - * - * @param array $setup Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_mocked_enqueued( $setup, $expected_done, $expected_output ) { - $this->setup_deps( $setup ); - - $this->expectOutputString( $expected_output ); - $actual_done = $this->wp_fonts->do_items(); - $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' ); - } - - /** - * Integration test that registers providers and fonts and then enqueues before - * testing the printing functionality. - * - * @dataProvider data_print_enqueued - * - * @param array $setup Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_enqueued( $setup, $expected_done, $expected_output ) { - $this->setup_integrated_deps( $setup ); - - $this->expectOutputString( $expected_output, 'Printed @font-face styles should match' ); - $actual_done = $this->wp_fonts->do_items(); - $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' ); - } - - /** - * Integration test to validate printing given handles. Rather than mocking internal functionality, - * it registers providers and fonts but does not enqueue. - * - * @dataProvider data_print_enqueued - * - * @param array $setup Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_handles_when_not_enqueued( $setup, $expected_done, $expected_output ) { - $this->setup_integrated_deps( $setup, false ); - // Do not enqueue. Instead, pass the handles to WP_Fonts::do_items(). - $handles = $setup['enqueued']; - $this->assertEmpty( $this->wp_fonts->queue, 'No fonts should be enqueued' ); - - $this->expectOutputString( $expected_output ); - $actual_done = $this->wp_fonts->do_items( $handles ); - $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' ); - } - - /** - * Sets up the dependencies for the mocked test. - * - * @param array $setup Dependencies to set up. - */ - private function setup_deps( array $setup ) { - $setup = array_merge( - array( - 'provider' => array(), - 'provider_handles' => array(), - 'registered' => array(), - 'enqueued' => array(), - ), - $setup - ); - - if ( ! empty( $setup['provider'] ) ) { - foreach ( $setup['provider'] as $provider_id => $provider ) { - $this->setup_provider_property_mock( $this->wp_fonts, $provider, $setup['provider_handles'][ $provider_id ] ); - } - } - - if ( ! empty( $setup['registered'] ) ) { - $this->setup_registration_mocks( $setup['registered'], $this->wp_fonts ); - } - - if ( ! empty( $setup['enqueued'] ) ) { - $queue = $this->get_reflection_property( 'queue' ); - $queue->setValue( $this->wp_fonts, $setup['enqueued'] ); - } - } - - /** - * Sets up the dependencies for integration test. - * - * @param array $setup Dependencies to set up. - * @param bool $enqueue Whether to enqueue. Default true. - */ - private function setup_integrated_deps( array $setup, $enqueue = true ) { - foreach ( $setup['provider'] as $provider ) { - $this->wp_fonts->register_provider( $provider['id'], $provider['class'] ); - } - foreach ( $setup['registered'] as $handle => $variations ) { - $this->setup_register( $handle, $variations, $this->wp_fonts ); - } - - if ( $enqueue ) { - $this->wp_fonts->enqueue( $setup['enqueued'] ); - } - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/enqueue.php b/phpunit/tests/fonts-api/wpFonts/enqueue.php deleted file mode 100644 index 09c14676c7ae0..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/enqueue.php +++ /dev/null @@ -1,53 +0,0 @@ -enqueue( $handles ); - - $this->assertSame( $expected, $this->get_queued_before_register( $wp_fonts ), 'Handles should be added to before registered queue' ); - $this->assertEmpty( $wp_fonts->queue, 'Handles should not be added to the enqueue queue when not registered' ); - } - - /** - * Integration test for enqueuing (a) a font family and all of its variations or (b) specific variations. - * - * @dataProvider data_enqueue - * @dataProviders data_enqueue_variations - * - * @param string|string[] $handles Handles to test. - * @param array $expected Expected queue. - */ - public function test_should_enqueue_when_registered( $handles, array $expected ) { - $wp_fonts = new WP_Fonts(); - foreach ( $this->get_data_registry() as $font_family => $variations ) { - $this->setup_register( $font_family, $variations, $wp_fonts ); - } - - $wp_fonts->enqueue( $handles ); - - $this->assertEmpty( $this->get_queued_before_register( $wp_fonts ), '"queued_before_register" queue should be empty' ); - $this->assertSame( $expected, $wp_fonts->queue, 'Queue should contain the given handles' ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/getEnqueued.php b/phpunit/tests/fonts-api/wpFonts/getEnqueued.php deleted file mode 100644 index bacb8a31aa650..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/getEnqueued.php +++ /dev/null @@ -1,57 +0,0 @@ -assertEmpty( $wp_fonts->get_enqueued() ); - } - - /** - * Unit test for when font families are enqueued. - * - * @dataProvider data_enqueue - * - * @param string|string[] $not_used Not used. - * @param array $expected Expected queue. - */ - public function test_should_return_queue_when_property_has_font_families( $not_used, array $expected ) { - $wp_fonts = new WP_Fonts(); - $wp_fonts->queue = $expected; - - $this->assertSame( $expected, $wp_fonts->get_enqueued() ); - } - - /** - * Full integration test that registers and enqueues the queue - * is properly wired for "get_enqueued()". - * - * @dataProvider data_enqueue - * - * @param string|string[] $font_family Font family to test. - * @param array $expected Expected queue. - */ - public function test_should_return_queue_when_font_families_registered_and_enqueued( $font_family, array $expected ) { - $wp_fonts = new WP_Fonts(); - - // Register and enqueue. - foreach ( $this->get_data_registry() as $handle => $variations ) { - $this->setup_register( $handle, $variations, $wp_fonts ); - } - $wp_fonts->enqueue( $font_family ); - - $this->assertSame( $expected, $wp_fonts->get_enqueued() ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/getProviders.php b/phpunit/tests/fonts-api/wpFonts/getProviders.php deleted file mode 100644 index 410adaace286a..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/getProviders.php +++ /dev/null @@ -1,62 +0,0 @@ -wp_fonts = new WP_Fonts(); - - $this->providers_property = new ReflectionProperty( WP_Fonts::class, 'providers' ); - $this->providers_property->setAccessible( true ); - } - - public function test_should_be_empty() { - $actual = $this->wp_fonts->get_providers(); - $this->assertIsArray( $actual, 'Should return an empty array' ); - $this->assertEmpty( $actual, 'Should return an empty array when no providers are registered' ); - } - - /** - * @dataProvider data_get_providers - * - * @param array $providers Array of providers to test. - * @param array $expected Expected results. - */ - public function test_get_providers( array $providers, array $expected ) { - $this->setup_providers( $providers ); - $this->assertSame( $expected, $this->wp_fonts->get_providers() ); - } - - /** - * Sets up the given providers and stores them in the `WP_Fonts::providers` property. - * - * @param array $providers Array of providers to set up. - */ - private function setup_providers( array $providers ) { - $data = array(); - - foreach ( $providers as $provider_id => $class ) { - $data[ $provider_id ] = array( - 'class' => $class, - 'fonts' => array(), - ); - } - - $this->providers_property->setValue( $this->wp_fonts, $data ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/getRegistered.php b/phpunit/tests/fonts-api/wpFonts/getRegistered.php deleted file mode 100644 index 7dc116f587539..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/getRegistered.php +++ /dev/null @@ -1,90 +0,0 @@ -assertEmpty( $wp_fonts->get_registered() ); - } - - /** - * Unit test for when font families are enqueued. - * - * @dataProvider data_get_registered - * - * @param array $inputs Font family(ies) and variations to register. - */ - public function test_should_return_queue_when_mocking_registered_property( array $inputs ) { - $wp_fonts = new WP_Fonts(); - $mocks = $this->setup_registration_mocks( $inputs, $wp_fonts ); - $expected = array_keys( $mocks ); - - $this->assertSame( $expected, $wp_fonts->get_registered() ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_get_registered() { - return array( - 'no variations' => array( - 'inputs' => array( - 'lato' => array(), - ), - ), - 'with 1 variation' => array( - 'inputs' => array( - 'Source Serif Pro' => array( 'variation-1' ), - ), - ), - 'with 2 variations' => array( - 'inputs' => array( - 'my-cool-font' => array( 'cool-1', 'cool-2' ), - ), - ), - 'when multiple font families registered' => array( - 'inputs' => array( - 'font-family-1' => array( 'variation-11', 'variation-12' ), - 'font-family-2' => array( 'variation-21', 'variation-22' ), - 'font-family-3' => array( 'variation-31', 'variation-32' ), - ), - ), - ); - } - - /** - * Full integration test that registers varying number of font families and variations - * to validate if "get_registered()" internals is property wired to the registered queue. - * - * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations - * - * @param string $font_family Not used. - * @param array $inputs Font family(ies) and variations to register. - * @param array $expected Expected results. - */ - public function test_should_return_queue_when_items_are_registered( $font_family, array $inputs, array $expected ) { - $wp_fonts = new WP_Fonts(); - - // Register before testing. - foreach ( $inputs as $handle => $variations ) { - $this->setup_register( $handle, $variations, $wp_fonts ); - } - - $this->assertSame( $expected, $wp_fonts->get_registered() ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/query.php b/phpunit/tests/fonts-api/wpFonts/query.php deleted file mode 100644 index 0e56b515e7ebf..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/query.php +++ /dev/null @@ -1,151 +0,0 @@ -wp_fonts = new WP_Fonts(); - } - - /** - * @dataProvider data_invalid_query - * @dataProvider data_valid_query - * - * @param string $query_handle Handle to test. - */ - public function test_should_fail_when_handles_not_registered( $query_handle ) { - $this->assertFalse( $this->wp_fonts->query( $query_handle, 'registered' ) ); - } - - /** - * @dataProvider data_invalid_query - * @dataProvider data_valid_query - * - * @param string $query_handle Handle to test. - */ - public function test_should_fail_when_handles_not_registered_or_enqueued( $query_handle ) { - $this->assertFalse( $this->wp_fonts->query( $query_handle, 'queue' ) ); - } - - /** - * @dataProvider data_valid_query - * - * @param string $query_handle Handle to test. - */ - public function test_registered_query_should_succeed_when_registered( $query_handle ) { - $this->setup_registry(); - - $actual = $this->wp_fonts->query( $query_handle, 'registered' ); - $this->assertInstanceOf( '_WP_Dependency', $actual, 'Query should return an instance of _WP_Dependency' ); - $this->assertSame( $query_handle, $actual->handle, 'Query object handle should match the given handle to query' ); - } - - /** - * @dataProvider data_valid_query - * - * @param string $query_handle Handle to test. - */ - public function test_enqueued_query_should_succeed_when_registered_and_enqueued( $query_handle ) { - $this->setup_registry(); - $this->wp_fonts->enqueue( $query_handle ); - - $this->assertTrue( $this->wp_fonts->query( $query_handle, 'enqueued' ) ); - } - - /** - * @dataProvider data_valid_query - * - * @param string $query_handle Handle to test. - */ - public function test_enqueued_query_should_fail_when_not_registered_but_enqueued( $query_handle ) { - $this->wp_fonts->enqueue( $query_handle ); - - $this->assertFalse( $this->wp_fonts->query( $query_handle, 'enqueued' ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_invalid_query() { - return array( - 'DM Sans' => array( 'DM Sans' ), - 'roboto' => array( 'roboto' ), - 'my-font' => array( 'my-font' ), - ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_valid_query() { - return array( - 'lato' => array( 'lato' ), - 'merriweather' => array( 'merriweather' ), - 'Source Serif Pro' => array( 'source-serif-pro' ), - ); - } - - public function test_done_query_should_fail_when_no_variations() { - $this->wp_fonts->register_provider( 'local', WP_Fonts_Provider_Local::class ); - $this->setup_registry(); - $this->wp_fonts->enqueue( 'lato' ); - - $this->wp_fonts->do_items( 'lato' ); - - $this->assertFalse( $this->wp_fonts->query( 'lato', 'done' ) ); - } - - /** - * @dataProvider data_done_query - * - * @param string $query_handle Handle to test. - */ - public function test_done_query_should_succeed_when_registered_and_enqueued( $query_handle ) { - $this->wp_fonts->register_provider( 'local', WP_Fonts_Provider_Local::class ); - $this->setup_registry(); - $this->wp_fonts->enqueue( $query_handle ); - - // Process the fonts while ignoring all the printed output. - $this->expectOutputRegex( '`.`' ); - $this->wp_fonts->do_items( $query_handle ); - $this->getActualOutput(); - - $this->assertTrue( $this->wp_fonts->query( $query_handle, 'done' ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_done_query() { - return array( - 'merriweather' => array( 'merriweather' ), - 'Source Serif Pro' => array( 'source-serif-pro' ), - ); - } - - private function setup_registry() { - foreach ( $this->get_registered_local_fonts() as $handle => $variations ) { - $this->setup_register( $handle, $variations, $this->wp_fonts ); - } - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/registerProvider.php b/phpunit/tests/fonts-api/wpFonts/registerProvider.php deleted file mode 100644 index c67976e0a6865..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/registerProvider.php +++ /dev/null @@ -1,116 +0,0 @@ -assertTrue( $wp_fonts->register_provider( $provider_id, $class_name ), 'WP_Fonts::register_provider() should return true' ); - $this->assertSame( $expected, $wp_fonts->get_providers(), 'Provider "' . $provider_id . '" should be registered in providers queue' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_register_providers() { - return array( - 'mock' => array( - 'provider_id' => 'mock', - 'class' => Mock_Provider::class, - 'expected' => array( - 'mock' => array( - 'class' => Mock_Provider::class, - 'fonts' => array(), - ), - ), - ), - 'local' => array( - 'provider_id' => 'local', - 'class' => WP_Fonts_Provider_Local::class, - 'expected' => array( - 'local' => array( - 'class' => WP_Fonts_Provider_Local::class, - 'fonts' => array(), - ), - ), - ), - ); - } - - public function test_should_register_multiple_providers() { - $wp_fonts = new WP_Fonts(); - $providers = $this->get_provider_definitions(); - foreach ( $providers as $provider ) { - $this->assertTrue( $wp_fonts->register_provider( $provider['id'], $provider['class'] ), 'WP_Fonts::register_provider() should return true for provider ' . $provider['id'] ); - } - - $expected = array( - 'mock' => array( - 'class' => $providers['mock']['class'], - 'fonts' => array(), - ), - 'local' => array( - 'class' => $providers['local']['class'], - 'fonts' => array(), - ), - ); - - $this->assertSame( $expected, $wp_fonts->get_providers(), 'Both local and mock providers should be registered' ); - } - - /** - * @dataProvider data_invalid_providers - * - * @param string $provider_id Provider ID. - * @param string $class_name Provider class name. - */ - public function test_should_not_register( $provider_id, $class_name ) { - $wp_fonts = new WP_Fonts(); - - $this->assertFalse( $wp_fonts->register_provider( $provider_id, $class_name ), 'WP_Fonts::register_provider() should return false' ); - $this->assertArrayNotHasKey( $provider_id, $wp_fonts->get_providers(), 'Both local and mock providers should be registered' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_invalid_providers() { - return array( - 'provider_id is empty' => array( - 'provider_id' => '', - 'class' => Mock_Provider::class, - ), - 'class is empty' => array( - 'provider_id' => 'local', - 'class' => '', - ), - 'class does not exist' => array( - 'provider_id' => 'doesnotexist', - 'class' => 'Provider_Does_Not_Exist', - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/remove.php b/phpunit/tests/fonts-api/wpFonts/remove.php deleted file mode 100644 index 208bd58c3d2c3..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/remove.php +++ /dev/null @@ -1,118 +0,0 @@ -remove( array( 'handle-1', 'handle2' ) ); - - $this->assertEmpty( $wp_fonts->registered ); - } - - /** - * @dataProvider data_remove_when_registered - * - * @param array $handles Handles to remove. - * @param array $expected Expected handles are running test. - */ - public function test_should_remove_when_registered( array $handles, array $expected ) { - $wp_fonts = new WP_Fonts(); - $wp_fonts->registered = $this->generate_registered_queue(); - - $wp_fonts->remove( $handles ); - - $this->assertSameSets( $expected, array_keys( $wp_fonts->registered ), 'Registered queue should match after removing handles' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_remove_when_registered() { - $all = array( - 'handle-1', - 'handle-2', - 'handle-3', - 'handle-4', - 'handle-5', - 'handle-6', - 'handle-7', - 'handle-8', - 'handle-9', - 'handle-10', - ); - - return array( - 'remove none' => array( - 'handles' => array(), - 'expected' => $all, - ), - 'remove handle-5' => array( - 'handles' => array( 'handle-5' ), - 'expected' => array( - 'handle-1', - 'handle-2', - 'handle-3', - 'handle-4', - 'handle-6', - 'handle-7', - 'handle-8', - 'handle-9', - 'handle-10', - ), - ), - 'remove 2 from start and end' => array( - 'handles' => array( 'handle-1', 'handle-2', 'handle-9', 'handle-10' ), - 'expected' => array( - 'handle-3', - 'handle-4', - 'handle-5', - 'handle-6', - 'handle-7', - 'handle-8', - ), - ), - 'remove all' => array( - 'handles' => $all, - 'expected' => array(), - ), - 'remove only registered' => array( - 'handles' => array( 'handle-1', 'handle-10', 'handle-abc', 'handle-5' ), - 'expected' => array( - 'handle-2', - 'handle-3', - 'handle-4', - 'handle-6', - 'handle-7', - 'handle-8', - 'handle-9', - ), - ), - ); - } - - private function generate_registered_queue() { - $queue = array(); - for ( $num = 1; $num <= 10; $num++ ) { - $handle = "handle-{$num}"; - $queue[ $handle ] = $num; - } - - return $queue; - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/removeFontFamily.php b/phpunit/tests/fonts-api/wpFonts/removeFontFamily.php deleted file mode 100644 index 6e762307d0520..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/removeFontFamily.php +++ /dev/null @@ -1,89 +0,0 @@ -setup_registration_mocks( $inputs, $wp_fonts ); - // Test the before state, just to make sure. - $this->assertArrayHasKey( $font_family, $wp_fonts->registered, 'Registered queue should contain the font family before remove' ); - $this->assertSame( $registered_handles, array_keys( $wp_fonts->registered ), 'Font family and variations should be registered before remove' ); - - $wp_fonts->remove_font_family( $font_family ); - - $this->assertArrayNotHasKey( $font_family, $wp_fonts->registered, 'Registered queue should not contain the font family' ); - $this->assertSame( $expected, array_keys( $wp_fonts->registered ), 'Registered queue should match after removing font family' ); - } - - /** - * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations - * - * @param string $font_family Font family to test. - * @param array $inputs Font family(ies) and variations to pre-register. - * @param array $registered_handles Not used. - * @param array $expected Array of expected handles. - */ - public function test_should_bail_out_when_not_registered( $font_family, array $inputs, array $registered_handles, array $expected ) { - $wp_fonts = new WP_Fonts(); - unset( $inputs[ $font_family ] ); - $this->setup_registration_mocks( $inputs, $wp_fonts ); - - $wp_fonts->remove_font_family( $font_family ); - - $this->assertArrayNotHasKey( $font_family, $wp_fonts->registered, 'Registered queue should not contain the font family' ); - $this->assertSame( $expected, array_keys( $wp_fonts->registered ), 'Registered queue should match after removing font family' ); - } - - /** - * Integration test for removing a font family and all of its variation when font family is registered. - * - * @dataProvider data_one_to_many_font_families_and_zero_to_many_variations - * - * @param string $font_family Font family to test. - * @param array $inputs Font family(ies) and variations to pre-register. - * @param array $registered_handles Expected handles after registering. - * @param array $expected Array of expected handles. - */ - public function test_should_deregister_when_registered( $font_family, array $inputs, array $registered_handles, array $expected ) { - $wp_fonts = new WP_Fonts(); - // Register all font families and their variations. - foreach ( $inputs as $input_font_family => $variations ) { - $handle = $wp_fonts->add_font_family( $input_font_family ); - foreach ( $variations as $variation_handle => $variation ) { - if ( ! is_string( $variation_handle ) ) { - $variation_handle = ''; - } - $wp_fonts->add_variation( $handle, $variation, $variation_handle ); - } - } - // Test the before state, just to make sure. - $this->assertArrayHasKey( $font_family, $wp_fonts->registered, 'Registered queue should contain the font family before remove' ); - $this->assertSame( $registered_handles, array_keys( $wp_fonts->registered ), 'Font family and variations should be registered before remove' ); - - $wp_fonts->remove_font_family( $font_family ); - - $this->assertArrayNotHasKey( $font_family, $wp_fonts->registered, 'Registered queue should not contain the font family' ); - $this->assertSame( $expected, array_keys( $wp_fonts->registered ), 'Registered queue should match after removing font family' ); - } -} diff --git a/phpunit/tests/fonts-api/wpFonts/removeVariation.php b/phpunit/tests/fonts-api/wpFonts/removeVariation.php deleted file mode 100644 index 508c8ce264d8f..0000000000000 --- a/phpunit/tests/fonts-api/wpFonts/removeVariation.php +++ /dev/null @@ -1,278 +0,0 @@ -wp_fonts = new WP_Fonts(); - $this->fonts_to_register = $this->get_registered_local_fonts(); - } - - /** - * Sets up the unit test by mocking the WP_Dependencies object using stdClass and - * registering each font family directly to the WP_Fonts::$registered property - * and its variations to the mocked $deps property. - */ - private function setup_unit_test() { - $this->setup_registration_mocks( $this->fonts_to_register, $this->wp_fonts ); - } - - /** - * Sets up the integration test by properly registering each font family and its variations - * by using the WP_Fonts::add() and WP_Fonts::add_variation() methods. - */ - private function setup_integration_test() { - foreach ( $this->fonts_to_register as $font_family_handle => $variations ) { - $this->setup_register( $font_family_handle, $variations, $this->wp_fonts ); - } - } - - /** - * Testing the test setup to ensure it works. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - */ - public function test_mocked_setup( $font_family_handle, $variation_handle ) { - $this->setup_unit_test(); - - $this->assertArrayHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be in the registered queue before removal' ); - $this->assertContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should be in its font family deps before removal' ); - } - - /** - * Unit test. - * - * @dataProvider data_should_do_nothing_when_variation_and_font_family_not_registered - * - * @param string $font_family Font family name. - * @param string $font_family_handle Font family handle. - * @param string $variation_handle Variation handle to remove. - */ - public function test_unit_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) { - // Set up the test. - unset( $this->fonts_to_register[ $font_family ] ); - $this->setup_unit_test(); - $registered_queue = $this->wp_fonts->registered; - - // Run the tests. - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $font_family_handle, $this->wp_fonts->registered, 'Font family should not be registered' ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertSame( $registered_queue, $this->wp_fonts->registered, 'Registered queue should not have changed' ); - } - - /** - * Integration test. - * - * @dataProvider data_should_do_nothing_when_variation_and_font_family_not_registered - * - * @param string $font_family Font family name. - * @param string $font_family_handle Font family handle. - * @param string $variation_handle Variation handle to remove. - */ - public function test_should_do_nothing_when_variation_and_font_family_not_registered( $font_family, $font_family_handle, $variation_handle ) { - // Set up the test. - unset( $this->fonts_to_register[ $font_family ] ); - $this->setup_integration_test(); - $registered_queue = $this->wp_fonts->get_registered(); - - // Run the tests. - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $font_family_handle, $this->wp_fonts->registered, 'Font family should not be registered' ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertSameSets( $registered_queue, $this->wp_fonts->get_registered(), 'Registered queue should not have changed' ); - } - - /** - * Data provider for testing removal of variations. - * - * @return array - */ - public function data_should_do_nothing_when_variation_and_font_family_not_registered() { - return array( - 'Font with 1 variation' => array( - 'font_family' => 'merriweather', - 'font_family_handle' => 'merriweather', - 'variation_handle' => 'merriweather-200-900-normal', - ), - 'Font with multiple variations' => array( - 'font_family' => 'Source Serif Pro', - 'font_family_handle' => 'source-serif-pro', - 'variation_handle' => 'Source Serif Pro-300-normal', - ), - ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - $this->setup_remove_variation_from_registered( $variation_handle ); - - // Run the tests. - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_only_remove_from_font_family_deps_when_variation_not_in_queue( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - $this->setup_remove_variation_from_registered( $variation_handle ); - - // Run the tests. - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variant should not be registered' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' ); - - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_remove_variation_from_registered_queue_though_font_family_not_registered( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - $this->setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should not be in its font family deps before removal' ); - - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Unit test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_unit_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_unit_test(); - - $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' ); - - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be not be in registered queue' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Integration test. - * - * @dataProvider data_remove_variations - * - * @param string $font_family_handle Font family for the variation. - * @param string $variation_handle Variation handle to remove. - * @param array $expected Expected results. - */ - public function test_should_remove_variation_from_queue_and_font_family_deps( $font_family_handle, $variation_handle, $expected ) { - // Set up the test. - $this->setup_integration_test(); - - $this->assertArrayHasKey( $variation_handle, array_flip( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Variation should be in its font family deps before removal' ); - - $this->wp_fonts->remove_variation( $font_family_handle, $variation_handle ); - - $this->assertArrayNotHasKey( $variation_handle, $this->wp_fonts->registered, 'Variation should be not be in registered queue' ); - $this->assertNotContains( $variation_handle, $this->wp_fonts->registered[ $font_family_handle ]->deps, 'Variation should not be its font family deps' ); - $this->assertSameSets( $expected['font_family_deps'], array_values( $this->wp_fonts->registered[ $font_family_handle ]->deps ), 'Only the tested variation handle should be removed from font family deps' ); - } - - /** - * Remove the variation handle from the font family's deps. - * - * @param string $font_family_handle Font family. - * @param string $variation_handle The variation handle to remove. - */ - private function setup_remove_from_font_family_deps( $font_family_handle, $variation_handle ) { - foreach ( $this->wp_fonts->registered[ $font_family_handle ]->deps as $index => $vhandle ) { - if ( $variation_handle !== $vhandle ) { - continue; - } - unset( $this->wp_fonts->registered[ $font_family_handle ]->deps[ $index ] ); - break; - } - } - - /** - * Removes the variation from the WP_Fonts::$registered queue. - * - * @param string $variation_handle The variation handle to remove. - */ - private function setup_remove_variation_from_registered( $variation_handle ) { - unset( $this->wp_fonts->registered[ $variation_handle ] ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsProviderLocal.php b/phpunit/tests/fonts-api/wpFontsProviderLocal.php deleted file mode 100644 index f9319390c3b6b..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsProviderLocal.php +++ /dev/null @@ -1,180 +0,0 @@ -provider = new WP_Fonts_Provider_Local(); - - $this->set_up_theme(); - } - - public function tear_down() { - // Restore the original theme directory setup. - $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir; - wp_clean_themes_cache(); - unset( $GLOBALS['wp_themes'] ); - - parent::tear_down(); - } - - /** - * @covers WP_Fonts_Provider_Local::set_fonts - */ - public function test_set_fonts() { - $fonts = array( - 'source-serif-pro-200-900-normal-local' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'source-serif-pro-200-900-italic-local' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - ), - ); - - $this->provider->set_fonts( $fonts ); - - $property = $this->get_fonts_property(); - $this->assertSame( $fonts, $property->getValue( $this->provider ) ); - } - - /** - * @covers WP_Fonts_Provider_Local::get_css - * - * @dataProvider data_get_css_print_styles - * - * @param array $fonts Prepared fonts (to store in WP_Fonts_Provider_Local::$fonts property). - * @param string $expected Expected CSS. - */ - public function test_get_css( array $fonts, $expected ) { - $property = $this->get_fonts_property(); - $property->setValue( $this->provider, $fonts ); - - $this->assertSame( $expected['font-face-css'], $this->provider->get_css() ); - } - - /** - * @covers WP_Fonts_Provider_Local::print_styles - * - * @dataProvider data_get_css_print_styles - * - * @param array $fonts Prepared fonts (to store in WP_Fonts_Provider_Local::$fonts property). - * @param string $expected Expected CSS. - */ - public function test_print_styles( array $fonts, $expected ) { - $property = $this->get_fonts_property(); - $property->setValue( $this->provider, $fonts ); - - $expected_output = sprintf( $expected['style-element'], $expected['font-face-css'] ); - $this->expectOutputString( $expected_output ); - $this->provider->print_styles(); - } - - /** - * Data provider. - * - * @return array - */ - public function data_get_css_print_styles() { - return array( - 'truetype format' => array( - 'fonts' => array( - 'open-sans-bold-italic-local' => array( - 'provider' => 'local', - 'font-family' => 'Open Sans', - 'font-style' => 'italic', - 'font-weight' => 'bold', - 'src' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', - ), - ), - 'expected' => array( - 'style-element' => "\n", - 'font-face-css' => << array( - 'fonts' => array( - 'source-serif-pro-200-900-normal-local' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'source-serif-pro-400-900-italic-local' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - ), - ), - 'expected' => array( - 'style-element' => "\n", - 'font-face-css' => <<theme_root = realpath( GUTENBERG_DIR_TESTDATA . '/themedir1' ); - $this->orig_theme_dir = $GLOBALS['wp_theme_directories']; - $GLOBALS['wp_theme_directories'] = array( $this->theme_root ); - - $theme_root_callback = function () { - return $this->theme_root; - }; - add_filter( 'theme_root', $theme_root_callback ); - add_filter( 'stylesheet_root', $theme_root_callback ); - add_filter( 'template_root', $theme_root_callback ); - - // Clear caches. - wp_clean_themes_cache(); - unset( $GLOBALS['wp_themes'] ); - } - - private function get_fonts_property() { - $property = new ReflectionProperty( $this->provider, 'fonts' ); - $property->setAccessible( true ); - - return $property; - } -} diff --git a/phpunit/tests/fonts-api/wpFontsResolver/addMissingFontsToThemeJson.php b/phpunit/tests/fonts-api/wpFontsResolver/addMissingFontsToThemeJson.php deleted file mode 100644 index d6e45561e5e33..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsResolver/addMissingFontsToThemeJson.php +++ /dev/null @@ -1,252 +0,0 @@ -assertInstanceOf( WP_Theme_JSON_Gutenberg::class, $actual, 'Instance of WP_Theme_JSON_Gutenberg should be returned' ); - } - - /** - * @dataProvider data_themes - * - * @param string $theme Theme to use. - */ - public function test_should_bail_out_when_no_registered_fonts( $theme ) { - switch_theme( $theme ); - - $data = new WP_Theme_JSON_Gutenberg( self::$theme_json_data[ $theme ] ); - $actual = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( $data ); - - $this->assertEmpty( wp_fonts()->get_registered_font_families(), 'No fonts should be registered in Fonts API' ); - $this->assertSame( $data, $actual, 'Same instance of WP_Theme_JSON_Gutenberg should be returned' ); - } - - /** - * Data Provider. - * - * @return array - */ - public function data_themes() { - return array( - 'no fonts defined' => array( 'block-theme' ), - 'no fonts registered' => array( static::FONTS_THEME ), - ); - } - - /** - * @dataProvider data_should_add_non_theme_json_fonts - * - * @param string $theme Theme to use. - * @param array $fonts Fonts to register. - * @param array $expected Expected fonts to be added. - */ - public function test_should_add_non_theme_json_fonts( $theme, $fonts, $expected ) { - switch_theme( static::FONTS_THEME ); - - // Register the fonts. - wp_register_fonts( $fonts ); - - $data = new WP_Theme_JSON_Gutenberg( self::$theme_json_data[ $theme ] ); - $actual = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( $data ); - - $this->assertNotSame( $data, $actual, 'New instance of WP_Theme_JSON_Gutenberg should be returned' ); - $actual_raw_data = $actual->get_raw_data(); - - $this->assertArrayHasKey( 'typography', $actual_raw_data['settings'] ); - $this->assertArrayHasKey( 'fontFamilies', $actual_raw_data['settings']['typography'] ); - $this->assertArrayHasKey( 'theme', $actual_raw_data['settings']['typography']['fontFamilies'] ); - - $this->assertContains( - $expected, - $actual_raw_data['settings']['typography']['fontFamilies']['theme'], - 'Fonts should be added after running WP_Fonts_Resolver::add_missing_fonts_to_theme_json()' - ); - } - - /** - * Data Provider. - * - * @return array - */ - public function data_should_add_non_theme_json_fonts() { - $lato = array( - 'Lato' => array( - array( - 'font-family' => 'Lato', - 'font-style' => 'normal', - 'font-weight' => '400', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular.woff2', - ), - array( - 'font-family' => 'Lato', - 'font-style' => 'italic', - 'font-weight' => '400', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular-Italic.woff2', - ), - ), - ); - - $expected_lato = array( - 'fontFamily' => 'Lato', - 'name' => 'Lato', - 'slug' => 'lato', - 'fontFace' => array( - 'lato-400-normal' => array( - 'origin' => 'gutenberg_wp_fonts_api', - 'provider' => 'local', - 'fontFamily' => 'Lato', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'fontDisplay' => 'fallback', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular.woff2', - ), - 'lato-400-italic' => array( - 'origin' => 'gutenberg_wp_fonts_api', - 'provider' => 'local', - 'fontFamily' => 'Lato', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'fontDisplay' => 'fallback', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular-Italic.woff2', - ), - ), - ); - - return array( - 'theme with no fonts defined' => array( - 'theme' => 'block-theme', - 'fonts' => $lato, - 'expected' => $expected_lato, - ), - 'theme with fonts: new fonts not in theme' => array( - 'theme' => static::FONTS_THEME, - 'fonts' => $lato, - 'expected' => $expected_lato, - ), - - /* - * @TODO Add these tests fixing https://github.com/WordPress/gutenberg/issues/50047. - * - 'theme with fonts: new variations registered' => array( - 'theme' => static::FONTS_THEME, - 'fonts' => array( - 'DM Sans' => array( - 'dm-sans-500-normal' => array( - 'font-family' => 'DM Sans', - 'font-style' => 'normal', - 'font-weight' => '500', - 'src' => 'https://example.com/tests/assets/fonts/dm-sans/DMSans-Medium.woff2', - ), - 'dm-sans-500-italic' => array( - 'font-family' => 'DM Sans', - 'font-style' => 'italic', - 'font-weight' => '500', - 'src' => 'https://example.com/tests/assets/fonts/dm-sans/DMSans-Medium.woff2', - ), - ), - ), - 'expected' => array( - 'fontFace' => array( - array( - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => array( 'file:./assets/fonts/dm-sans/DMSans-Regular.woff2' ), - ), - array( - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => array( 'file:./assets/fonts/dm-sans/DMSans-Regular-Italic.woff2' ), - ), - 'dm-sans-500-normal' => array( - 'origin' => 'gutenberg_wp_fonts_api', - 'provider' => 'local', - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'normal', - 'fontWeight' => '500', - 'fontDisplay' => 'fallback', - 'src' => array( get_stylesheet_directory_uri() . 'assets/fonts/dm-sans/DMSans-Medium.woff2' ), - ), - 'dm-sans-500-italic' => array( - 'origin' => 'gutenberg_wp_fonts_api', - 'provider' => 'local', - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'fontDisplay' => 'fallback', - 'src' => array( get_stylesheet_directory_uri() . 'assets/fonts/dm-sans/DMSans-Medium-Italic.woff2' ), - ), - array( - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'normal', - 'fontWeight' => '700', - 'src' => array( 'file:./assets/fonts/dm-sans/DMSans-Bold.woff2' ), - ), - array( - 'fontFamily' => 'DM Sans', - 'fontStretch' => 'normal', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - 'src' => array( 'file:./assets/fonts/dm-sans/DMSans-Bold-Italic.woff2' ), - ), - ), - 'fontFamily' => '"DM Sans", sans-serif', - 'name' => 'DM Sans', - 'slug' => 'dm-sans', - ), - ), - */ - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsResolver/enqueueUserSelectedFonts.php b/phpunit/tests/fonts-api/wpFontsResolver/enqueueUserSelectedFonts.php deleted file mode 100644 index 9ff01f1c3166c..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsResolver/enqueueUserSelectedFonts.php +++ /dev/null @@ -1,131 +0,0 @@ -user->create( - array( - 'role' => 'administrator', - 'user_email' => 'administrator@example.com', - ) - ); - } - - /** - * @dataProvider data_should_not_enqueue_when_no_user_selected_fonts - * - * @param array $styles Optional. Test styles. Default empty array. - */ - public function test_should_not_enqueue_when_no_user_selected_fonts( $styles = array() ) { - $this->set_up_global_styles( $styles ); - - $mock = $this->set_up_mock( 'enqueue' ); - $mock->expects( $this->never() ) - ->method( 'enqueue' ); - - $expected = array(); - $this->assertSame( $expected, WP_Fonts_Resolver::enqueue_user_selected_fonts() ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_not_enqueue_when_no_user_selected_fonts() { - return array( - 'no user-selected styles' => array(), - 'invalid element' => array( - array( - 'elements' => array( - 'invalid' => array( - 'typography' => array( - 'fontFamily' => 'var:preset|font-family|font1', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - ), - ), - ), - ), - ); - } - - /** - * @dataProvider data_should_enqueue_when_user_selected_fonts - * - * @param array $styles Test styles. - * @param array $expected Expected results. - */ - public function test_should_enqueue_when_user_selected_fonts( $styles, $expected ) { - $mock = $this->set_up_mock( 'enqueue' ); - $mock->expects( $this->once() ) - ->method( 'enqueue' ) - ->with( - $this->identicalTo( $expected ) - ); - - $this->set_up_global_styles( $styles ); - - $this->assertSameSets( $expected, WP_Fonts_Resolver::enqueue_user_selected_fonts() ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_enqueue_when_user_selected_fonts() { - $global_styles = $this->get_mock_user_selected_fonts_global_styles(); - - return array( - 'heading, caption, text' => array( - 'styles' => $global_styles['font1'], - 'expected' => array( 'font1' ), - ), - 'heading, button' => array( - 'styles' => $global_styles['font2'], - 'expected' => array( 'font2' ), - ), - 'text' => array( - 'styles' => $global_styles['font3'], - 'expected' => array( 'font3' ), - ), - 'all' => array( - 'styles' => $global_styles['all'], - 'expected' => array( - 0 => 'font1', - // font1 occurs 2 more times and gets removed as duplicates. - 3 => 'font2', - 4 => 'font3', - ), - ), - 'all with invalid element' => array( - 'styles' => $global_styles['all with invalid element'], - 'expected' => array( - 0 => 'font1', - // font1 occurs 2 more times and gets removed as duplicates. - 3 => 'font2', - // Skips font2 for the "invalid" element. - 4 => 'font3', - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsResolver/registerFontsFromThemeJson.php b/phpunit/tests/fonts-api/wpFontsResolver/registerFontsFromThemeJson.php deleted file mode 100644 index 0186dbf34f3fd..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsResolver/registerFontsFromThemeJson.php +++ /dev/null @@ -1,297 +0,0 @@ - array( - // From theme.json. - 'dm-sans', - 'source-serif-pro', - // From style variation. - 'open-sans', - ), - ); - - public static function set_up_before_class() { - self::$requires_switch_theme_fixtures = true; - - parent::set_up_before_class(); - } - - public function test_should_bails_out_when_no_fonts_defined() { - switch_theme( 'block-theme' ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - $wp_fonts = wp_fonts(); - - $this->assertEmpty( $wp_fonts->get_registered() ); - $this->assertEmpty( $wp_fonts->get_enqueued() ); - } - - public function test_should_register_and_enqueue_style_variation_fonts() { - switch_theme( static::FONTS_THEME ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - $wp_fonts = wp_fonts(); - - $this->assertContains( 'open-sans', $wp_fonts->get_registered_font_families(), 'Font families should be registered' ); - $this->assertContains( 'open-sans', $wp_fonts->get_enqueued(), 'Font families should be enqueued' ); - } - - /** - * Tests all font families are registered and enqueued. "All" means all font families from - * the theme's theme.json and within the style variations. - */ - public function test_should_register_and_enqueue_all_defined_font_families() { - switch_theme( static::FONTS_THEME ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - $wp_fonts = wp_fonts(); - - $expected = static::FONT_FAMILIES[ static::FONTS_THEME ]; - $this->assertSameSetsWithIndex( $expected, $wp_fonts->get_registered_font_families(), 'Font families should be registered' ); - $this->assertSameSetsWithIndex( $expected, $wp_fonts->get_enqueued(), 'Font families should be enqueued' ); - } - - /** - * Test ensures duplicate fonts and variations in the style variations - * are not re-registered. - * - * The Dm Sans fonts are duplicated in the theme's /styles/variations-duplicate-fonts.json. - */ - public function test_should_not_reregister_duplicate_fonts_from_style_variations() { - switch_theme( static::FONTS_THEME ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - $wp_fonts = wp_fonts(); - - // Font families are not duplicated. - $this->assertSameSetsWithIndex( - static::FONT_FAMILIES[ static::FONTS_THEME ], - $wp_fonts->get_registered_font_families(), - 'Font families should not be duplicated' - ); - - // Font variations are not duplicated. - $this->assertSameSets( - array( - // From theme.json. - 'dm-sans', - 'dm-sans-400-normal', - 'dm-sans-400-italic', - 'dm-sans-700-normal', - 'dm-sans-700-italic', - 'source-serif-pro', - 'source-serif-pro-200-900-normal', - 'source-serif-pro-200-900-italic', - // From style variation. - 'open-sans', - 'open-sans-400-normal', - 'open-sans-400-italic', - 'dm-sans-500-normal', - 'dm-sans-500-italic', - ), - $wp_fonts->get_registered(), - 'Font families and their variations should not be duplicated' - ); - } - - /** - * @dataProvider data_should_replace_src_file_placeholder - * - * @param string $handle Variation's handle. - * @param string $expected Expected src. - */ - public function test_should_replace_src_file_placeholder( $handle, $expected ) { - switch_theme( static::FONTS_THEME ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - - $variation = wp_fonts()->registered[ $handle ]; - $actual = array_pop( $variation->src ); - $expected = get_stylesheet_directory_uri() . $expected; - - $this->assertStringNotContainsString( 'file:./', $actual, 'Font src should not contain the "file:./" placeholder' ); - $this->assertSame( $expected, $actual, 'Font src should be an URL to its file' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_replace_src_file_placeholder() { - return array( - // Theme's theme.json. - 'DM Sans: 400 normal' => array( - 'handle' => 'dm-sans-400-normal', - 'expected' => '/assets/fonts/dm-sans/DMSans-Regular.woff2', - ), - 'DM Sans: 400 italic' => array( - 'handle' => 'dm-sans-400-italic', - 'expected' => '/assets/fonts/dm-sans/DMSans-Regular-Italic.woff2', - ), - 'DM Sans: 700 normal' => array( - 'handle' => 'dm-sans-700-normal', - 'expected' => '/assets/fonts/dm-sans/DMSans-Bold.woff2', - ), - 'DM Sans: 700 italic' => array( - 'handle' => 'dm-sans-700-italic', - 'expected' => '/assets/fonts/dm-sans/DMSans-Bold-Italic.woff2', - ), - 'Source Serif Pro: 200-900 normal' => array( - 'handle' => 'source-serif-pro-200-900-normal', - 'expected' => '/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'Source Serif Pro: 200-900 italic' => array( - 'handle' => 'source-serif-pro-200-900-italic', - 'expected' => '/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - ), - - // Style Variation: variation-with-new-font-family.json. - 'Style Variation: new font-family' => array( - 'handle' => 'open-sans-400-normal', - 'expected' => '/assets/fonts/open-sans/OpenSans-VariableFont_wdth,wght.tff', - ), - 'Style Variation: new font-family italic variation' => array( - 'handle' => 'open-sans-400-italic', - 'expected' => '/assets/fonts/open-sans/OpenSans-Italic-VariableFont_wdth,wght.tff', - ), - - // Style Variation: variation-with-new-variation.json. - 'Style Variation: new medium variation' => array( - 'handle' => 'dm-sans-500-normal', - 'expected' => '/assets/fonts/dm-sans/DMSans-Medium.woff2', - ), - 'Style Variation: new medium italic variation' => array( - 'handle' => 'dm-sans-500-italic', - 'expected' => '/assets/fonts/dm-sans/DMSans-Medium-Italic.woff2', - ), - ); - } - - public function test_should_convert_font_face_properties_into_kebab_case() { - switch_theme( static::FONTS_THEME ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - - // Testing only one variation since this theme's fonts use the same properties. - $variation = wp_fonts()->registered['dm-sans-400-normal']; - $actual_properties = $variation->extra['font-properties']; - - $this->assertArrayHasKey( 'font-family', $actual_properties, 'fontFamily should have been converted into font-family' ); - $this->assertArrayNotHasKey( 'fontFamily', $actual_properties, 'fontFamily should not exist.' ); - $this->assertArrayHasKey( 'font-stretch', $actual_properties, 'fontStretch should have been converted into font-stretch' ); - $this->assertArrayNotHasKey( 'fontStretch', $actual_properties, 'fontStretch should not exist' ); - $this->assertArrayHasKey( 'font-style', $actual_properties, 'fontStyle should have been converted into font-style' ); - $this->assertArrayNotHasKey( 'fontStyle', $actual_properties, 'fontStyle should not exist.' ); - $this->assertArrayHasKey( 'font-weight', $actual_properties, 'fontWeight should have been converted into font-weight' ); - $this->assertArrayNotHasKey( 'fontWeight', $actual_properties, 'fontWeight should not exist' ); - } - - /** - * Tests that WP_Fonts_Resolver::register_fonts_from_theme_json() skips fonts that are already registered - * in the Fonts API. How does it do that? Using the 'origin' property when checking each variation. - * This property is added when WP_Theme_JSON_Resolver_Gutenberg::get_merged_data() runs. - * - * To simulate this scenario, a font is registered first, but not enqueued. Then after running, - * it checks if the WP_Fonts_Resolver::register_fonts_from_theme_json() enqueued the font. If no, then - * it was skipped as expected. - */ - public function test_should_skip_registered_fonts() { - switch_theme( static::FONTS_THEME ); - - // Register Lato font. - wp_register_fonts( - array( - 'Lato' => array( - array( - 'font-family' => 'Lato', - 'font-style' => 'normal', - 'font-weight' => '400', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular.woff2', - ), - array( - 'font-family' => 'Lato', - 'font-style' => 'italic', - 'font-weight' => '400', - 'src' => 'https://example.com/tests/assets/fonts/lato/Lato-Regular-Italic.woff2', - ), - ), - ) - ); - - // Pre-check to ensure no fonts are enqueued. - $this->assertEmpty( wp_fonts()->get_enqueued(), 'No fonts should be enqueued before running WP_Fonts_Resolver::register_fonts_from_theme_json()' ); - - /* - * When this function runs, it invokes WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(), - * which will include the Lato fonts with a 'origin' property set in each variation. - */ - WP_Fonts_Resolver::register_fonts_from_theme_json(); - - $actual_enqueued_fonts = wp_fonts()->get_enqueued(); - - $this->assertNotContains( 'lato', $actual_enqueued_fonts, 'Lato font-family should not be enqueued' ); - $this->assertSameSets( static::FONT_FAMILIES[ static::FONTS_THEME ], $actual_enqueued_fonts, 'Only the theme font families should be enqueued' ); - } - - public function test_should_skip_when_font_face_not_defined() { - switch_theme( static::FONTS_THEME ); - $expected_font_family = 'source-serif-pro'; - - /** - * Callback that removes the 'fontFace' of the expected font family from the theme's theme.json data. - * This callback is invoked at the start of WP_Fonts_Resolver::register_fonts_from_theme_json() before processing - * within that function. How? It's in the call stack of WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(). - * - * @param WP_Theme_JSON_Data_Gutenberg| WP_Theme_JSON_Data $theme_json_data Instance of the Data object. - * @return WP_Theme_JSON_Data_Gutenberg| WP_Theme_JSON_Data Modified instance. - * @throws ReflectionException - */ - $remove_expected_font_family = static function ( $theme_json_data ) use ( $expected_font_family ) { - // Need to get the underlying data array which is in WP_Theme_JSON_Gutenberg | WP_Theme_JSON object. - $property = new ReflectionProperty( $theme_json_data, 'theme_json' ); - $property->setAccessible( true ); - $theme_json_object = $property->getValue( $theme_json_data ); - - $property = new ReflectionProperty( $theme_json_object, 'theme_json' ); - $property->setAccessible( true ); - $data = $property->getValue( $theme_json_object ); - - // Loop through the fonts to find the expected font-family to modify. - foreach ( $data['settings']['typography']['fontFamilies']['theme'] as $index => $definitions ) { - if ( $expected_font_family !== $definitions['slug'] ) { - continue; - } - - // Remove the 'fontFace' element, which removes the font's variations. - unset( $data['settings']['typography']['fontFamilies']['theme'][ $index ]['fontFace'] ); - break; - } - - $theme_json_data->update_with( $data ); - - return $theme_json_data; - }; - add_filter( 'wp_theme_json_data_theme', $remove_expected_font_family ); - - WP_Fonts_Resolver::register_fonts_from_theme_json(); - - remove_filter( 'wp_theme_json_data_theme', $remove_expected_font_family ); - - $this->assertNotContains( $expected_font_family, wp_fonts()->get_registered_font_families() ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsUtils/convertFontFamilyIntoHandle.php b/phpunit/tests/fonts-api/wpFontsUtils/convertFontFamilyIntoHandle.php deleted file mode 100644 index 19b2f15c826ed..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsUtils/convertFontFamilyIntoHandle.php +++ /dev/null @@ -1,84 +0,0 @@ -assertSame( $expected, WP_Fonts_Utils::convert_font_family_into_handle( $font_family ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_valid_input() { - return array( - 'font family single word name' => array( - 'font_family' => 'Merriweather', - 'expected' => 'merriweather', - ), - 'font family multiword name' => array( - 'font_family' => 'Source Sans Pro', - 'expected' => 'source-sans-pro', - ), - 'font family handle delimited by hyphens' => array( - 'font_family' => 'source-serif-pro', - 'expected' => 'source-serif-pro', - ), - 'font family handle delimited by underscore' => array( - 'font_family' => 'source_serif_pro', - 'expected' => 'source_serif_pro', - ), - 'font family handle delimited by hyphens and underscore' => array( - 'font_family' => 'my-custom_font_family', - 'expected' => 'my-custom_font_family', - ), - 'font family handle delimited mixture' => array( - 'font_family' => 'My custom_font-family', - 'expected' => 'my-custom_font-family', - ), - ); - } - - /** - * @dataProvider data_with_invalid_input - * - * @covers WP_Fonts_Utils::convert_font_family_into_handle - * - * @param mixed $invalid_input Invalid input. - */ - public function test_should_not_convert_with_invalid_input( $invalid_input ) { - $this->assertNull( WP_Fonts_Utils::convert_font_family_into_handle( $invalid_input ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_invalid_input() { - return array( - 'empty string' => array( '' ), - 'integer' => array( 10 ), - 'font family wrapped in an array' => array( array( 'source-serif-pro' ) ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsUtils/convertVariationIntoHandle.php b/phpunit/tests/fonts-api/wpFontsUtils/convertVariationIntoHandle.php deleted file mode 100644 index 9268aac8ce372..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsUtils/convertVariationIntoHandle.php +++ /dev/null @@ -1,122 +0,0 @@ -assertSame( $expected, WP_Fonts_Utils::convert_variation_into_handle( $font_family, $variation ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_valid_input() { - return array( - 'with only font-weight' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'font-weight' => '400', - ), - 'expected' => 'merriweather-400', - ), - 'with no font-style' => array( - 'font_family' => 'source-sans-pro', - 'variation' => array( - 'font-weight' => '200 900', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'provider' => 'local', - ), - 'expected' => 'source-sans-pro-200-900', - ), - 'with font family name and full variant' => array( - 'font_family' => 'source-sans-pro', - 'variation' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'expected' => 'source-sans-pro-200-900-normal', - ), - ); - } - - /** - * @dataProvider data_with_invalid_input - * - * @param string $font_family Font family to test. - * @param array $invalid_input Variation to test. - */ - public function tests_should_convert_with_invalid_input( $font_family, $invalid_input ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Variant handle could not be determined as font-weight and/or font-style are require' ); - - $this->assertNull( WP_Fonts_Utils::convert_variation_into_handle( $font_family, $invalid_input ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_invalid_input() { - return array( - 'with no font-weight or font-style' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'provider' => 'local', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - ), - 'with non-string font-weight' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'font-weight' => 400, - ), - ), - 'with non-string font-style' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'font-style' => 0, - ), - ), - 'with empty string font-weight' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'font-weight' => '', - ), - ), - 'with empty string font-style' => array( - 'font_family' => 'merriweather', - 'variation' => array( - 'font-style' => '', - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsUtils/getFontFamilyFromVariation.php b/phpunit/tests/fonts-api/wpFontsUtils/getFontFamilyFromVariation.php deleted file mode 100644 index 2b3e3689c4317..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsUtils/getFontFamilyFromVariation.php +++ /dev/null @@ -1,134 +0,0 @@ -assertSame( $expected, WP_Fonts_Utils::get_font_family_from_variation( $variation ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_valid_variation() { - return array( - 'keyed by font-family' => array( - 'variation' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'expected' => 'Source Serif Pro', - ), - 'keyed by fontFamily and as a handle' => array( - 'variation' => array( - 'fontFamily' => 'source-sans-pro', - 'font-weight' => '200 900', - 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', - 'provider' => 'local', - ), - 'expected' => 'source-sans-pro', - ), - 'with font family name and full variant' => array( - 'variation' => array( - 'provider' => 'local', - 'font-family' => 'Merriweather', - 'font-style' => 'normal', - 'font-weight' => '400 600', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/merriweather.ttf.woff2', - 'font-display' => 'fallback', - ), - 'expected' => 'Merriweather', - ), - ); - } - - /** - * @dataProvider data_with_invalid_input - * - * @param array $invalid_variation Variation to test. - * @param string $expected_message Expected notice message. - */ - public function test_with_invalid_input( array $invalid_variation, $expected_message ) { - $this->expectNotice(); - $this->expectNoticeMessage( $expected_message ); - - $this->assertNull( WP_Fonts_Utils::get_font_family_from_variation( $invalid_variation ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_with_invalid_input() { - return array( - 'keyed with underscore' => array( - 'variation' => array( - 'provider' => 'local', - 'font_family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'expected_message' => 'Font family not found.', - ), - 'keyed with space' => array( - 'variation' => array( - 'font family' => 'Source Sans Pro', - 'font-weight' => '200 900', - 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', - 'provider' => 'local', - ), - 'expected_message' => 'Font family not found.', - ), - 'fontFamily => empty string' => array( - 'variation' => array( - 'fontFamily' => '', - 'font-weight' => '200 900', - 'src' => 'https://example.com/assets/fonts/source-sans-pro/source-sans-pro.ttf.woff2', - 'provider' => 'local', - ), - 'expected_message' => 'Font family not defined in the variation.', - ), - 'font-family => empty string' => array( - 'variation' => array( - 'provider' => 'local', - 'font-family' => '', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - 'expected_message' => 'Font family not defined in the variation.', - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpFontsUtils/isDefined.php b/phpunit/tests/fonts-api/wpFontsUtils/isDefined.php deleted file mode 100644 index 3ae48ad52671a..0000000000000 --- a/phpunit/tests/fonts-api/wpFontsUtils/isDefined.php +++ /dev/null @@ -1,61 +0,0 @@ -assertTrue( WP_Fonts_Utils::is_defined( $input ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_when_defined() { - return array( - 'name: non empty string' => array( 'Some Font Family' ), - 'handle: non empty string' => array( 'some-font-family' ), - ); - } - - /** - * @dataProvider data_when_not_defined - * - * @param mixed $invalid_input Input to test. - */ - public function test_should_return_false_when_not_defined( $invalid_input ) { - $this->assertFalse( WP_Fonts_Utils::is_defined( $invalid_input ) ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_when_not_defined() { - return array( - 'empty string' => array( '' ), - 'string 0' => array( '0' ), - 'integer' => array( 10 ), - 'name wrapped in an array' => array( array( 'Some Font Family' ) ), - 'handle wrapped in an array' => array( array( 'some-font-family' ) ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpPrintFonts.php b/phpunit/tests/fonts-api/wpPrintFonts.php deleted file mode 100644 index 40b416aa56421..0000000000000 --- a/phpunit/tests/fonts-api/wpPrintFonts.php +++ /dev/null @@ -1,230 +0,0 @@ -assertSame( array(), wp_print_fonts() ); - } - - /** - * Unit test which mocks WP_Fonts methods. - * - * @dataProvider data_mocked_handles - * - * @param string|string[] $handles Handles to test. - */ - public function test_should_return_mocked_handles( $handles ) { - $mock = $this->set_up_mock( array( 'get_registered_font_families', 'do_items' ) ); - $mock->expects( $this->once() ) - ->method( 'get_registered_font_families' ) - ->will( $this->returnValue( $handles ) ); - - $mock->expects( $this->once() ) - ->method( 'do_items' ) - ->with( - $this->identicalTo( $handles ) - ) - ->will( $this->returnValue( $handles ) ); - - wp_print_fonts( $handles ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_mocked_handles() { - return array( - 'font family' => array( - array( 'my-custom-font' ), - ), - 'multiple font families' => array( - array( - 'font1', - 'font2', - ), - ), - ); - } - - /** - * Integration test that registers providers and fonts and then enqueues before - * testing the printing functionality. - * - * @dataProvider data_print_enqueued - * - * @param array $setup Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_enqueued( $setup, $expected_done, $expected_output ) { - $wp_fonts = wp_fonts(); - - $this->setup_integrated_deps( $setup, $wp_fonts ); - - $this->expectOutputString( $expected_output ); - $actual_done = wp_print_fonts(); - $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' ); - } - - /** - * Integration test to validate printing given handles. Rather than mocking internal functionality, - * it registers providers and fonts but does not enqueue. - * - * @dataProvider data_print_enqueued - * - * @param array $setup Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_handles_when_not_enqueued( $setup, $expected_done, $expected_output ) { - $wp_fonts = wp_fonts(); - - $this->setup_integrated_deps( $setup, $wp_fonts, false ); - // Do not enqueue. Instead, pass the handles to wp_print_fonts(). - $handles = $setup['enqueued']; - $this->assertEmpty( $wp_fonts->queue, 'No fonts should be enqueued' ); - - $this->expectOutputString( $expected_output ); - $actual_done = wp_print_fonts( $handles ); - $this->assertSameSets( $expected_done, $actual_done, 'Printed handles should match' ); - } - - /** - * @dataProvider data_should_print_all_registered_fonts_for_iframed_editor - * - * @param string $fonts Fonts to register. - * @param array $expected Expected results. - */ - public function test_should_print_all_registered_fonts_for_iframed_editor( $fonts, $expected ) { - wp_register_fonts( $fonts ); - - $this->expectOutputString( $expected['output'] ); - $actual_done = wp_print_fonts( true ); - $this->assertSameSets( $expected['done'], $actual_done, 'All registered font-family handles should be returned' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_should_print_all_registered_fonts_for_iframed_editor() { - $local_fonts = $this->get_registered_local_fonts(); - $font_faces = $this->get_registered_fonts_css(); - - return array( - 'Merriweather with 1 variation' => array( - 'fonts' => array( 'merriweather' => $local_fonts['merriweather'] ), - 'expected' => array( - 'done' => array( 'merriweather', 'merriweather-200-900-normal' ), - 'output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'] - ), - ), - ), - 'Source Serif Pro with 2 variations' => array( - 'fonts' => array( 'Source Serif Pro' => $local_fonts['Source Serif Pro'] ), - 'expected' => array( - 'done' => array( 'source-serif-pro', 'Source Serif Pro-300-normal', 'Source Serif Pro-900-italic' ), - 'output' => sprintf( - "\n", - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - ), - 'all fonts' => array( - 'fonts' => $local_fonts, - 'expected' => array( - 'done' => array( - 'merriweather', - 'merriweather-200-900-normal', - 'source-serif-pro', - 'Source Serif Pro-300-normal', - 'Source Serif Pro-900-italic', - ), - 'output' => sprintf( - "\n", - $font_faces['merriweather-200-900-normal'], - $font_faces['Source Serif Pro-300-normal'], - $font_faces['Source Serif Pro-900-italic'] - ), - ), - ), - ); - } - - /** - * Integration test for printing user-selected global fonts. - * This test registers providers and fonts and then enqueues before testing the printing functionality. - * - * @dataProvider data_print_user_selected_fonts - * - * @param array $global_styles Test set up information for provider, fonts, and enqueued. - * @param array $expected_done Expected array of printed handles. - * @param string $expected_output Expected printed output. - */ - public function test_should_print_user_selected_fonts( $global_styles, $expected_done, $expected_output ) { - $wp_fonts = wp_fonts(); - - $setup = array( - 'provider' => array( 'mock' => $this->get_provider_definitions( 'mock' ) ), - 'registered' => $this->get_registered_mock_fonts(), - 'global_styles' => $global_styles, - ); - $this->setup_integrated_deps( $setup, $wp_fonts, false ); - - $this->expectOutputString( $expected_output ); - $actual_printed_fonts = wp_print_fonts(); - $this->assertSameSets( $expected_done, $actual_printed_fonts, 'Should print font-faces for given user-selected fonts' ); - } - - - /** - * Sets up the dependencies for integration test. - * - * @param array $setup Dependencies to set up. - * @param WP_Fonts $wp_fonts Instance of WP_Fonts. - * @param bool $enqueue Whether to enqueue. Default true. - */ - private function setup_integrated_deps( array $setup, $wp_fonts, $enqueue = true ) { - foreach ( $setup['provider'] as $provider ) { - $wp_fonts->register_provider( $provider['id'], $provider['class'] ); - } - foreach ( $setup['registered'] as $handle => $variations ) { - $this->setup_register( $handle, $variations, $wp_fonts ); - } - - if ( $enqueue ) { - $wp_fonts->enqueue( $setup['enqueued'] ); - } - - if ( ! empty( $setup['global_styles'] ) ) { - $this->set_up_global_styles( $setup['global_styles'] ); - } - } -} diff --git a/phpunit/tests/fonts-api/wpRegisterFontProvider.php b/phpunit/tests/fonts-api/wpRegisterFontProvider.php deleted file mode 100644 index 907355bdc91c8..0000000000000 --- a/phpunit/tests/fonts-api/wpRegisterFontProvider.php +++ /dev/null @@ -1,95 +0,0 @@ -set_up_mock( 'register_provider' ); - $mock->expects( $this->once() ) - ->method( 'register_provider' ) - ->with( - $this->identicalTo( $provider_id ), - $this->identicalTo( $class_name ) - ) - ->will( $this->returnValue( true ) ); - - $this->assertTrue( wp_register_font_provider( $provider_id, $class_name ), 'wp_register_font_provider() should return true' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_register_providers() { - return array( - 'mock' => array( - 'provider_id' => 'mock', - 'class' => Mock_Provider::class, - ), - 'local' => array( - 'provider_id' => 'local', - 'class' => WP_Fonts_Provider_Local::class, - ), - ); - } - - /** - * @dataProvider data_invalid_providers - * - * @param string $provider_id Provider ID. - * @param string $class_name Provider class name. - */ - public function test_should_not_register( $provider_id, $class_name ) { - $mock = $this->set_up_mock( 'register_provider' ); - $mock->expects( $this->once() ) - ->method( 'register_provider' ) - ->with( - $this->identicalTo( $provider_id ), - $this->identicalTo( $class_name ) - ) - ->will( $this->returnValue( false ) ); - - $this->assertFalse( wp_register_font_provider( $provider_id, $class_name ), 'wp_register_font_provider() should return false' ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_invalid_providers() { - return array( - 'provider_id is empty' => array( - 'provider_id' => '', - 'class' => Mock_Provider::class, - ), - 'class is empty' => array( - 'provider_id' => 'local', - 'class' => '', - ), - 'class does not exist' => array( - 'provider_id' => 'doesnotexist', - 'class' => 'Provider_Does_Not_Exist', - ), - ); - } -} diff --git a/phpunit/tests/fonts-api/wpRegisterFonts.php b/phpunit/tests/fonts-api/wpRegisterFonts.php deleted file mode 100644 index c57c1406a6cac..0000000000000 --- a/phpunit/tests/fonts-api/wpRegisterFonts.php +++ /dev/null @@ -1,104 +0,0 @@ -assertSame( $expected['wp_register_fonts'], $actual, 'Font family handle(s) should be returned' ); - $this->assertSame( $expected['get_registered'], $this->get_registered_handles(), 'Web fonts should match registered queue' ); - } - - /** - * @dataProvider data_fonts - * - * @param array $fonts Array of fonts to test. - */ - public function test_should_not_enqueue_on_registration( array $fonts ) { - wp_register_fonts( $fonts ); - $this->assertEmpty( $this->get_enqueued_handles() ); - } - - /** - * Data provider. - * - * @return array - */ - public function data_fonts() { - return array( - 'font family keyed with slug' => array( - 'fonts' => array( - 'source-serif-pro' => array( - array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - ), - ), - 'expected' => array( - 'wp_register_fonts' => array( 'source-serif-pro' ), - 'get_registered' => array( - 'source-serif-pro', - 'source-serif-pro-200-900-normal', - ), - ), - ), - 'font family keyed with name' => array( - 'fonts' => array( - 'Source Serif Pro' => array( - array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'font-display' => 'fallback', - ), - array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'font-display' => 'fallback', - ), - ), - ), - 'expected' => array( - 'wp_register_fonts' => array( 'source-serif-pro' ), - 'get_registered' => array( - 'source-serif-pro', - 'source-serif-pro-200-900-normal', - 'source-serif-pro-200-900-italic', - ), - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php new file mode 100644 index 0000000000000..a971bd5123430 --- /dev/null +++ b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php @@ -0,0 +1,164 @@ +create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + $font_family = 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 ); + + $font_family_title = 'Open Sans'; + $this->assertSame( $font_family_title, $font_family->post_title ); + + $font_family_slug = 'open-sans'; + $this->assertSame( $font_family_slug, $font_family->post_name ); + + $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 ); + + $meta = get_post_meta( $font_family_id, '_gutenberg_legacy_font_family', true ); + $this->assertSame( $legacy_content, $meta ); + + // 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 ); + + $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 ); + + $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 ); + + // 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 ); + + // 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 ); + + $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 ); + + $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 ); + + // 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 ); + + // 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 ); + + $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 ); + + $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 ); + + // 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 ); + } + + public function test_font_faces_with_local_src() { + $legacy_content = '{"fontFace":[{"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":"' . site_url() . '/wp-content/fonts/open-sans_normal_400.ttf"}],"fontFamily":"\'Open Sans\', sans-serif","name":"Open Sans","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans.svg","slug":"open-sans"}'; + + $font_family_id = $this->create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + $font_faces = $this->get_font_faces( $font_family_id ); + $this->assertCount( 1, $font_faces ); + $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 ); + } + + public function test_migration_only_runs_once() { + $legacy_content = '{"fontFace":[],"fontFamily":"\'Open Sans\', sans-serif","name":"Open Sans","preview":"","slug":"open-sans"}'; + + // Simulate that the migration has already run. + update_option( 'gutenberg_font_family_format_converted', true ); + + $font_family_id = $this->create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + // 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( + array( + 'post_type' => 'wp_font_family', + 'post_status' => 'publish', + 'post_title' => 'Open Sans', + 'post_name' => 'open-sans', + 'post_content' => $content, + ) + ); + } + + protected function get_font_faces( $font_family_id ) { + return get_posts( + array( + 'post_parent' => $font_family_id, + 'post_type' => 'wp_font_face', + 'order' => 'ASC', + 'orderby' => 'id', + ) + ); + } +} diff --git a/phpunit/tests/fonts/font-library/fontLibraryHooks.php b/phpunit/tests/fonts/font-library/fontLibraryHooks.php new file mode 100644 index 0000000000000..2c471e2a9759c --- /dev/null +++ b/phpunit/tests/fonts/font-library/fontLibraryHooks.php @@ -0,0 +1,85 @@ +post->create( + array( + 'post_type' => 'wp_font_family', + ) + ); + $font_face_id = self::factory()->post->create( + array( + 'post_type' => 'wp_font_face', + 'post_parent' => $font_family_id, + ) + ); + $other_font_family_id = self::factory()->post->create( + array( + 'post_type' => 'wp_font_family', + ) + ); + $other_font_face_id = self::factory()->post->create( + array( + 'post_type' => 'wp_font_face', + 'post_parent' => $other_font_family_id, + ) + ); + + wp_delete_post( $font_family_id, true ); + + $this->assertNull( get_post( $font_face_id ) ); + $this->assertNotNull( get_post( $other_font_face_id ) ); + } + + public function test_deleting_font_faces_deletes_associated_font_files() { + list( $font_face_id, $font_path ) = $this->create_font_face_with_file( 'OpenSans-Regular.woff2' ); + list( , $other_font_path ) = $this->create_font_face_with_file( 'OpenSans-Regular.ttf' ); + + wp_delete_post( $font_face_id, true ); + + $this->assertFalse( file_exists( $font_path ) ); + $this->assertTrue( file_exists( $other_font_path ) ); + } + + protected function create_font_face_with_file( $filename ) { + $font_face_id = self::factory()->post->create( + array( + 'post_type' => 'wp_font_face', + ) + ); + + $font_file = $this->upload_font_file( $filename ); + + // Make sure the font file uploaded successfully. + $this->assertFalse( $font_file['error'] ); + + $font_path = $font_file['file']; + $font_filename = basename( $font_path ); + add_post_meta( $font_face_id, '_wp_font_face_file', $font_filename ); + + return array( $font_face_id, $font_path ); + } + + 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_dir', 'wp_get_font_dir' ); + $font_file = wp_upload_bits( + $font_filename, + null, + 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' ) ); + + return $font_file; + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php index 380226ee8af8a..8e9cf7bca08e5 100644 --- a/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php +++ b/phpunit/tests/fonts/font-library/wpFontCollection/__construct.php @@ -13,32 +13,50 @@ class Tests_Fonts_WpFontCollection_Construct extends WP_UnitTestCase { public function test_should_initialize_data() { - $property = new ReflectionProperty( WP_Font_Collection::class, 'config' ); - $property->setAccessible( true ); + $slug = new ReflectionProperty( WP_Font_Collection::class, 'slug' ); + $slug->setAccessible( true ); - $config = array( + $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', ); - $font_collection = new WP_Font_Collection( $config ); + $collection = new WP_Font_Collection( $config ); - $actual = $property->getValue( $font_collection ); - $property->setAccessible( false ); + $actual_slug = $slug->getValue( $collection ); + $this->assertSame( 'my-collection', $actual_slug, 'Provided slug and initialized slug should match.' ); + $slug->setAccessible( false ); - $this->assertSame( $config, $actual ); + $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_throw_exception + * @dataProvider data_should_do_ti_wrong * * @param mixed $config Config of the font collection. - * @param string $expected_exception_message Expected exception message. */ - public function test_should_throw_exception( $config, $expected_exception_message ) { - $this->expectException( 'Exception' ); - $this->expectExceptionMessage( $expected_exception_message ); + public function test_should_do_ti_wrong( $config ) { + $this->setExpectedIncorrectUsage( 'WP_Font_Collection::is_config_valid' ); new WP_Font_Collection( $config ); } @@ -47,7 +65,7 @@ public function test_should_throw_exception( $config, $expected_exception_messag * * @return array */ - public function data_should_throw_exception() { + public function data_should_do_ti_wrong() { return array( 'no id' => array( array( @@ -55,27 +73,22 @@ public function data_should_throw_exception() { 'description' => 'My collection description', 'src' => 'my-collection-data.json', ), - 'Font Collection config slug is required as a non-empty string.', ), 'no config' => array( '', - 'Font Collection config options is required as a non-empty array.', ), 'empty array' => array( array(), - 'Font Collection config options is required as a non-empty array.', ), 'boolean instead of config array' => array( false, - 'Font Collection config options is required as a non-empty array.', ), 'null instead of config array' => array( null, - 'Font Collection config options is required as a non-empty array.', ), 'missing src' => array( @@ -84,9 +97,7 @@ public function data_should_throw_exception() { 'name' => 'My Collection', 'description' => 'My collection description', ), - 'Font Collection config "src" option OR "data" option is required.', ), - ); } } diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php b/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php index 5f1f082297d41..393de7d22614d 100644 --- a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php +++ b/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php @@ -32,7 +32,7 @@ public function data_should_get_config() { file_put_contents( $mock_file, '{"this is mock data":true}' ); return array( - 'with a file' => array( + 'with a file' => array( 'config' => array( 'slug' => 'my-collection', 'name' => 'My Collection', @@ -45,7 +45,7 @@ public function data_should_get_config() { 'description' => 'My collection description', ), ), - 'with a url' => array( + 'with a url' => array( 'config' => array( 'slug' => 'my-collection-with-url', 'name' => 'My Collection with URL', @@ -58,12 +58,12 @@ public function data_should_get_config() { 'description' => 'My collection description', ), ), - 'with data' => array( + 'with font_families' => array( 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'data' => array( 'this is mock data' => true ), + 'slug' => 'my-collection', + 'name' => 'My Collection', + 'description' => 'My collection description', + 'font_families' => array( array() ), ), 'expected_data' => array( 'slug' => 'my-collection', diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getConfigAndData.php b/phpunit/tests/fonts/font-library/wpFontCollection/getContent.php similarity index 52% rename from phpunit/tests/fonts/font-library/wpFontCollection/getConfigAndData.php rename to phpunit/tests/fonts/font-library/wpFontCollection/getContent.php index 885b0a0b9036c..ab0e87cde000e 100644 --- a/phpunit/tests/fonts/font-library/wpFontCollection/getConfigAndData.php +++ b/phpunit/tests/fonts/font-library/wpFontCollection/getContent.php @@ -1,6 +1,6 @@ 'mock', - 'categories' => 'mock', + 'font_families' => array( 'mock' ), + 'categories' => array( 'mock' ), ); return array( @@ -47,14 +47,14 @@ public function mock_request( $preempt, $args, $url ) { } /** - * @dataProvider data_should_get_config_and_data + * @dataProvider data_should_get_content * * @param array $config Font collection config options. - * @param array $expected_data Expected data. + * @param array $expected_data Expected output data. */ - public function test_should_get_config_and_data( $config, $expected_data ) { + public function test_should_get_content( $config, $expected_data ) { $collection = new WP_Font_Collection( $config ); - $this->assertSame( $expected_data, $collection->get_config_and_data() ); + $this->assertSame( $expected_data, $collection->get_content() ); } /** @@ -62,12 +62,12 @@ public function test_should_get_config_and_data( $config, $expected_data ) { * * @return array[] */ - public function data_should_get_config_and_data() { + public function data_should_get_content() { $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, '{"this is mock data":true}' ); + file_put_contents( $mock_file, '{"font_families":[ "mock" ], "categories":[ "mock" ] }' ); return array( - 'with a file' => array( + 'with a file' => array( 'config' => array( 'slug' => 'my-collection', 'name' => 'My Collection', @@ -75,13 +75,11 @@ public function data_should_get_config_and_data() { 'src' => $mock_file, ), 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'data' => array( 'this is mock data' => true ), + 'font_families' => array( 'mock' ), + 'categories' => array( 'mock' ), ), ), - 'with a url' => array( + 'with a url' => array( 'config' => array( 'slug' => 'my-collection-with-url', 'name' => 'My Collection with URL', @@ -89,27 +87,33 @@ public function data_should_get_config_and_data() { 'src' => 'https://localhost/fonts/mock-font-collection.json', ), 'expected_data' => array( - 'slug' => 'my-collection-with-url', - 'name' => 'My Collection with URL', - 'description' => 'My collection description', - 'data' => array( - 'fontFamilies' => 'mock', - 'categories' => 'mock', - ), + 'font_families' => array( 'mock' ), + 'categories' => array( 'mock' ), ), ), - 'with data' => array( + 'with font_families and categories' => array( 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'data' => array( 'this is mock data' => true ), + 'slug' => 'my-collection', + 'name' => 'My Collection', + 'description' => 'My collection description', + 'font_families' => array( 'mock' ), + 'categories' => array( 'mock' ), ), 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'data' => array( 'this is mock data' => true ), + '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/isConfigValid.php b/phpunit/tests/fonts/font-library/wpFontCollection/isConfigValid.php new file mode 100644 index 0000000000000..7cfdfc829ab86 --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontCollection/isConfigValid.php @@ -0,0 +1,103 @@ +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_ti_wrong + * + * @param mixed $config Config of the font collection. + */ + public function test_is_config_valid_should_call_doing_ti_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_ti_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/wpFontFamily/__construct.php b/phpunit/tests/fonts/font-library/wpFontFamily/__construct.php index 3a1e387c3651b..cee0628dad310 100644 --- a/phpunit/tests/fonts/font-library/wpFontFamily/__construct.php +++ b/phpunit/tests/fonts/font-library/wpFontFamily/__construct.php @@ -30,11 +30,11 @@ public function test_should_initialize_data() { } /** - * @dataProvider data_should_throw_exception + * @dataProvider data_should_do_it_wrong * * @param mixed $font_data Data to test. */ - public function test_should_throw_exception( $font_data ) { + public function test_should_do_it_wrong( $font_data ) { $this->expectException( 'Exception' ); $this->expectExceptionMessage( 'Font family data is missing the slug.' ); @@ -46,7 +46,7 @@ public function test_should_throw_exception( $font_data ) { * * @return array */ - public function data_should_throw_exception() { + public function data_should_do_it_wrong() { return array( 'no slug' => array( array( diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/formatFontFamily.php b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/formatFontFamily.php index 4f247c5219feb..19987010d80a7 100644 --- a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/formatFontFamily.php +++ b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/formatFontFamily.php @@ -36,11 +36,11 @@ public function data_should_format_font_family() { return array( 'data_families_with_spaces_and_numbers' => array( 'font_family' => 'Rock 3D , Open Sans,serif', - 'expected' => "'Rock 3D', 'Open Sans', serif", + 'expected' => '"Rock 3D", "Open Sans", serif', ), 'data_single_font_family' => array( 'font_family' => 'Rock 3D', - 'expected' => "'Rock 3D'", + 'expected' => '"Rock 3D"', ), 'data_no_spaces' => array( 'font_family' => 'Rock3D', @@ -48,7 +48,7 @@ public function data_should_format_font_family() { ), 'data_many_spaces_and_existing_quotes' => array( 'font_family' => 'Rock 3D serif, serif,sans-serif, "Open Sans"', - 'expected' => "'Rock 3D serif', serif, sans-serif, \"Open Sans\"", + 'expected' => '"Rock 3D serif", serif, sans-serif, "Open Sans"', ), 'data_empty_family' => array( 'font_family' => ' ', diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php new file mode 100644 index 0000000000000..1f87d0d2fd5a1 --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php @@ -0,0 +1,81 @@ +assertSame( $expected_slug, $slug ); + } + + public function data_get_font_face_slug_normalizes_values() { + return array( + 'Sets defaults' => array( + 'settings' => array( + 'fontFamily' => 'Open Sans', + ), + 'expected_slug' => 'open sans;normal;400;100%;U+0-10FFFF', + ), + 'Converts normal weight to 400' => array( + 'settings' => array( + 'fontFamily' => 'Open Sans', + 'fontWeight' => 'normal', + ), + 'expected_slug' => 'open sans;normal;400;100%;U+0-10FFFF', + ), + 'Converts bold weight to 700' => array( + 'settings' => array( + 'fontFamily' => 'Open Sans', + 'fontWeight' => 'bold', + ), + 'expected_slug' => 'open sans;normal;700;100%;U+0-10FFFF', + ), + 'Converts normal font-stretch to 100%' => array( + 'settings' => array( + 'fontFamily' => 'Open Sans', + 'fontStretch' => 'normal', + ), + 'expected_slug' => 'open sans;normal;400;100%;U+0-10FFFF', + ), + 'Removes double quotes from fontFamilies' => array( + 'settings' => array( + 'fontFamily' => '"Open Sans"', + ), + 'expected_slug' => 'open sans;normal;400;100%;U+0-10FFFF', + ), + 'Removes single quotes from fontFamilies' => array( + 'settings' => array( + 'fontFamily' => "'Open Sans'", + ), + 'expected_slug' => 'open sans;normal;400;100%;U+0-10FFFF', + ), + 'Removes spaces between comma separated font families' => array( + 'settings' => array( + 'fontFamily' => 'Open Sans, serif', + ), + 'expected_slug' => 'open sans,serif;normal;400;100%;U+0-10FFFF', + ), + 'Removes tabs between comma separated font families' => array( + 'settings' => array( + 'fontFamily' => "Open Sans,\tserif", + ), + 'expected_slug' => 'open sans,serif;normal;400;100%;U+0-10FFFF', + ), + 'Removes new lines between comma separated font families' => array( + 'settings' => array( + 'fontFamily' => "Open Sans,\nserif", + ), + 'expected_slug' => 'open sans,serif;normal;400;100%;U+0-10FFFF', + ), + ); + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php index a7ea2870957e9..b06ae3c8d5354 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php @@ -29,9 +29,9 @@ public function test_should_return_error_if_slug_is_missing() { 'description' => 'My Collection Description', 'src' => 'my-collection-data.json', ); - $this->expectException( 'Exception' ); - $this->expectExceptionMessage( 'Font Collection config slug is required as a non-empty string.' ); - WP_Font_Library::register_font_collection( $config ); + $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() { @@ -40,16 +40,16 @@ public function test_should_return_error_if_name_is_missing() { 'description' => 'My Collection Description', 'src' => 'my-collection-data.json', ); - $this->expectException( 'Exception' ); - $this->expectExceptionMessage( 'Font Collection config name is required as a non-empty string.' ); - WP_Font_Library::register_font_collection( $config ); + $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->expectException( 'Exception' ); - $this->expectExceptionMessage( 'Font Collection config options is required as a non-empty array.' ); - WP_Font_Library::register_font_collection( $config ); + $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_slug_is_repeated() { diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php new file mode 100644 index 0000000000000..164f88f3f7b4b --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php @@ -0,0 +1,163 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + self::$editor_id = $factory->user->create( + array( + 'role' => 'editor', + ) + ); + $mock_file = wp_tempnam( 'my-collection-data-' ); + file_put_contents( $mock_file, '{"font_families": [ "mock" ], "categories": [ "mock" ] }' ); + + wp_register_font_collection( + array( + 'name' => 'My Collection', + 'slug' => 'mock-col-slug', + 'src' => $mock_file, + ) + ); + } + + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$editor_id ); + wp_unregister_font_collection( 'mock-col-slug' ); + } + + + /** + * @covers WP_REST_Font_Collections_Controller::register_routes + */ + public function test_register_routes() { + $routes = rest_get_server()->get_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.' ); + } + + /** + * @covers WP_REST_Font_Collections_Controller::get_items + */ + public function test_get_items() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections' ); + $response = rest_get_server()->dispatch( $request ); + $content = $response->get_data(); + $this->assertIsArray( $content ); + $this->assertEquals( 200, $response->get_status() ); + } + + /** + * @covers WP_REST_Font_Collections_Controller::get_item + */ + 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' ); + + $response_data = $response->get_data(); + $this->assertArrayHasKey( 'name', $response_data, 'Response data does not have the name key.' ); + $this->assertArrayHasKey( 'slug', $response_data, 'Response data does not have the slug key.' ); + $this->assertArrayHasKey( 'description', $response_data, 'Response data does not have the description key.' ); + $this->assertArrayHasKey( 'font_families', $response_data, 'Response data does not have the font_families key.' ); + $this->assertArrayHasKey( 'categories', $response_data, 'Response data does not have the categories key.' ); + + $this->assertIsString( $response_data['name'], 'name is not a string.' ); + $this->assertIsString( $response_data['slug'], 'slug is not a string.' ); + $this->assertIsString( $response_data['description'], 'description is not a string.' ); + + $this->assertIsArray( $response_data['font_families'], 'font_families is not an array.' ); + $this->assertIsArray( $response_data['categories'], 'categories is not an array.' ); + } + + /** + * @covers WP_REST_Font_Collections_Controller::get_item + */ + public function test_get_item_invalid_slug() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/non-existing-collection' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'font_collection_not_found', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Collections_Controller::get_item + */ + public function test_get_item_invalid_id_permission() { + $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/mock-col-slug' ); + + 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.' ); + + 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.' ); + } + + /** + * @doesNotPerformAssertions + */ + public function test_context_param() { + // Controller does not use get_context_param(). + } + + /** + * @doesNotPerformAssertions + */ + public function test_create_item() { + // Controller does not use test_create_item(). + } + + /** + * @doesNotPerformAssertions + */ + public function test_update_item() { + // Controller does not use test_update_item(). + } + + /** + * @doesNotPerformAssertions + */ + public function test_delete_item() { + // Controller does not use test_delete_item(). + } + + /** + * @doesNotPerformAssertions + */ + public function test_prepare_item() { + // Controller does not use test_prepare_item(). + } + + /** + * @doesNotPerformAssertions + */ + public function test_get_item_schema() { + // Controller does not use test_get_item_schema(). + } +} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/base.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/base.php deleted file mode 100644 index 2469d71dc79ce..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/base.php +++ /dev/null @@ -1,42 +0,0 @@ -factory->user->create( - array( - 'role' => 'administrator', - ) - ); - wp_set_current_user( $admin_id ); - } - - /** - * Tear down each test method. - */ - public function tear_down() { - parent::tear_down(); - - // Reset $collections static property of WP_Font_Library class. - $reflection = new ReflectionClass( 'WP_Font_Library' ); - $property = $reflection->getProperty( 'collections' ); - $property->setAccessible( true ); - $property->setValue( null, array() ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollection.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollection.php deleted file mode 100644 index c9d003389997b..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollection.php +++ /dev/null @@ -1,126 +0,0 @@ - 'one-collection', - 'name' => 'One Font Collection', - 'description' => 'Demo about how to a font collection to your WordPress Font Library.', - 'src' => $mock_file, - ); - wp_register_font_collection( $config_with_file ); - - $config_with_url = array( - 'slug' => 'collection-with-url', - 'name' => 'Another Font Collection', - 'description' => 'Demo about how to a font collection to your WordPress Font Library.', - 'src' => 'https://wordpress.org/fonts/mock-font-collection.json', - ); - - wp_register_font_collection( $config_with_url ); - - $config_with_non_existing_file = array( - 'slug' => 'collection-with-non-existing-file', - 'name' => 'Another Font Collection', - 'description' => 'Demo about how to a font collection to your WordPress Font Library.', - 'src' => '/home/non-existing-file.json', - ); - - wp_register_font_collection( $config_with_non_existing_file ); - - $config_with_non_existing_url = array( - 'slug' => 'collection-with-non-existing-url', - 'name' => 'Another Font Collection', - 'description' => 'Demo about how to a font collection to your WordPress Font Library.', - 'src' => 'https://non-existing-url-1234x.com.ar/fake-path/missing-file.json', - ); - - wp_register_font_collection( $config_with_non_existing_url ); - } - - public function mock_request( $preempt, $args, $url ) { - // Check if it's the URL you want to mock. - if ( 'https://wordpress.org/fonts/mock-font-collection.json' === $url ) { - - // Mock the response body. - $mock_collection_data = array( - 'fontFamilies' => 'mock', - 'categories' => 'mock', - ); - - return array( - 'body' => json_encode( $mock_collection_data ), - 'response' => array( - 'code' => 200, - ), - ); - } - - // For any other URL, return false which ensures the request is made as usual (or you can return other mock data). - return false; - } - - public function tear_down() { - // Remove the mock to not affect other tests. - remove_filter( 'pre_http_request', array( $this, 'mock_request' ) ); - - parent::tear_down(); - } - - public function test_get_font_collection_from_file() { - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/one-collection' ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - $this->assertSame( 200, $response->get_status(), 'The response status is not 200.' ); - $this->assertArrayHasKey( 'data', $data, 'The response data does not have the key with the file data.' ); - $this->assertSame( array( 'this is mock data' => true ), $data['data'], 'The response data does not have the expected file data.' ); - } - - public function test_get_font_collection_from_url() { - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/collection-with-url' ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - $this->assertSame( 200, $response->get_status(), 'The response status is not 200.' ); - $this->assertArrayHasKey( 'data', $data, 'The response data does not have the key with the file data.' ); - } - - public function test_get_non_existing_collection_should_return_404() { - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/non-existing-collection-id' ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 404, $response->get_status() ); - } - - public function test_get_non_existing_file_should_return_500() { - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/collection-with-non-existing-file' ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 500, $response->get_status() ); - } - - public function test_get_non_existing_url_should_return_500() { - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/collection-with-non-existing-url' ); - $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 500, $response->get_status() ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollections.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollections.php deleted file mode 100644 index 0a8d24e8f392b..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/getFontCollections.php +++ /dev/null @@ -1,45 +0,0 @@ -dispatch( $request ); - $this->assertSame( 200, $response->get_status() ); - $this->assertSame( array(), $response->get_data() ); - } - - public function test_get_font_collections() { - // Mock font collection data file. - $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, '{"this is mock data":true}' ); - - // Add a 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' => $mock_file, - ); - wp_register_font_collection( $config ); - - $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections' ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - $this->assertSame( 200, $response->get_status(), 'The response status is not 200.' ); - $this->assertCount( 1, $data, 'The response data is not an array with one element.' ); - $this->assertArrayHasKey( 'slug', $data[0], 'The response data does not have the key with the collection slug.' ); - $this->assertArrayHasKey( 'name', $data[0], 'The response data does not have the key with the collection name.' ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/registerRoutes.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/registerRoutes.php deleted file mode 100644 index fb100a400fb4c..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController/registerRoutes.php +++ /dev/null @@ -1,24 +0,0 @@ -get_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.' ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php new file mode 100644 index 0000000000000..1904a17228bdc --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php @@ -0,0 +1,887 @@ + '"Open Sans"', + 'fontWeight' => '400', + 'fontStyle' => 'normal', + 'src' => 'https://fonts.gstatic.com/s/open-sans/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf', + ); + + 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_face_id1 = self::create_font_face_post( + self::$font_family_id, + array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '400', + 'fontStyle' => 'normal', + 'src' => home_url( '/wp-content/fonts/open-sans-medium.ttf' ), + ) + ); + self::$font_face_id2 = self::create_font_face_post( + self::$font_family_id, + array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '900', + 'fontStyle' => 'normal', + 'src' => home_url( '/wp-content/fonts/open-sans-bold.ttf' ), + ) + ); + + self::$admin_id = $factory->user->create( + array( + 'role' => 'administrator', + ) + ); + self::$editor_id = $factory->user->create( + array( + 'role' => 'editor', + ) + ); + } + + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$editor_id ); + } + + public static function create_font_face_post( $parent_id, $settings = array() ) { + $settings = array_merge( self::$default_settings, $settings ); + $title = WP_Font_Family_Utils::get_font_face_slug( $settings ); + return self::factory()->post->create( + wp_slash( + array( + 'post_type' => 'wp_font_face', + 'post_status' => 'publish', + 'post_title' => $title, + 'post_name' => sanitize_title( $title ), + 'post_content' => wp_json_encode( $settings ), + 'post_parent' => $parent_id, + ) + ) + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::register_routes + */ + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( + '/wp/v2/font-families/(?P[\d]+)/font-faces', + $routes, + 'Font faces collection for the given font family does not exist' + ); + $this->assertCount( + 2, + $routes['/wp/v2/font-families/(?P[\d]+)/font-faces'], + 'Font faces collection for the given font family does not have exactly two elements' + ); + $this->assertArrayHasKey( + '/wp/v2/font-families/(?P[\d]+)/font-faces/(?P[\d]+)', + $routes, + 'Single font face route for the given font family does not exist' + ); + $this->assertCount( + 2, + $routes['/wp/v2/font-families/(?P[\d]+)/font-faces/(?P[\d]+)'], + 'Font faces collection for the given font family does not have exactly two elements' + ); + } + + public function test_font_faces_no_autosave_routes() { + // @core-merge: Enable this test. + $this->markTestSkipped( 'This test only works with WP 6.4 and above. Enable it once 6.5 is released.' ); + $routes = rest_get_server()->get_routes(); + $this->assertArrayNotHasKey( + '/wp/v2/font-families/(?P[\d]+)/font-faces/(?P[\d]+)/autosaves', + $routes, + 'Font faces autosaves route exists.' + ); + $this->assertArrayNotHasKey( + '/wp/v2/font-families/(?P[\d]+)/font-faces/(?P[\d]+)/autosaves/(?P[\d]+)', + $routes, + 'Font faces autosaves by id route exists.' + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_context_param + */ + 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( 'edit', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + + // Single. + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id1 ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); + $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_items + */ + public function test_get_items() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces' ); + $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->check_font_face_data( $data[0], self::$font_face_id2, $data[0]['_links'] ); + $this->assertArrayHasKey( '_links', $data[1] ); + $this->check_font_face_data( $data[1], self::$font_face_id1, $data[1]['_links'] ); + } + + /** + * @covers WP_REST_Font_Faces_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/' . self::$font_family_id . '/font-faces' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); + + wp_set_current_user( self::$editor_id ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 403 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_items + */ + public function test_get_items_missing_parent() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/font-faces' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item() { + wp_set_current_user( self::$admin_id ); + $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 ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->check_font_face_data( $data, self::$font_face_id1, $response->get_links() ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response + */ + public function test_get_item_removes_extra_settings() { + $font_face_id = self::create_font_face_post( self::$font_family_id, array( 'extra' => array() ) ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->assertArrayNotHasKey( 'extra', $data['font_face_settings'] ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response + */ + public function test_get_item_malformed_post_content_returns_empty_settings() { + $font_face_id = wp_insert_post( + array( + 'post_type' => 'wp_font_face', + 'post_parent' => self::$font_family_id, + 'post_status' => 'publish', + 'post_content' => 'invalid', + ) + ); + + $empty_settings = array( + 'fontFamily' => '', + 'src' => array(), + ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item_invalid_font_face_id() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_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_id . '/font-faces/' . self::$font_face_id1 ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); + + wp_set_current_user( self::$editor_id ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 403 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item_missing_parent() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/font-faces/' . self::$font_face_id1 ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item_valid_parent_id() { + wp_set_current_user( self::$admin_id ); + $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 ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( self::$font_family_id, $data['parent'], 'The returned parent id should match the font family id.' ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item_invalid_parent_id() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$other_font_family_id . '/font-faces/' . self::$font_face_id1 ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_font_face_parent_id_mismatch', $response, 404 ); + + $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.' ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item() { + wp_set_current_user( self::$admin_id ); + $files = $this->setup_font_file_upload( array( 'woff2' ) ); + + $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( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '200', + 'fontStyle' => 'normal', + 'src' => array_keys( $files )[0], + ) + ) + ); + $request->set_file_params( $files ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $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', + ) + ); + + $this->assertSame( self::$font_family_id, $data['parent'], 'The returned parent id should match the font family id.' ); + + wp_delete_post( $data['id'], true ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_with_multiple_font_files() { + wp_set_current_user( self::$admin_id ); + $files = $this->setup_font_file_upload( array( 'ttf', 'otf', 'woff', 'woff2' ) ); + + $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( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '200', + 'fontStyle' => 'normal', + 'src' => array_keys( $files ), + ) + ) + ); + $request->set_file_params( $files ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_invalid_file_type() { + $image_file = DIR_TESTDATA . '/images/canola.jpg'; + $image_path = wp_tempnam( 'canola.jpg' ); + copy( $image_file, $image_path ); + + $files = array( + 'file-0' => array( + 'name' => 'canola.jpg', + 'full_path' => 'canola.jpg', + 'type' => 'font/woff2', + 'tmp_name' => $image_path, + 'error' => 0, + 'size' => filesize( $image_path ), + ), + ); + + 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( + 'fontWeight' => '200', + 'src' => array_keys( $files )[0], + ) + ) + ) + ); + $request->set_file_params( $files ); + + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_font_upload_invalid_file_type', $response, 400 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_with_url_src() { + 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( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '200', + 'fontStyle' => 'normal', + 'src' => 'https://fonts.gstatic.com/s/open-sans/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf', + ) + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $this->check_font_face_data( $data, $data['id'], $response->get_links() ); + + wp_delete_post( $data['id'], true ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_with_all_properties() { + wp_set_current_user( self::$admin_id ); + + $properties = array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '300 500', + 'fontStyle' => 'oblique 30deg 50deg', + 'fontDisplay' => 'swap', + '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://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg', + 'src' => 'https://fonts.gstatic.com/s/open-sans/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf', + ); + + $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( $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_missing_parent() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/font-faces' ); + $request->set_param( + 'font_face_settings', + wp_json_encode( array_merge( self::$default_settings, array( 'fontWeight' => '100' ) ) ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_with_duplicate_properties() { + $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 ); + + 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 ); + + $this->assertErrorResponse( 'rest_duplicate_font_face', $response, 400 ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_request + */ + public function test_create_item_default_theme_json_version() { + 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( + array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '200', + 'src' => 'https://fonts.gstatic.com/s/open-sans/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf', + ) + ) + ); + + $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.' ); + + wp_delete_post( $data['id'], true ); + } + + /** + * @dataProvider data_create_item_invalid_theme_json_version + * + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_invalid_theme_json_version( $theme_json_version ) { + 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', $theme_json_version ); + $request->set_param( 'font_face_settings', '' ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + public function data_create_item_invalid_theme_json_version() { + return array( + array( 1 ), + array( 3 ), + ); + } + + /** + * @dataProvider data_create_item_invalid_settings + * + * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings + */ + public function test_create_item_invalid_settings( $settings ) { + 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( $settings ) ); + + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + public function data_create_item_invalid_settings() { + return array( + 'Missing fontFamily' => array( + 'settings' => array_diff_key( self::$default_settings, array( 'fontFamily' => '' ) ), + ), + 'Empty fontFamily' => array( + 'settings' => array_merge( self::$default_settings, array( 'fontFamily' => '' ) ), + ), + 'Wrong fontFamily type' => array( + 'settings' => array_merge( self::$default_settings, array( 'fontFamily' => 1234 ) ), + ), + 'Invalid fontDisplay' => array( + 'settings' => array_merge( self::$default_settings, array( 'fontDisplay' => 'invalid' ) ), + ), + 'Missing src' => array( + 'settings' => array_diff_key( self::$default_settings, array( 'src' => '' ) ), + ), + 'Empty src string' => array( + 'settings' => array_merge( self::$default_settings, array( 'src' => '' ) ), + ), + 'Empty src array' => array( + 'settings' => array_merge( self::$default_settings, array( 'src' => array() ) ), + ), + 'Empty src array values' => array( + 'settings' => array_merge( self::$default_settings, array( '', '' ) ), + ), + 'Wrong src type' => array( + 'settings' => array_merge( self::$default_settings, array( 'src' => 1234 ) ), + ), + 'Wrong src array types' => array( + 'settings' => array_merge( self::$default_settings, array( 'src' => array( 1234, 5678 ) ) ), + ), + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings + */ + public function test_create_item_invalid_settings_json() { + 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', 'invalid' ); + + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings + */ + public function test_create_item_invalid_file_src() { + $files = $this->setup_font_file_upload( array( 'woff2' ) ); + + 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' => 'invalid' ) ) + ) + ); + $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].'; + $message = $response->as_error()->get_all_error_data()[0]['params']['font_face_settings']; + $this->assertSame( $expected_message, $message ); + } + + /** + * @dataProvider data_create_item_santize_font_family + * + * @covers WP_REST_Font_Face_Controller::sanitize_font_face_settings + */ + public function test_create_item_santize_font_family( $font_family_setting, $expected ) { + $settings = array_merge( self::$default_settings, array( 'fontFamily' => $font_family_setting ) ); + + 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(); + + $this->assertSame( 201, $response->get_status() ); + $this->assertSame( $expected, $data['font_face_settings']['fontFamily'] ); + } + + public function data_create_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' ), + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + // public function test_create_item_no_permission() {} + + 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 ); + $this->assertErrorResponse( 'rest_no_route', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item() { + wp_set_current_user( self::$admin_id ); + $font_face_id = self::create_font_face_post( self::$font_family_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . $font_face_id ); + $request->set_param( 'force', true ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertSame( 200, $response->get_status() ); + $this->assertNull( get_post( $font_face_id ) ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_no_trash() { + wp_set_current_user( self::$admin_id ); + $font_face_id = self::create_font_face_post( self::$font_family_id ); + + // 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 ); + + $request->set_param( 'force', 'false' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); + + // Ensure the post still exists. + $post = get_post( $font_face_id ); + $this->assertNotEmpty( $post ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_invalid_font_face_id() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ); + $request->set_param( 'force', true ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete + */ + public function test_delete_item_missing_parent() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/font-faces/' . self::$font_face_id1 ); + $request->set_param( 'force', true ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_delete_item_invalid_parent_id() { + wp_set_current_user( self::$admin_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 ); + + $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.' ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_no_permissions() { + $font_face_id = $this->create_font_face_post( self::$font_family_id ); + + 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 ); + + 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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response + */ + public function test_prepare_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id2 ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->check_font_face_data( $data, self::$font_face_id2, $response->get_links() ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item_schema + */ + public function test_get_item_schema() { + $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->assertSame( 200, $response->get_status() ); + $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 ); + } + + protected function check_font_face_data( $data, $post_id, $links ) { + $post = get_post( $post_id ); + + $this->assertArrayHasKey( 'id', $data ); + $this->assertSame( $post->ID, $data['id'] ); + + $this->assertArrayHasKey( 'parent', $data ); + $this->assertSame( $post->post_parent, $data['parent'] ); + + $this->assertArrayHasKey( 'theme_json_version', $data ); + $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'] ); + + $this->assertArrayHasKey( 'font_face_settings', $data ); + $this->assertSame( $post->post_content, wp_json_encode( $data['font_face_settings'] ) ); + + $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'] ); + } + + protected function check_file_meta( $font_face_id, $srcs ) { + $file_meta = get_post_meta( $font_face_id, '_wp_font_face_file' ); + + foreach ( $srcs as $src ) { + $file_name = basename( $src ); + $this->assertContains( $file_name, $file_meta, 'The uploaded font file path should be saved in the post meta.' ); + } + } + + protected function setup_font_file_upload( $formats ) { + $files = array(); + foreach ( $formats as $format ) { + // @core-merge Use `DIR_TESTDATA` instead of `GUTENBERG_DIR_TESTDATA`. + $font_file = GUTENBERG_DIR_TESTDATA . 'fonts/OpenSans-Regular.' . $format; + $font_path = wp_tempnam( 'OpenSans-Regular.' . $format ); + copy( $font_file, $font_path ); + + $files[ 'file-' . count( $files ) ] = array( + 'name' => 'OpenSans-Regular.' . $format, + 'full_path' => 'OpenSans-Regular.' . $format, + 'type' => 'font/' . $format, + 'tmp_name' => $font_path, + 'error' => 0, + 'size' => filesize( $font_path ), + ); + } + + return $files; + } +} diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php new file mode 100644 index 0000000000000..6e6a822a9881f --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php @@ -0,0 +1,866 @@ + 'Open Sans', + 'slug' => 'open-sans', + 'fontFamily' => '"Open Sans", sans-serif', + 'preview' => 'https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg', + ); + + public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { + self::$admin_id = $factory->user->create( + array( + 'role' => 'administrator', + ) + ); + self::$editor_id = $factory->user->create( + array( + 'role' => 'editor', + ) + ); + + self::$font_family_id1 = self::create_font_family_post( + array( + 'name' => 'Open Sans', + 'slug' => 'open-sans', + 'fontFamily' => '"Open Sans", sans-serif', + 'preview' => 'https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg', + ) + ); + self::$font_family_id2 = self::create_font_family_post( + array( + 'name' => 'Helvetica', + 'slug' => 'helvetica', + 'fontFamily' => 'Helvetica, Arial, sans-serif', + ) + ); + self::$font_face_id1 = WP_REST_Font_Faces_Controller_Test::create_font_face_post( + self::$font_family_id1, + array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '400', + 'fontStyle' => 'normal', + '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_family_id1, + array( + 'fontFamily' => '"Open Sans"', + 'fontWeight' => '900', + 'fontStyle' => 'normal', + 'src' => home_url( '/wp-content/fonts/open-sans-bold.ttf' ), + ) + ); + } + + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$editor_id ); + } + + public static function create_font_family_post( $settings = array() ) { + $settings = array_merge( self::$default_settings, $settings ); + return self::factory()->post->create( + wp_slash( + array( + 'post_type' => 'wp_font_family', + 'post_status' => 'publish', + 'post_title' => $settings['name'], + 'post_name' => $settings['slug'], + 'post_content' => wp_json_encode( + array( + 'fontFamily' => $settings['fontFamily'], + 'preview' => $settings['preview'], + ) + ), + ) + ) + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::register_routes + */ + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( + '/wp/v2/font-families', + $routes, + 'Font faces collection for the given font family does not exist' + ); + $this->assertCount( + 2, + $routes['/wp/v2/font-families'], + 'Font faces collection for the given font family does not have exactly two elements' + ); + $this->assertArrayHasKey( + '/wp/v2/font-families/(?P[\d]+)', + $routes, + 'Single font face route for the given font family does not exist' + ); + $this->assertCount( + 3, + $routes['/wp/v2/font-families/(?P[\d]+)'], + 'Font faces collection for the given font family does not have exactly two elements' + ); + } + + public function test_font_families_no_autosave_routes() { + // @core-merge: Enable this test. + $this->markTestSkipped( 'This test only works with WP 6.4 and above. Enable it once 6.5 is released.' ); + $routes = rest_get_server()->get_routes(); + $this->assertArrayNotHasKey( + '/wp/v2/font-families/(?P[\d]+)/autosaves', + $routes, + 'Font families autosaves route exists.' + ); + $this->assertArrayNotHasKey( + '/wp/v2/font-families/(?P[\d]+)/autosaves/(?P[\d]+)', + $routes, + 'Font families autosaves by id route exists.' + ); + } + + /** + * @covers WP_REST_Font_Families_Controller::get_context_param + */ + 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( 'edit', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + + // Single. + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id1 ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); + $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + } + + + /** + * @covers WP_REST_Font_Faces_Controller::get_items + */ + public function test_get_items() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families' ); + $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->check_font_family_data( $data[0], self::$font_family_id2, $data[0]['_links'] ); + $this->assertArrayHasKey( '_links', $data[1] ); + $this->check_font_family_data( $data[1], self::$font_family_id1, $data[1]['_links'] ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_items + */ + public function test_get_items_by_slug() { + $font_family = get_post( self::$font_family_id2 ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families' ); + $request->set_param( 'slug', $font_family->post_name ); + $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'] ); + } + + /** + * @covers WP_REST_Font_Faces_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 ); + + 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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id1 ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->check_font_family_data( $data, self::$font_family_id1, $response->get_links() ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response + */ + public function test_get_item_embedded_font_faces() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id1 ); + $request->set_param( '_embed', true ); + $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'] ); + + foreach ( $data['_embedded']['font_faces'] as $font_face ) { + $this->assertArrayHasKey( 'id', $font_face ); + + $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 ); + } + } + + /** + * @covers WP_REST_Font_Families_Controller::get_item + */ + public function test_get_item_removes_extra_settings() { + $font_family_id = self::create_font_family_post( array( 'fontFace' => array() ) ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . $font_family_id ); + $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 ); + } + + /** + * @covers WP_REST_Font_Families_Controller::prepare_item_for_response + */ + public function test_get_item_malformed_post_content_returns_empty_settings() { + $font_family_id = wp_insert_post( + array( + 'post_type' => 'wp_font_family', + 'post_status' => 'publish', + 'post_content' => 'invalid', + ) + ); + + $empty_settings = array( + 'name' => '', + // Slug will default to the post id. + 'slug' => (string) $font_family_id, + 'fontFamily' => '', + 'preview' => '', + ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . $font_family_id ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::get_item + */ + public function test_get_item_invalid_font_family_id() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_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 ); + + wp_set_current_user( self::$editor_id ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 403 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item() { + $settings = array_merge( self::$default_settings, array( 'slug' => 'open-sans-2' ) ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', 2 ); + $request->set_param( 'font_family_settings', wp_json_encode( $settings ) ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_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' ) ); + 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(); + + $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.' ); + + wp_delete_post( $data['id'], true ); + } + + /** + * @dataProvider data_create_item_invalid_theme_json_version + * + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_invalid_theme_json_version( $theme_json_version ) { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', $theme_json_version ); + $request->set_param( 'font_family_settings', wp_json_encode( self::$default_settings ) ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + public function data_create_item_invalid_theme_json_version() { + return array( + array( 1 ), + array( 3 ), + ); + } + + /** + * @dataProvider data_create_item_with_default_preview + * + * @covers WP_REST_Font_Faces_Controller::sanitize_font_family_settings + */ + public function test_create_item_with_default_preview( $settings ) { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', 2 ); + $request->set_param( 'font_family_settings', wp_json_encode( $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'] ); + + wp_delete_post( $data['id'], true ); + } + + public function data_create_item_with_default_preview() { + $default_settings = array( + 'name' => 'Open Sans', + 'slug' => 'open-sans-2', + 'fontFamily' => '"Open Sans", sans-serif', + ); + return array( + 'No preview param' => array( + 'settings' => $default_settings, + ), + 'Empty preview' => array( + 'settings' => array_merge( $default_settings, array( 'preview' => '' ) ), + ), + ); + } + + /** + * @dataProvider data_create_item_invalid_settings + * + * @covers WP_REST_Font_Faces_Controller::validate_create_font_face_settings + */ + public function test_create_item_invalid_settings( $settings ) { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', 2 ); + $request->set_param( 'font_family_settings', wp_json_encode( $settings ) ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + public function data_create_item_invalid_settings() { + return array( + 'Missing name' => array( + 'settings' => array_diff_key( self::$default_settings, array( 'name' => '' ) ), + ), + 'Empty name' => array( + 'settings' => array_merge( self::$default_settings, array( 'name' => '' ) ), + ), + 'Wrong name type' => array( + 'settings' => array_merge( self::$default_settings, array( 'name' => 1234 ) ), + ), + 'Missing slug' => array( + 'settings' => array_diff_key( self::$default_settings, array( 'slug' => '' ) ), + ), + 'Empty slug' => array( + 'settings' => array_merge( self::$default_settings, array( 'slug' => '' ) ), + ), + 'Wrong slug type' => array( + 'settings' => array_merge( self::$default_settings, array( 'slug' => 1234 ) ), + ), + 'Missing fontFamily' => array( + 'settings' => array_diff_key( self::$default_settings, array( 'fontFamily' => '' ) ), + ), + 'Empty fontFamily' => array( + 'settings' => array_merge( self::$default_settings, array( 'fontFamily' => '' ) ), + ), + 'Wrong fontFamily type' => array( + 'settings' => array_merge( self::$default_settings, array( 'fontFamily' => 1234 ) ), + ), + ); + } + + /** + * @covers WP_REST_Font_Family_Controller::validate_font_family_settings + */ + public function test_create_item_invalid_settings_json() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', 2 ); + $request->set_param( 'font_family_settings', 'invalid' ); + + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + $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 ); + } + + /** + * @covers WP_REST_Font_Family_Controller::create_item + */ + public function test_create_item_with_duplicate_slug() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( 'theme_json_version', 2 ); + $request->set_param( 'font_family_settings', wp_json_encode( array_merge( self::$default_settings, array( 'slug' => 'helvetica' ) ) ) ); + + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_duplicate_font_family', $response, 400 ); + $expected_message = 'A font family with slug "helvetica" already exists.'; + $message = $response->as_error()->get_error_messages()[0]; + $this->assertSame( $expected_message, $message ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::create_item + */ + public function test_create_item_no_permission() { + $settings = array_merge( self::$default_settings, array( 'slug' => 'open-sans-2' ) ); + wp_set_current_user( 0 ); + $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 ); + + wp_set_current_user( self::$editor_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $request->set_param( + 'font_family_settings', + wp_json_encode( + array( + 'name' => 'Open Sans', + 'slug' => 'open-sans', + 'fontFamily' => '"Open Sans", sans-serif', + 'preview' => 'https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg', + ) + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_create', $response, 403 ); + } + + /** + * @covers WP_REST_Font_Families_Controller::update_item + */ + public function test_update_item() { + wp_set_current_user( self::$admin_id ); + + $settings = array( + 'name' => 'Open Sans', + 'fontFamily' => '"Open Sans, "Noto Sans", sans-serif', + 'preview' => 'https://s.w.org/images/fonts/16.9/previews/open-sans/open-sans-400-normal.svg', + ); + + $font_family_id = self::create_font_family_post( array( 'slug' => 'open-sans-2' ) ); + $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->check_font_family_data( $data, $font_family_id, $response->get_links() ); + + $expected_settings = array( + 'name' => $settings['name'], + 'slug' => 'open-sans-2', + 'fontFamily' => $settings['fontFamily'], + 'preview' => $settings['preview'], + ); + $this->assertSame( $expected_settings, $data['font_family_settings'] ); + + wp_delete_post( $font_family_id, true ); + } + + /** + * @dataProvider data_update_item_individual_settings + * @covers WP_REST_Font_Families_Controller::update_item + */ + public function test_update_item_individual_settings( $settings ) { + wp_set_current_user( self::$admin_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( $settings ) ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $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 ); + } + + public function data_update_item_individual_settings() { + return array( + array( array( 'name' => 'Opened Sans' ) ), + array( array( 'fontFamily' => '"Opened Sans", sans-serif' ) ), + array( array( 'preview' => 'https://s.w.org/images/fonts/16.7/previews/opened-sans/opened-sans-400-normal.svg' ) ), + // Empty preview is allowed. + array( array( 'preview' => '' ) ), + ); + } + + /** + * @dataProvider data_update_item_santize_font_family + * + * @covers WP_REST_Font_Families_Controller::sanitize_font_face_settings + */ + public function test_update_item_santize_font_family( $font_family_setting, $expected ) { + wp_set_current_user( self::$admin_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 ) ) ); + $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' ), + ); + } + + /** + * @dataProvider data_update_item_invalid_settings + * + * @covers WP_REST_Font_Faces_Controller::update_item + */ + public function test_update_item_empty_settings( $settings ) { + wp_set_current_user( self::$admin_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_invalid_param', $response, 400 ); + } + + public function data_update_item_invalid_settings() { + return array( + 'Empty name' => array( + array( 'name' => '' ), + ), + 'Wrong name type' => array( + array( 'name' => 1234 ), + ), + 'Empty fontFamily' => array( + array( 'fontFamily' => '' ), + ), + 'Wrong fontFamily type' => array( + array( 'fontFamily' => 1234 ), + ), + ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::update_item + */ + public function test_update_item_update_slug_not_allowed() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/font-families/' . self::$font_family_id1 ); + $request->set_param( + 'font_family_settings', + wp_json_encode( array( 'slug' => 'new-slug' ) ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + $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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::update_item + */ + public function test_update_item_invalid_font_family_id() { + $settings = array_diff_key( self::$default_settings, array( 'slug' => '' ) ); + + wp_set_current_user( self::$admin_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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::update_item + */ + public function test_update_item_no_permission() { + $settings = array_diff_key( self::$default_settings, array( 'slug' => '' ) ); + + wp_set_current_user( 0 ); + $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 ); + + 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 ); + } + + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item() { + wp_set_current_user( self::$admin_id ); + $font_family_id = self::create_font_family_post(); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . $font_family_id ); + $request['force'] = true; + $response = rest_get_server()->dispatch( $request ); + + $this->assertSame( 200, $response->get_status() ); + $this->assertNull( get_post( $font_family_id ) ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_no_trash() { + wp_set_current_user( self::$admin_id ); + $font_family_id = self::create_font_family_post(); + + // 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 ); + + $request->set_param( 'force', 'false' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); + + // Ensure the post still exists. + $post = get_post( $font_family_id ); + $this->assertNotEmpty( $post ); + + wp_delete_post( $font_family_id, true ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_invalid_font_family_id() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::delete_item + */ + public function test_delete_item_no_permissions() { + $font_family_id = self::create_font_family_post(); + + 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 ); + + 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 ); + } + + /** + * @covers WP_REST_Font_Faces_Controller::prepare_item_for_response + */ + public function test_prepare_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/font-families/' . self::$font_family_id2 ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $this->check_font_family_data( $data, self::$font_family_id2, $response->get_links() ); + } + + /** + * @covers WP_REST_Font_Faces_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() ); + $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 ); + } + + protected function check_font_family_data( $data, $post_id, $links ) { + $post = get_post( $post_id ); + + $this->assertArrayHasKey( 'id', $data ); + $this->assertSame( $post->ID, $data['id'] ); + + $this->assertArrayHasKey( 'theme_json_version', $data ); + $this->assertSame( WP_Theme_JSON::LATEST_SCHEMA, $data['theme_json_version'] ); + + $font_face_ids = get_children( + array( + 'fields' => 'ids', + 'post_parent' => $post_id, + 'post_type' => 'wp_font_face', + 'order' => 'ASC', + 'orderby' => 'ID', + ) + ); + $this->assertArrayHasKey( 'font_faces', $data ); + + foreach ( $font_face_ids as $font_face_id ) { + $this->assertContains( $font_face_id, $data['font_faces'] ); + } + + $this->assertArrayHasKey( 'font_family_settings', $data ); + $settings = $data['font_family_settings']; + $expected_settings = array( + 'name' => $post->post_title, + 'slug' => $post->post_name, + 'fontFamily' => $settings['fontFamily'], + 'preview' => $settings['preview'], + ); + $this->assertSame( $expected_settings, $settings ); + + $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'] ); + + if ( ! $font_face_ids ) { + return; + } + + // 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'] ); + + $embeddable = isset( $link['attributes']['embeddable'] ) + ? $link['attributes']['embeddable'] + : $link['embeddable']; + $this->assertTrue( $embeddable ); + } + } +} diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php deleted file mode 100644 index e2d190cd76af1..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php +++ /dev/null @@ -1,43 +0,0 @@ -factory->user->create( - array( - 'role' => 'administrator', - ) - ); - wp_set_current_user( $admin_id ); - } - - /** - * Tear down each test method. - */ - public function tear_down() { - parent::tear_down(); - - // Clean up the /fonts directory. - foreach ( $this->files_in_dir( static::$fonts_dir ) as $file ) { - @unlink( $file ); - } - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php deleted file mode 100644 index 98c1cb6e13fe5..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php +++ /dev/null @@ -1,334 +0,0 @@ -set_param( 'font_family_settings', $font_family_json ); - $install_request->set_file_params( $files ); - $response = rest_get_server()->dispatch( $install_request ); - $data = $response->get_data(); - $this->assertSame( 200, $response->get_status(), 'The response status is not 200.' ); - $this->assertCount( count( $expected_response['successes'] ), $data['successes'], 'Not all the font families were installed correctly.' ); - - // Checks that the font families were installed correctly. - for ( $family_index = 0; $family_index < count( $data['successes'] ); $family_index++ ) { - $installed_font = $data['successes'][ $family_index ]; - $expected_font = $expected_response['successes'][ $family_index ]; - - if ( isset( $installed_font['fontFace'] ) || isset( $expected_font['fontFace'] ) ) { - for ( $face_index = 0; $face_index < count( $installed_font['fontFace'] ); $face_index++ ) { - // Checks that the font asset were created correctly. - if ( isset( $installed_font['fontFace'][ $face_index ]['src'] ) ) { - $this->assertStringEndsWith( $expected_font['fontFace'][ $face_index ]['src'], $installed_font['fontFace'][ $face_index ]['src'], 'The src of the fonts were not updated as expected.' ); - } - // Removes the src from the response to compare the rest of the data. - unset( $installed_font['fontFace'][ $face_index ]['src'] ); - unset( $expected_font['fontFace'][ $face_index ]['src'] ); - unset( $installed_font['fontFace'][ $face_index ]['uploadedFile'] ); - } - } - - // Compares if the rest of the data is the same. - $this->assertEquals( $expected_font, $installed_font, 'The endpoint answer is not as expected.' ); - } - } - - /** - * Data provider for test_install_fonts - */ - public function data_install_fonts() { - - $temp_file_path1 = wp_tempnam( 'Piazzola1-' ); - copy( __DIR__ . '/../../../data/fonts/Merriweather.ttf', $temp_file_path1 ); - - $temp_file_path2 = wp_tempnam( 'Monteserrat-' ); - copy( __DIR__ . '/../../../data/fonts/Merriweather.ttf', $temp_file_path2 ); - - return array( - - 'google_fonts_to_download' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - 'files' => array(), - 'expected_response' => array( - 'successes' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => '/wp-content/fonts/piazzolla_normal_400.ttf', - ), - ), - ), - ), - 'errors' => array(), - ), - ), - - 'google_fonts_to_use_as_is' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - 'files' => array(), - 'expected_response' => array( - 'successes' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - ), - 'errors' => array(), - ), - ), - - 'fonts_without_font_faces' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Arial', - 'slug' => 'arial', - 'name' => 'Arial', - ), - 'files' => array(), - 'expected_response' => array( - 'successes' => array( - array( - 'fontFamily' => 'Arial', - 'slug' => 'arial', - 'name' => 'Arial', - ), - ), - 'errors' => array(), - ), - ), - - 'fonts_with_local_fonts_assets' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files0', - ), - ), - ), - 'files' => array( - 'files0' => array( - 'name' => 'piazzola1.ttf', - 'type' => 'font/ttf', - 'tmp_name' => $temp_file_path1, - 'error' => 0, - 'size' => 123, - ), - 'files1' => array( - 'name' => 'montserrat1.ttf', - 'type' => 'font/ttf', - 'tmp_name' => $temp_file_path2, - 'error' => 0, - 'size' => 123, - ), - ), - 'expected_response' => array( - 'successes' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => '/wp-content/fonts/piazzolla_normal_400.ttf', - ), - ), - ), - ), - 'errors' => array(), - ), - ), - ); - } - - /** - * Tests failure when fonfaces has improper inputs - * - * @dataProvider data_install_with_improper_inputs - * - * @param array $font_families Font families to install in theme.json format. - * @param array $files Font files to install. - */ - public function test_install_with_improper_inputs( $font_families, $files = array() ) { - $install_request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); - $font_families_json = json_encode( $font_families ); - $install_request->set_param( 'font_families', $font_families_json ); - $install_request->set_file_params( $files ); - - $response = rest_get_server()->dispatch( $install_request ); - $this->assertSame( 400, $response->get_status() ); - } - - /** - * Data provider for test_install_with_improper_inputs - */ - public function data_install_with_improper_inputs() { - $temp_file_path1 = wp_tempnam( 'Piazzola1-' ); - file_put_contents( $temp_file_path1, 'Mocking file content' ); - - return array( - 'not a font families array' => array( - 'font_family_settings' => 'This is not an array', - ), - - 'empty array' => array( - 'font_family_settings' => array(), - ), - - 'without slug' => array( - 'font_family_settings' => array( - array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - ), - ), - ), - - 'with improper font face property' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => 'This is not an array', - ), - ), - - 'with empty font face property' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array(), - ), - ), - - 'fontface referencing uploaded file without uploaded files' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files0', - ), - ), - ), - 'files' => array(), - ), - - 'fontface referencing uploaded file without uploaded files' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files666', - ), - ), - ), - 'files' => array( - 'files0' => array( - 'name' => 'piazzola1.ttf', - 'type' => 'font/ttf', - 'tmp_name' => $temp_file_path1, - 'error' => 0, - 'size' => 123, - ), - ), - ), - - 'fontface with incompatible properties (downloadFromUrl and uploadedFile together)' => array( - 'font_family_settings' => array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'uploadedFile' => 'files0', - ), - ), - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/uninstallFonts.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/uninstallFonts.php deleted file mode 100644 index 241f26284fe5d..0000000000000 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/uninstallFonts.php +++ /dev/null @@ -1,96 +0,0 @@ - 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - ), - ), - ), - ); - - $install_request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); - $font_families_json = json_encode( $mock_families ); - $install_request->set_param( 'font_families', $font_families_json ); - rest_get_server()->dispatch( $install_request ); - } - - public function test_uninstall() { - $font_families_to_uninstall = array( - array( - 'slug' => 'piazzolla', - ), - array( - 'slug' => 'montserrat', - ), - ); - - $uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families' ); - $uninstall_request->set_param( 'font_families', $font_families_to_uninstall ); - $response = rest_get_server()->dispatch( $uninstall_request ); - $this->assertSame( 200, $response->get_status(), 'The response status is not 200.' ); - } - - - public function test_uninstall_non_existing_fonts() { - $uninstall_request = new WP_REST_Request( 'DELETE', '/wp/v2/font-families' ); - - $non_existing_font_data = array( - array( - 'slug' => 'non-existing-font', - 'name' => 'Non existing font', - ), - array( - 'slug' => 'another-not-installed-font', - 'name' => 'Another not installed font', - ), - ); - - $uninstall_request->set_param( 'font_families', $non_existing_font_data ); - $response = rest_get_server()->dispatch( $uninstall_request ); - $data = $response->get_data(); - $this->assertCount( 2, $data['errors'], 'The response should have 2 errors, one for each font family uninstall failure.' ); - } -} diff --git a/schemas/json/block.json b/schemas/json/block.json index fd69ea1badb33..9cc7a83c2304d 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -543,6 +543,11 @@ } } }, + "shadow": { + "type": "boolean", + "description": "Allow blocks to define a box shadow.", + "default": false + }, "typography": { "type": "object", "description": "This value signals that a block supports some of the CSS style properties related to typography. When it does, the block editor will show UI controls for the user to set their values if the theme declares support.\n\nWhen the block declares support for a specific typography property, its attributes definition is extended to include the style attribute.", diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js index 9620b45fed9da..d1de2df1da7ff 100644 --- a/test/e2e/specs/editor/blocks/links.spec.js +++ b/test/e2e/specs/editor/blocks/links.spec.js @@ -445,6 +445,9 @@ test.describe( 'Links', () => { await pageUtils.pressKeys( 'Enter' ); const linkPopover = LinkUtils.getLinkPopover(); + await expect( linkPopover ).toBeVisible(); + // Close the link control to return the caret to the canvas + await pageUtils.pressKeys( 'Escape' ); // Deselect the link text by moving the caret to the end of the line // and the link popover should not be displayed. @@ -612,6 +615,13 @@ test.describe( 'Links', () => { await page.keyboard.type( 'w.org' ); await page.keyboard.press( 'Enter' ); + // Close the link control to return the caret to the canvas + const linkPopover = LinkUtils.getLinkPopover(); + await pageUtils.pressKeys( 'Escape' ); + // Deselect the link text by moving the caret to the end of the line + // and the link popover should not be displayed. + await pageUtils.pressKeys( 'End' ); + await expect( linkPopover ).toBeHidden(); await expect.poll( editor.getBlocks ).toMatchObject( [ { @@ -634,17 +644,12 @@ test.describe( 'Links', () => { await page.getByPlaceholder( 'Search or type url' ).fill( '' ); await page.keyboard.type( 'wordpress.org' ); - const linkPopover = LinkUtils.getLinkPopover(); - // Update the link. await linkPopover.getByRole( 'button', { name: 'Save' } ).click(); - // Navigate back to the popover. - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowLeft' ); - - // Navigate back to inputs to verify appears as changed. - await pageUtils.pressKeys( 'primary+k' ); + // Navigate back to the link editing state inputs to verify appears as changed. + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Enter' ); expect( await page @@ -683,20 +688,21 @@ test.describe( 'Links', () => { await pageUtils.pressKeys( 'primary+k' ); await page.keyboard.type( 'w.org' ); await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Escape' ); // Move to edge of text "Gutenberg". await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // If you just use Alt here it won't work on windows. await pageUtils.pressKeys( 'ArrowLeft' ); - await pageUtils.pressKeys( 'ArrowLeft' ); // Select "Gutenberg". - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); + await pageUtils.pressKeys( 'shiftAlt+ArrowRight' ); // Create a link. await pageUtils.pressKeys( 'primary+k' ); await page.keyboard.type( 'https://wordpress.org/plugins/gutenberg/' ); await page.keyboard.press( 'Enter' ); - + await page.keyboard.press( 'Escape' ); + await pageUtils.pressKeys( 'End' ); // Move back into the link. await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); await pageUtils.pressKeys( 'primary+k' ); @@ -1054,6 +1060,9 @@ test.describe( 'Links', () => { // Update the link. await pageUtils.pressKeys( 'Enter' ); + await pageUtils.pressKeys( 'Escape' ); + await pageUtils.pressKeys( 'ArrowRight' ); + // Reactivate the link. await pageUtils.pressKeys( 'ArrowLeft' ); await pageUtils.pressKeys( 'ArrowLeft' ); @@ -1117,11 +1126,10 @@ test.describe( 'Links', () => { // Update the link. await pageUtils.pressKeys( 'Enter' ); + await pageUtils.pressKeys( 'Escape' ); // Move cursor next to the **end** of `linkTextOne` - await pageUtils.pressKeys( 'ArrowLeft', { - times: linkedTextTwo.length, - } ); + await pageUtils.pressKeys( 'ArrowLeft' ); // Select `linkTextOne` await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); @@ -1134,6 +1142,8 @@ test.describe( 'Links', () => { // Update the link. await pageUtils.pressKeys( 'Enter' ); + await pageUtils.pressKeys( 'Escape' ); + await pageUtils.pressKeys( 'ArrowRight' ); // Move cursor within `linkTextOne` await pageUtils.pressKeys( 'ArrowLeft', { @@ -1146,8 +1156,8 @@ test.describe( 'Links', () => { await expect( linkPopover ).toBeVisible(); // Expand selection so that it overlaps with `linkTextTwo` - await pageUtils.pressKeys( 'ArrowRight', { - times: 3, + await pageUtils.pressKeys( 'Shift+ArrowRight', { + times: 6, } ); // Link UI should be inactive. @@ -1254,6 +1264,8 @@ class LinkUtils { // Click on the Submit button. await this.pageUtils.pressKeys( 'Enter' ); + await this.pageUtils.pressKeys( 'Escape' ); + await this.pageUtils.pressKeys( 'End' ); // Reselect the link. await this.pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); diff --git a/test/e2e/specs/editor/blocks/paragraph.spec.js b/test/e2e/specs/editor/blocks/paragraph.spec.js index 142b01be282d4..dd3cd9196efb2 100644 --- a/test/e2e/specs/editor/blocks/paragraph.spec.js +++ b/test/e2e/specs/editor/blocks/paragraph.spec.js @@ -252,8 +252,9 @@ test.describe( 'Paragraph', () => { { // Dragging on the bottom half of the heading block. + // Make sure to target the bottom dropzone by dragging > 30px inside the block. await draggingUtils.dragOver( - headingBox.x, + headingBox.x + 32, headingBox.y + headingBox.height - 1 ); await expect( draggingUtils.dropZone ).toBeHidden(); @@ -268,6 +269,26 @@ test.describe( 'Paragraph', () => { ) .toBeGreaterThan( headingBox.y + headingBox.height ); } + + { + // Dragging on the right edge of the heading block. + // Targets the right hand dropzone. + await draggingUtils.dragOver( + headingBox.x + headingBox.width - 1, + headingBox.y + headingBox.height - 1 + ); + await expect( draggingUtils.dropZone ).toBeHidden(); + await expect( + draggingUtils.insertionIndicator + ).toBeVisible(); + await expect + .poll( () => + draggingUtils.insertionIndicator + .boundingBox() + .then( ( { x, width } ) => x + width ) + ) + .toBe( headingBox.x + headingBox.width ); + } } ); test( 'Only the second block is an empty paragraph block', async ( { @@ -299,7 +320,7 @@ test.describe( 'Paragraph', () => { { // Dragging on the top half of the heading block. await draggingUtils.dragOver( - headingBox.x, + headingBox.x + 32, headingBox.y + 1 ); await expect( draggingUtils.dropZone ).toBeHidden(); @@ -318,7 +339,7 @@ test.describe( 'Paragraph', () => { { // Dragging on the bottom half of the heading block. await draggingUtils.dragOver( - headingBox.x, + headingBox.x + 32, headingBox.y + headingBox.height - 1 ); await expect( draggingUtils.dropZone ).toBeVisible(); diff --git a/test/e2e/specs/editor/various/draggable-blocks.spec.js b/test/e2e/specs/editor/various/draggable-blocks.spec.js index 29a81f57540e0..e08030191dd60 100644 --- a/test/e2e/specs/editor/various/draggable-blocks.spec.js +++ b/test/e2e/specs/editor/various/draggable-blocks.spec.js @@ -134,9 +134,10 @@ test.describe( 'Draggable block', () => { const secondParagraphBound = await secondParagraph.boundingBox(); // Call the move function twice to make sure the `dragOver` event is sent. // @see https://github.com/microsoft/playwright/issues/17153 + // Make sure mouse is > 30px within the block for bottom drop indicator to appear. for ( let i = 0; i < 2; i += 1 ) { await page.mouse.move( - secondParagraphBound.x, + secondParagraphBound.x + 32, secondParagraphBound.y + secondParagraphBound.height * 0.75 ); } diff --git a/test/e2e/specs/editor/various/inserting-blocks.spec.js b/test/e2e/specs/editor/various/inserting-blocks.spec.js index 4d26a198fc345..a000f02eaca8c 100644 --- a/test/e2e/specs/editor/various/inserting-blocks.spec.js +++ b/test/e2e/specs/editor/various/inserting-blocks.spec.js @@ -451,7 +451,8 @@ class InsertingBlocksUtils { for ( let i = 0; i < 2; i += 1 ) { await this.page.mouse.move( // Hover on the right side of the block to avoid collapsing with the preview. - boundingBox.x + boundingBox.width - 1, + // But not too far to avoid triggering the grouping block inserter. + boundingBox.x + boundingBox.width - 32, // Hover on the bottom of the paragraph block. boundingBox.y + boundingBox.height - 1 ); diff --git a/test/e2e/specs/editor/various/list-view.spec.js b/test/e2e/specs/editor/various/list-view.spec.js index 674801cf94aba..00f21b4e51c5e 100644 --- a/test/e2e/specs/editor/various/list-view.spec.js +++ b/test/e2e/specs/editor/various/list-view.spec.js @@ -53,11 +53,40 @@ test.describe( 'List View', () => { name: 'Paragraph', exact: true, } ); + const imageBlockItem = listView.getByRole( 'gridcell', { + name: 'Image', + exact: true, + } ); const headingBlockItem = listView.getByRole( 'gridcell', { name: 'Heading', exact: true, } ); - await paragraphBlockItem.dragTo( headingBlockItem, { x: 0, y: 0 } ); + + await paragraphBlockItem.hover(); + await page.mouse.down(); + + // To work around a drag and drop bug in Safari, the list view applies + // `pointer-events: none` to the list view while dragging, so that + // `onDragLeave` is not fired when dragging within the list view. + // Without the `force: true` option, the `hover` action will fail + // as playwright will complain that pointer-events are intercepted. + // https://bugs.webkit.org/show_bug.cgi?id=66547 + // See: https://github.com/WordPress/gutenberg/pull/56625 + + // Hover over each block to mimic moving up the list view. + // Also, hover twice to ensure a dragover event is dispatched. + // See: https://playwright.dev/docs/input#dragging-manually + await imageBlockItem.hover( { force: true } ); + await imageBlockItem.hover( { force: true } ); + await headingBlockItem.hover( { force: true } ); + await headingBlockItem.hover( { force: true } ); + + // Disable reason: Need to wait until the throttle timeout of 250ms has passed. + /* eslint-disable playwright/no-wait-for-timeout */ + await editor.page.waitForTimeout( 300 ); + /* eslint-enable playwright/no-wait-for-timeout */ + + await page.mouse.up(); // Ensure the block was dropped correctly. await expect diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index 818a05881f53e..4e7bc80fdff5d 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -8,7 +8,7 @@ test.describe( 'Pattern Overrides', () => { await Promise.all( [ requestUtils.activateTheme( 'emptytheme' ), requestUtils.setGutenbergExperiments( [ - 'gutenberg-pattern-partial-syncing', + 'gutenberg-block-bindings', ] ), requestUtils.deleteAllBlocks(), ] ); @@ -216,4 +216,51 @@ test.describe( 'Pattern Overrides', () => { ] ); } ); } ); + + test( 'retains override values when converting a pattern block to regular blocks', async ( { + page, + admin, + requestUtils, + editor, + } ) => { + const paragraphId = 'paragraph-id'; + const { id } = await requestUtils.createBlock( { + title: 'Pattern', + content: ` +

    Editable

    +`, + status: 'publish', + } ); + + await admin.createNewPost(); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + // Make an edit to the pattern. + await editor.canvas + .getByRole( 'document', { name: 'Block: Paragraph' } ) + .focus(); + await page.keyboard.type( 'edited ' ); + + // Convert back to regular blocks. + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await editor.showBlockToolbar(); + await editor.clickBlockOptionsMenuItem( 'Detach' ); + + // Check that the overrides remain. + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'edited Editable', + metadata: undefined, + }, + }, + ] ); + } ); } ); diff --git a/test/e2e/specs/editor/various/patterns.spec.js b/test/e2e/specs/editor/various/patterns.spec.js index 4b47b2dd70a35..745f5a5aa417b 100644 --- a/test/e2e/specs/editor/various/patterns.spec.js +++ b/test/e2e/specs/editor/various/patterns.spec.js @@ -141,7 +141,16 @@ test.describe( 'Synced pattern', () => { { name: 'core/block', attributes: { ref: expect.any( Number ) }, - innerBlocks: [], + innerBlocks: [ + { + attributes: { + content: 'A useful paragraph to reuse', + dropCap: false, + }, + innerBlocks: [], + name: 'core/paragraph', + }, + ], }, ] ); const after = await editor.getBlocks(); @@ -167,4 +176,149 @@ test.describe( 'Synced pattern', () => { await expect.poll( editor.getBlocks ).toEqual( [ ...after, ...after ] ); } ); + + // Check for regressions of https://github.com/WordPress/gutenberg/issues/33072. + test( 'can be saved when modified inside of a published post', async ( { + page, + requestUtils, + editor, + } ) => { + const { id } = await requestUtils.createBlock( { + title: 'Alternative greeting block', + content: + '\n

    Guten Tag!

    \n', + status: 'publish', + } ); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + await editor.publishPost(); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await editor.showBlockToolbar(); + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'link', { name: 'Edit original' } ) + .click(); + + const editorTopBar = page.getByRole( 'region', { + name: 'Editor top bar', + } ); + + // Navigate to the pattern focus mode. + await expect( + editorTopBar.getByRole( 'heading', { + name: 'Alternative greeting block', + level: 1, + } ) + ).toBeVisible(); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Paragraph' } ) + ); + + // Change the block's content. + await page.keyboard.type( 'Einen ' ); + + // Save the reusable block and update the post. + await editorTopBar.getByRole( 'button', { name: 'Update' } ).click(); + await page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'Pattern updated.' } ) + .click(); + + // Go back to the post. + await editorTopBar.getByRole( 'button', { name: 'Back' } ).click(); + + await expect.poll( editor.getBlocks ).toEqual( [ + { + name: 'core/block', + attributes: { ref: id }, + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { + content: 'Einen Guten Tag!', + dropCap: false, + }, + innerBlocks: [], + }, + ], + }, + ] ); + } ); + + // Check for regressions of https://github.com/WordPress/gutenberg/issues/26421. + test( 'allows conversion back to blocks when the reusable block has unsaved edits', async ( { + page, + requestUtils, + editor, + } ) => { + const { id } = await requestUtils.createBlock( { + title: 'Synced pattern', + content: + '\n

    Before Edit

    \n', + status: 'publish', + } ); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'link', { name: 'Edit original' } ) + .click(); + + const editorTopBar = page.getByRole( 'region', { + name: 'Editor top bar', + } ); + + // Navigate to the pattern focus mode. + await expect( + editorTopBar.getByRole( 'heading', { + name: 'Synced pattern', + level: 1, + } ) + ).toBeVisible(); + + // Make an edit to the source pattern. + await editor.canvas + .getByRole( 'document', { name: 'Block: Paragraph' } ) + .fill( 'After Edit' ); + + // Go back to the post. + await editorTopBar.getByRole( 'button', { name: 'Back' } ).click(); + + const expectedParagraphBlock = { + name: 'core/paragraph', + attributes: { content: 'After Edit' }, + }; + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/block', + attributes: { ref: id }, + innerBlocks: [ expectedParagraphBlock ], + }, + ] ); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await editor.clickBlockOptionsMenuItem( 'Detach' ); + + await expect + .poll( editor.getBlocks ) + .toMatchObject( [ expectedParagraphBlock ] ); + } ); } ); diff --git a/test/e2e/specs/interactivity/directive-each.spec.ts b/test/e2e/specs/interactivity/directive-each.spec.ts new file mode 100644 index 0000000000000..4f024b1f828a9 --- /dev/null +++ b/test/e2e/specs/interactivity/directive-each.spec.ts @@ -0,0 +1,486 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-each', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-each' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-each' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'should use `item` as the defaul item name in the context', async ( { + page, + } ) => { + const elements = page.getByTestId( 'letters' ).getByTestId( 'item' ); + await expect( elements ).toHaveText( [ 'A', 'B', 'C' ] ); + } ); + + test( 'should use the specified item name in the context', async ( { + page, + } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + await expect( elements ).toHaveText( [ + 'avocado', + 'banana', + 'cherimoya', + ] ); + } ); + + test.describe( 'without `wp-each-key`', () => { + test.beforeEach( async ( { page } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + + // These tags are included to check that the elements are not unmounted + // and mounted again. If an element remounts, its tag should be missing. + await elements.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + } ); + + test( 'should preserve elements on deletion', async ( { page } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + + // An item is removed when clicked. + await elements.first().click(); + + await expect( elements ).toHaveText( [ 'banana', 'cherimoya' ] ); + await expect( elements.getByText( 'avocado' ) ).toBeHidden(); + + // Get the tags. They should not have disappeared. + const [ banana, cherimoya ] = await elements.all(); + await expect( banana ).toHaveAttribute( 'data-tag', '1' ); + await expect( cherimoya ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should preserve elements on reordering', async ( { page } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + + await page.getByTestId( 'fruits' ).getByTestId( 'rotate' ).click(); + + await expect( elements ).toHaveText( [ + 'cherimoya', + 'avocado', + 'banana', + ] ); + + // Get the tags. They should not have disappeared or changed. + const [ cherimoya, avocado, banana ] = await elements.all(); + await expect( cherimoya ).toHaveAttribute( 'data-tag', '2' ); + await expect( avocado ).toHaveAttribute( 'data-tag', '0' ); + await expect( banana ).toHaveAttribute( 'data-tag', '1' ); + } ); + + test( 'should preserve elements on addition', async ( { page } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + + await page.getByTestId( 'fruits' ).getByTestId( 'add' ).click(); + + await expect( elements ).toHaveText( [ + 'ananas', + 'avocado', + 'banana', + 'cherimoya', + ] ); + + // Get the tags. They should not have disappeared or changed, + // except for the newly created element. + const [ ananas, avocado, banana, cherimoya ] = await elements.all(); + await expect( ananas ).not.toHaveAttribute( 'data-tag' ); + await expect( avocado ).toHaveAttribute( 'data-tag', '0' ); + await expect( banana ).toHaveAttribute( 'data-tag', '1' ); + await expect( cherimoya ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should preserve elements on replacement', async ( { page } ) => { + const elements = page.getByTestId( 'fruits' ).getByTestId( 'item' ); + + await page.getByTestId( 'fruits' ).getByTestId( 'replace' ).click(); + + await expect( elements ).toHaveText( [ + 'ananas', + 'banana', + 'cherimoya', + ] ); + + // Get the tags. They should not have disappeared or changed, + // except for the newly created element. + const [ ananas, banana, cherimoya ] = await elements.all(); + await expect( ananas ).not.toHaveAttribute( 'data-tag' ); + await expect( banana ).toHaveAttribute( 'data-tag', '1' ); + await expect( cherimoya ).toHaveAttribute( 'data-tag', '2' ); + } ); + } ); + + test.describe( 'with `wp-each-key`', () => { + test.beforeEach( async ( { page } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + // These tags are included to check that the elements are not unmounted + // and mounted again. If an element remounts, its tag should be missing. + await elements.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + } ); + + test( 'should preserve elements on deletion', async ( { page } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + await expect( elements ).toHaveText( [ + 'A Game of Thrones', + 'A Clash of Kings', + 'A Storm of Swords', + ] ); + + // An item is removed when clicked. + await elements.first().click(); + + await expect( elements ).toHaveText( [ + 'A Clash of Kings', + 'A Storm of Swords', + ] ); + + // Get the tags. They should not have disappeared. + const [ acok, asos ] = await elements.all(); + await expect( acok ).toHaveAttribute( 'data-tag', '1' ); + await expect( asos ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should preserve elements on reordering', async ( { page } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + await page.getByTestId( 'books' ).getByTestId( 'rotate' ).click(); + + await expect( elements ).toHaveText( [ + 'A Storm of Swords', + 'A Game of Thrones', + 'A Clash of Kings', + ] ); + + // Get the tags. They should not have disappeared or changed. + const [ asos, agot, acok ] = await elements.all(); + await expect( asos ).toHaveAttribute( 'data-tag', '2' ); + await expect( agot ).toHaveAttribute( 'data-tag', '0' ); + await expect( acok ).toHaveAttribute( 'data-tag', '1' ); + } ); + + test( 'should preserve elements on addition', async ( { page } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + await page.getByTestId( 'books' ).getByTestId( 'add' ).click(); + + await expect( elements ).toHaveText( [ + 'A Feast for Crows', + 'A Game of Thrones', + 'A Clash of Kings', + 'A Storm of Swords', + ] ); + + // Get the tags. They should not have disappeared or changed, + // except for the newly created element. + const [ affc, agot, acok, asos ] = await elements.all(); + await expect( affc ).not.toHaveAttribute( 'data-tag' ); + await expect( agot ).toHaveAttribute( 'data-tag', '0' ); + await expect( acok ).toHaveAttribute( 'data-tag', '1' ); + await expect( asos ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should preserve elements on replacement', async ( { page } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + await page.getByTestId( 'books' ).getByTestId( 'replace' ).click(); + + await expect( elements ).toHaveText( [ + 'A Feast for Crows', + 'A Clash of Kings', + 'A Storm of Swords', + ] ); + + // Get the tags. They should not have disappeared or changed, + // except for the newly created element. + const [ affc, acok, asos ] = await elements.all(); + await expect( affc ).not.toHaveAttribute( 'data-tag' ); + await expect( acok ).toHaveAttribute( 'data-tag', '1' ); + await expect( asos ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should preserve elements on modification', async ( { + page, + } ) => { + const elements = page.getByTestId( 'books' ).getByTestId( 'item' ); + + await page.getByTestId( 'books' ).getByTestId( 'modify' ).click(); + + await expect( elements ).toHaveText( [ + 'A GAME OF THRONES', + 'A Clash of Kings', + 'A Storm of Swords', + ] ); + + // Get the tags. They should not have disappeared or changed. + const [ agot, acok, asos ] = await elements.all(); + await expect( agot ).toHaveAttribute( 'data-tag', '0' ); + await expect( acok ).toHaveAttribute( 'data-tag', '1' ); + await expect( asos ).toHaveAttribute( 'data-tag', '2' ); + } ); + } ); + + test( 'should respect elements after', async ( { page } ) => { + const elements = page.getByTestId( 'numbers' ).getByTestId( 'item' ); + await expect( elements ).toHaveText( [ '1', '2', '3', '4' ] ); + await page.getByTestId( 'numbers' ).getByTestId( 'shift' ).click(); + await expect( elements ).toHaveText( [ '2', '3', '4' ] ); + await page + .getByTestId( 'numbers' ) + .getByTestId( 'unshift' ) + .click( { clickCount: 2 } ); + await expect( elements ).toHaveText( [ '0', '1', '2', '3', '4' ] ); + } ); + + test( 'should support initial empty lists', async ( { page } ) => { + const elements = page.getByTestId( 'empty' ).getByTestId( 'item' ); + await expect( elements ).toHaveText( [ 'item X' ] ); + await page + .getByTestId( 'empty' ) + .getByTestId( 'add' ) + .click( { clickCount: 2 } ); + + await expect( elements ).toHaveText( [ 'item 0', 'item 1', 'item X' ] ); + } ); + + test( 'should support multiple siblings inside the template', async ( { + page, + } ) => { + const elements = page.getByTestId( 'siblings' ).getByTestId( 'item' ); + await expect( elements ).toHaveText( [ + 'two', + '2', + 'three', + '3', + 'four', + '4', + ] ); + await page.getByTestId( 'siblings' ).getByTestId( 'unshift' ).click(); + await expect( elements ).toHaveText( [ + 'one', + '1', + 'two', + '2', + 'three', + '3', + 'four', + '4', + ] ); + } ); + + test( 'should work on navigation', async ( { page } ) => { + const elements = page + .getByTestId( 'navigation-updated list' ) + .getByTestId( 'item' ); + + // These tags are included to check that the elements are not unmounted + // and mounted again. If an element remounts, its tag should be missing. + await elements.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + + await expect( elements ).toHaveText( [ 'beta', 'gamma', 'delta' ] ); + + await page + .getByTestId( 'navigation-updated list' ) + .getByTestId( 'navigate' ) + .click(); + + await expect( elements ).toHaveText( [ + 'alpha', + 'beta', + 'gamma', + 'delta', + ] ); + + // Get the tags. They should not have disappeared or changed, + // except for the newly created element. + const [ alpha, beta, gamma, delta ] = await elements.all(); + await expect( alpha ).not.toHaveAttribute( 'data-tag' ); + await expect( beta ).toHaveAttribute( 'data-tag', '0' ); + await expect( gamma ).toHaveAttribute( 'data-tag', '1' ); + await expect( delta ).toHaveAttribute( 'data-tag', '2' ); + } ); + + test( 'should work with nested lists', async ( { page } ) => { + const mainElement = page.getByTestId( 'nested' ); + + // These tags are included to check that the elements are not unmounted + // and mounted again. If an element remounts, its tag should be missing. + const listItems = mainElement.getByRole( 'listitem' ); + await listItems.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + + const animals = mainElement.getByTestId( 'animal' ); + + { + // Ensure it hydrates correctly. + const [ dog, cat ] = await animals.all(); + await expect( dog.getByTestId( 'name' ) ).toHaveText( 'Dog' ); + await expect( dog.getByRole( 'listitem' ) ).toHaveText( [ + 'chihuahua', + 'rottweiler', + ] ); + await expect( cat.getByTestId( 'name' ) ).toHaveText( 'Cat' ); + await expect( cat.getByRole( 'listitem' ) ).toHaveText( [ + 'sphynx', + 'siamese', + ] ); + } + + await mainElement.getByTestId( 'add animal' ).click(); + + { + // Ensure it works when the top list is modified. + const [ rat, dog, cat ] = await animals.all(); + await expect( rat.getByTestId( 'name' ) ).toHaveText( 'Rat' ); + await expect( rat.getByRole( 'listitem' ) ).toHaveText( [ + 'dumbo', + 'rex', + ] ); + await expect( dog.getByTestId( 'name' ) ).toHaveText( 'Dog' ); + await expect( dog.getByRole( 'listitem' ) ).toHaveText( [ + 'chihuahua', + 'rottweiler', + ] ); + await expect( cat.getByTestId( 'name' ) ).toHaveText( 'Cat' ); + await expect( cat.getByRole( 'listitem' ) ).toHaveText( [ + 'sphynx', + 'siamese', + ] ); + await expect( rat ).not.toHaveAttribute( 'data-tag' ); + const [ d1, d2 ] = await dog.getByRole( 'listitem' ).all(); + await expect( dog ).toHaveAttribute( 'data-tag', '0' ); + await expect( d1 ).toHaveAttribute( 'data-tag', '1' ); + await expect( d2 ).toHaveAttribute( 'data-tag', '2' ); + const [ c1, c2 ] = await cat.getByRole( 'listitem' ).all(); + await expect( cat ).toHaveAttribute( 'data-tag', '3' ); + await expect( c1 ).toHaveAttribute( 'data-tag', '4' ); + await expect( c2 ).toHaveAttribute( 'data-tag', '5' ); + } + + // Reset tags so the added elements have one. + await listItems.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + + await mainElement.getByTestId( 'add breeds' ).click(); + + { + // Ensure it works when the top list is modified. + const [ rat, dog, cat ] = await animals.all(); + await expect( rat.getByTestId( 'name' ) ).toHaveText( 'Rat' ); + await expect( rat.getByRole( 'listitem' ) ).toHaveText( [ + 'satin', + 'dumbo', + 'rex', + ] ); + await expect( dog.getByTestId( 'name' ) ).toHaveText( 'Dog' ); + await expect( dog.getByRole( 'listitem' ) ).toHaveText( [ + 'german shepherd', + 'chihuahua', + 'rottweiler', + ] ); + await expect( cat.getByTestId( 'name' ) ).toHaveText( 'Cat' ); + await expect( cat.getByRole( 'listitem' ) ).toHaveText( [ + 'maine coon', + 'sphynx', + 'siamese', + ] ); + const [ r1, r2, r3 ] = await rat.getByRole( 'listitem' ).all(); + await expect( rat ).toHaveAttribute( 'data-tag', '0' ); + await expect( r1 ).not.toHaveAttribute( 'data-tag' ); + await expect( r2 ).toHaveAttribute( 'data-tag', '1' ); + await expect( r3 ).toHaveAttribute( 'data-tag', '2' ); + const [ d1, d2, d3 ] = await dog.getByRole( 'listitem' ).all(); + await expect( dog ).toHaveAttribute( 'data-tag', '3' ); + await expect( d1 ).not.toHaveAttribute( 'data-tag' ); + await expect( d2 ).toHaveAttribute( 'data-tag', '4' ); + await expect( d3 ).toHaveAttribute( 'data-tag', '5' ); + const [ c1, c2, c3 ] = await cat.getByRole( 'listitem' ).all(); + await expect( cat ).toHaveAttribute( 'data-tag', '6' ); + await expect( c1 ).not.toHaveAttribute( 'data-tag' ); + await expect( c2 ).toHaveAttribute( 'data-tag', '7' ); + await expect( c3 ).toHaveAttribute( 'data-tag', '8' ); + } + } ); + + test( 'should do nothing when used on non-template elements', async ( { + page, + } ) => { + const elements = page + .getByTestId( 'invalid tag' ) + .getByTestId( 'item' ); + + await expect( elements ).toHaveCount( 1 ); + await expect( elements ).toBeEmpty(); + } ); + + test( 'should work with derived state as keys', async ( { page } ) => { + const elements = page + .getByTestId( 'derived state' ) + .getByTestId( 'item' ); + + // These tags are included to check that the elements are not unmounted + // and mounted again. If an element remounts, its tag should be missing. + await elements.evaluateAll( ( refs ) => + refs.forEach( ( ref, index ) => { + if ( ref instanceof HTMLElement ) { + ref.dataset.tag = `${ index }`; + } + } ) + ); + + await page + .getByTestId( 'derived state' ) + .getByTestId( 'rotate' ) + .click(); + + await expect( elements ).toHaveText( [ + 'cherimoya', + 'avocado', + 'banana', + ] ); + + // Get the tags. They should not have disappeared or changed. + const [ cherimoya, avocado, banana ] = await elements.all(); + await expect( cherimoya ).toHaveAttribute( 'data-tag', '2' ); + await expect( avocado ).toHaveAttribute( 'data-tag', '0' ); + await expect( banana ).toHaveAttribute( 'data-tag', '1' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directive-on-document.spec.ts b/test/e2e/specs/interactivity/directive-on-document.spec.ts index 824e217f08714..56ac7e91d3121 100644 --- a/test/e2e/specs/interactivity/directive-on-document.spec.ts +++ b/test/e2e/specs/interactivity/directive-on-document.spec.ts @@ -30,17 +30,28 @@ test.describe( 'data-wp-on-document', () => { page, } ) => { const counter = page.getByTestId( 'counter' ); + const isEventAttached = page.getByTestId( 'isEventAttached' ); const visibilityButton = page.getByTestId( 'visibility' ); + await expect( counter ).toHaveText( '0' ); + await expect( isEventAttached ).toHaveText( 'yes' ); await page.keyboard.press( 'ArrowDown' ); await expect( counter ).toHaveText( '1' ); + // Remove the element. await visibilityButton.click(); // This keyboard press should not increase the counter. await page.keyboard.press( 'ArrowDown' ); + // Add the element back. await visibilityButton.click(); await expect( counter ).toHaveText( '1' ); + await expect( counter ).toHaveText( '1' ); + + // Wait until the effects run again. + await expect( isEventAttached ).toHaveText( 'yes' ); + + // Check that the event listener is attached again. await page.keyboard.press( 'ArrowDown' ); await expect( counter ).toHaveText( '2' ); } ); diff --git a/test/e2e/specs/interactivity/with-scope.spec.ts b/test/e2e/specs/interactivity/with-scope.spec.ts new file mode 100644 index 0000000000000..1cb73cc915aca --- /dev/null +++ b/test/e2e/specs/interactivity/with-scope.spec.ts @@ -0,0 +1,27 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'withScope', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/with-scope' ); + } ); + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/with-scope' ) ); + } ); + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'directives using withScope should work with async and sync functions', async ( { + page, + } ) => { + const asyncCounter = page.getByTestId( 'asyncCounter' ); + await expect( asyncCounter ).toHaveText( '1' ); + const syncCounter = page.getByTestId( 'syncCounter' ); + await expect( syncCounter ).toHaveText( '1' ); + } ); +} ); diff --git a/test/e2e/specs/site-editor/block-removal.spec.js b/test/e2e/specs/site-editor/block-removal.spec.js index 27d3762364b44..bf63e089c8a0b 100644 --- a/test/e2e/specs/site-editor/block-removal.spec.js +++ b/test/e2e/specs/site-editor/block-removal.spec.js @@ -35,7 +35,7 @@ test.describe( 'Site editor block removal prompt', () => { // Expect the block removal prompt to have appeared await expect( page.getByText( - 'Post or page content will not be displayed if you delete these blocks.' + 'Deleting these blocks will stop your post or page content from displaying on this template. It is not recommended.' ) ).toBeVisible(); } ); @@ -59,7 +59,7 @@ test.describe( 'Site editor block removal prompt', () => { // Expect the block removal prompt to have appeared await expect( page.getByText( - 'Post or page content will not be displayed if you delete this block.' + 'Deleting this block will stop your post or page content from displaying on this template. It is not recommended.' ) ).toBeVisible(); } ); diff --git a/test/e2e/specs/site-editor/new-templates-list.spec.js b/test/e2e/specs/site-editor/new-templates-list.spec.js index 272a3ffff80bf..13484abcb13ad 100644 --- a/test/e2e/specs/site-editor/new-templates-list.spec.js +++ b/test/e2e/specs/site-editor/new-templates-list.spec.js @@ -18,11 +18,7 @@ test.describe( 'Templates', () => { ] ); } ); test( 'Sorting', async ( { admin, page } ) => { - await admin.visitSiteEditor( { path: '/wp_template' } ); - // Switch to table layout. - await page.getByLabel( 'View options' ).click(); - await page.getByRole( 'menuitem', { name: 'Layout' } ).click(); - await page.getByRole( 'menuitemradio', { name: 'Table' } ).click(); + await admin.visitSiteEditor( { path: '/wp_template/all' } ); // Descending by title. await page .getByRole( 'button', { name: 'Template', exact: true } ) @@ -52,13 +48,7 @@ test.describe( 'Templates', () => { title: 'Date Archives', content: 'hi', } ); - await admin.visitSiteEditor( { path: '/wp_template' } ); - - // Switch to table layout. - await page.getByLabel( 'View options' ).click(); - await page.getByRole( 'menuitem', { name: 'Layout' } ).click(); - await page.getByRole( 'menuitemradio', { name: 'Table' } ).click(); - + await admin.visitSiteEditor( { path: '/wp_template/all' } ); // Global search. await page.getByRole( 'searchbox', { name: 'Filter list' } ).click(); await page.keyboard.type( 'tag' ); @@ -94,13 +84,7 @@ test.describe( 'Templates', () => { await expect( titles ).toHaveCount( 2 ); } ); test( 'Field visibility', async ( { admin, page } ) => { - await admin.visitSiteEditor( { path: '/wp_template' } ); - - // Switch to table layout. - await page.getByLabel( 'View options' ).click(); - await page.getByRole( 'menuitem', { name: 'Layout' } ).click(); - await page.getByRole( 'menuitemradio', { name: 'Table' } ).click(); - + await admin.visitSiteEditor( { path: '/wp_template/all' } ); await page.getByRole( 'button', { name: 'Description' } ).click(); await page.getByRole( 'menuitem', { name: 'Hide' } ).click(); await expect( diff --git a/test/e2e/specs/site-editor/patterns.spec.js b/test/e2e/specs/site-editor/patterns.spec.js index e4fffe14fcc9a..6b2561d1f5bd9 100644 --- a/test/e2e/specs/site-editor/patterns.spec.js +++ b/test/e2e/specs/site-editor/patterns.spec.js @@ -13,7 +13,9 @@ const test = base.extend( { }, } ); -test.describe( 'Patterns', () => { +// Skip these tests for now as we plan to adapt them to +// the new patterns UI. +test.describe.skip( 'Patterns', () => { test.beforeAll( async ( { requestUtils } ) => { await requestUtils.activateTheme( 'emptytheme' ); await requestUtils.deleteAllBlocks(); diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js index ff62da26bcfac..9cb8ba53461e0 100644 --- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js +++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js @@ -14,7 +14,7 @@ test.use( { }, } ); -test.describe( 'Global styles revisions', () => { +test.describe( 'Style Revisions', () => { let stylesPostId; test.beforeAll( async ( { requestUtils } ) => { await Promise.all( [