diff --git a/phpcs.xml.dist b/phpcs.xml.dist index f5ea0774..d7f04d55 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -46,7 +46,7 @@ - + diff --git a/src/parser/content-parser.php b/src/parser/content-parser.php index 7d26b950..c9fab6c2 100644 --- a/src/parser/content-parser.php +++ b/src/parser/content-parser.php @@ -11,9 +11,11 @@ use Throwable; use WP_Error; -use WP_Block_Type; +use WP_Block; use WP_Block_Type_Registry; use Symfony\Component\DomCrawler\Crawler; +use function apply_filters; +use function parse_blocks; /** * The content parser that would be used to transform a post into an array of blocks, along with their attributes. @@ -61,21 +63,28 @@ public function __construct( $block_registry = null ) { * Filter out a block from the blocks output based on: * * - include parameter, if it is set or - * - exclude parameter, if it is set. + * - exclude parameter, if it is set or + * - whether it is an empty whitespace block * * and finally, based on a filter vip_block_data_api__allow_block * - * @param array $block Current block. - * @param string $block_name Name of the block. - * @param array $filter_options Options to be used for filtering, if any. + * @param WP_Block $block Current block. + * @param array $filter_options Options to be used for filtering, if any. * * @return bool true, if the block should be included or false otherwise * * @access private */ - public function should_block_be_included( $block, $block_name, $filter_options ) { + protected function should_block_be_included( WP_Block $block, array $filter_options ) { + $block_name = $block->name; $is_block_included = true; + // Whitespace blocks are always excluded. + $is_whitespace_block = null === $block_name && empty( trim( $block->inner_html ) ); + if ( $is_whitespace_block ) { + return false; + } + if ( ! empty( $filter_options['include'] ) ) { $is_block_included = in_array( $block_name, $filter_options['include'] ); } elseif ( ! empty( $filter_options['exclude'] ) ) { @@ -86,11 +95,11 @@ public function should_block_be_included( $block, $block_name, $filter_options ) * Filter out blocks from the blocks output * * @param bool $is_block_included True if the block should be included, or false to filter it out. - * @param string $block_name Name of the parsed block, e.g. 'core/paragraph'. - * @param string $block Result of parse_blocks() for this block. + * @param string $block_name Name of the parsed block, e.g. 'core/paragraph'. + * @param array $block Result of parse_blocks() for this block. * Contains 'blockName', 'attrs', 'innerHTML', and 'innerBlocks' keys. */ - return apply_filters( 'vip_block_data_api__allow_block', $is_block_included, $block_name, $block ); + return apply_filters( 'vip_block_data_api__allow_block', $is_block_included, $block_name, $block->parsed_block ); } /** @@ -137,16 +146,14 @@ public function parse( $post_content, $post_id = null, $filter_options = [] ) { $post_content = apply_filters( 'vip_block_data_api__before_parse_post_content', $post_content, $post_id ); $blocks = parse_blocks( $post_content ); - $blocks = array_values( array_filter( $blocks, function ( $block ) { - $is_whitespace_block = ( null === $block['blockName'] && empty( trim( $block['innerHTML'] ) ) ); - return ! $is_whitespace_block; - } ) ); - $registered_blocks = $this->block_registry->get_all_registered(); + $sourced_blocks = array_map( function ( $block ) use ( $filter_options ) { + // Render the block, then walk the tree using source_block to apply our + // sourced attribute logic. + $rendered_block = $this->render_parsed_block( $block ); - $sourced_blocks = array_map(function ( $block ) use ( $registered_blocks, $filter_options ) { - return $this->source_block( $block, $registered_blocks, $filter_options ); - }, $blocks); + return $this->source_block( $rendered_block, $filter_options ); + }, $blocks ); $sourced_blocks = array_values( array_filter( $sourced_blocks ) ); @@ -189,37 +196,113 @@ public function parse( $post_content, $post_id = null, $filter_options = [] ) { } } + /** + * Helper function to render a parsed block, so that we can benefit from + * core-powered functions like block bindings and synced patterns. + * + * This loosely mirrors the code in the `render_block` function in core, but + * allows us to capture the block instance so that we can traverse the tree: + * https://github.com/WordPress/WordPress/blob/01d2199622d52b08d1704871770c68e35d2a80dc/wp-includes/blocks.php#L2012 + * + * @param array $parsed_block Parsed block (result of `parse_blocks`). + * @return WP_Block + */ + protected function render_parsed_block( array $parsed_block ): WP_Block { + $context = []; + if ( is_int( $this->post_id ) ) { + $context['postId'] = $this->post_id; + $context['postType'] = get_post_type( $this->post_id ); + } + + $context = apply_filters( 'render_block_context', $context, $parsed_block, null ); + + $block_instance = new WP_Block( $parsed_block, $context, $this->block_registry ); + $block_instance->render(); + + return $block_instance; + } + /** * Processes a single block, and returns the sourced block data. * - * @param array $block Block to be processed. - * @param WP_Block_Type[] $registered_blocks Blocks that have been registered. - * @param array $filter_options Options to filter using, if any. + * @param WP_Block $block Block to be processed. + * @param array $filter_options Options to filter using, if any. * * @return array|null * * @access private */ - protected function source_block( $block, $registered_blocks, $filter_options ) { - $block_name = $block['blockName']; + protected function source_block( WP_Block $block, array $filter_options ) { + $block_name = $block->name; - if ( ! $this->should_block_be_included( $block, $block_name, $filter_options ) ) { + if ( ! $this->should_block_be_included( $block, $filter_options ) ) { return null; } - if ( ! isset( $registered_blocks[ $block_name ] ) ) { + if ( ! $this->block_registry->is_registered( $block_name ) ) { $this->add_missing_block_warning( $block_name ); } - $block_definition = $registered_blocks[ $block_name ] ?? null; - $block_definition_attributes = $block_definition->attributes ?? []; + $sourced_block = [ + 'name' => $block->name, + 'attributes' => $this->apply_sourced_attributes( $block ), + ]; - $block_attributes = $block['attrs']; + // WP_Block#inner_blocks can be an array or WP_Block_List (iterable). + $inner_blocks = iterator_to_array( $block->inner_blocks ); + + // Recursively iterate over inner blocks. + $sourced_inner_blocks = array_values( array_filter( array_map( function ( $inner_block ) use ( $filter_options ) { + return $this->source_block( $inner_block, $filter_options ); + }, $inner_blocks ) ) ); + + // Only set innerBlocks if entries are present to match prior version behavior. + if ( ! empty( $sourced_inner_blocks ) ) { + $sourced_block['innerBlocks'] = $sourced_inner_blocks; + } + + /** + * Filters a block when parsing is complete. + * + * @param array $sourced_block An associative array of parsed block data with keys 'name' and 'attribute'. + * @param string $block_name Name of the parsed block, e.g. 'core/paragraph'. + * @param int $post_id Post ID associated with the parsed block. + * @param array $block Result of parse_blocks() for this block. Contains 'blockName', 'attrs', 'innerHTML', and 'innerBlocks' keys. + */ + $sourced_block = apply_filters( 'vip_block_data_api__sourced_block_result', $sourced_block, $block_name, $this->post_id, $block->parsed_block ); + + // If attributes are empty, explicitly use an object to avoid encoding an empty array in JSON. + if ( empty( $sourced_block['attributes'] ) ) { + $sourced_block['attributes'] = (object) []; + } + + return $sourced_block; + } + + /** + * Source the attributes of a block and return a merged attribute array. + * + * @param WP_Block $block Block to be processed. + * @return array Attribute array + */ + protected function apply_sourced_attributes( WP_Block $block ): array { + $block_definition = $this->block_registry->get_registered( $block->name ) ?? null; + $block_definition_attributes = $block_definition->attributes ?? []; + $block_attributes = $block->attributes; foreach ( $block_definition_attributes as $block_attribute_name => $block_attribute_definition ) { $attribute_source = $block_attribute_definition['source'] ?? null; $attribute_default_value = $block_attribute_definition['default'] ?? null; + // If the attribute was resolved from a block binding, it has a value, and it's not the default value, skip. + if ( + isset( $block_attributes['metadata']['bindings'][ $block_attribute_name ], $block_attributes[ $block_attribute_name ] ) && + ! empty( $block_attributes[ $block_attribute_name ] ) && + $block_attributes[ $block_attribute_name ] !== $attribute_default_value + ) { + continue; + } + if ( null === $attribute_source ) { // Unsourced attributes are stored in the block's delimiter attributes, skip DOM parser. @@ -237,7 +320,7 @@ protected function source_block( $block, $registered_blocks, $filter_options ) { } // Specify a manual doctype so that the parser will use the HTML5 parser. - $crawler = new Crawler( sprintf( '%s', $block['innerHTML'] ) ); + $crawler = new Crawler( sprintf( '%s', $block->inner_html ) ); // Enter the tag for block parsing. $crawler = $crawler->filter( 'body' )->children(); @@ -249,45 +332,10 @@ protected function source_block( $block, $registered_blocks, $filter_options ) { } } - $sourced_block = [ - 'name' => $block_name, - 'attributes' => $block_attributes, - ]; - - if ( isset( $block['innerBlocks'] ) ) { - $inner_blocks = array_map( function ( $block ) use ( $registered_blocks, $filter_options ) { - return $this->source_block( $block, $registered_blocks, $filter_options ); - }, $block['innerBlocks'] ); - - $inner_blocks = array_values( array_filter( $inner_blocks ) ); - - if ( ! empty( $inner_blocks ) ) { - $sourced_block['innerBlocks'] = $inner_blocks; - } - } + // Sort attributes by key to ensure consistent output. + ksort( $block_attributes ); - if ( $this->is_debug_enabled() ) { - $sourced_block['debug'] = [ - 'block_definition_attributes' => $block_definition->attributes, - ]; - } - - /** - * Filters a block when parsing is complete. - * - * @param array $sourced_block An associative array of parsed block data with keys 'name' and 'attribute'. - * @param string $block_name Name of the parsed block, e.g. 'core/paragraph'. - * @param int $post_id Post ID associated with the parsed block. - * @param array $block Result of parse_blocks() for this block. Contains 'blockName', 'attrs', 'innerHTML', and 'innerBlocks' keys. - */ - $sourced_block = apply_filters( 'vip_block_data_api__sourced_block_result', $sourced_block, $block_name, $this->post_id, $block ); - - // If attributes are empty, explicitly use an object to avoid encoding an empty array in JSON. - if ( empty( $sourced_block['attributes'] ) ) { - $sourced_block['attributes'] = (object) []; - } - - return $sourced_block; + return $block_attributes; } /** diff --git a/tests/graphql/test-graphql-api-v1.php b/tests/graphql/test-graphql-api-v1.php index 9a5d74f2..5380e96c 100644 --- a/tests/graphql/test-graphql-api-v1.php +++ b/tests/graphql/test-graphql-api-v1.php @@ -37,7 +37,7 @@ public function test_is_graphql_enabled_false() { // get_blocks_data() tests public function test_get_blocks_data() { - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -53,7 +53,7 @@ public function test_get_blocks_data() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-quote', [ + $this->register_block_with_attributes( 'test/custom-quote', [ 'value' => [ 'type' => 'string', 'source' => 'html', @@ -70,7 +70,7 @@ public function test_get_blocks_data() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-heading', [ + $this->register_block_with_attributes( 'test/custom-heading', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -192,7 +192,7 @@ public function test_get_blocks_data() { // get_blocks_data() attribute type tests public function test_array_data_in_attribute() { - $this->register_global_block_with_attributes( 'test/custom-table', [ + $this->register_block_with_attributes( 'test/custom-table', [ 'head' => [ 'type' => 'array', 'default' => [], @@ -306,11 +306,6 @@ public function test_array_data_in_attribute() { [ 'name' => 'test/custom-table', 'attributes' => [ - [ - 'name' => 'head', - 'value' => '[{"cells":[{"content":"Header A","tag":"th"},{"content":"Header B","tag":"th"}]}]', - 'isValueJsonEncoded' => true, - ], [ 'name' => 'body', 'value' => '[{"cells":[{"content":"Value A","tag":"td"},{"content":"Value B","tag":"td"}]},{"cells":[{"content":"Value C","tag":"td"},{"content":"Value D","tag":"td"}]}]', @@ -321,6 +316,11 @@ public function test_array_data_in_attribute() { 'value' => '[{"cells":[{"content":"Footer A","tag":"td"},{"content":"Footer B","tag":"td"}]}]', 'isValueJsonEncoded' => true, ], + [ + 'name' => 'head', + 'value' => '[{"cells":[{"content":"Header A","tag":"th"},{"content":"Header B","tag":"th"}]}]', + 'isValueJsonEncoded' => true, + ], ], 'id' => '1', ], diff --git a/tests/graphql/test-graphql-api-v2.php b/tests/graphql/test-graphql-api-v2.php index 326c35eb..53e5e2c1 100644 --- a/tests/graphql/test-graphql-api-v2.php +++ b/tests/graphql/test-graphql-api-v2.php @@ -37,7 +37,7 @@ public function test_is_graphql_enabled_false() { // get_blocks_data() tests public function test_get_blocks_data() { - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -53,7 +53,7 @@ public function test_get_blocks_data() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-quote', [ + $this->register_block_with_attributes( 'test/custom-quote', [ 'value' => [ 'type' => 'string', 'source' => 'html', @@ -70,7 +70,7 @@ public function test_get_blocks_data() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-heading', [ + $this->register_block_with_attributes( 'test/custom-heading', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -193,7 +193,7 @@ public function test_get_blocks_data() { // get_blocks_data() attribute type tests public function test_array_data_in_attribute() { - $this->register_global_block_with_attributes( 'test/custom-table', [ + $this->register_block_with_attributes( 'test/custom-table', [ 'head' => [ 'type' => 'array', 'default' => [], @@ -309,11 +309,6 @@ public function test_array_data_in_attribute() { 'id' => '1', 'parentId' => null, 'attributes' => [ - [ - 'name' => 'head', - 'value' => '[{"cells":[{"content":"Header A","tag":"th"},{"content":"Header B","tag":"th"}]}]', - 'isValueJsonEncoded' => true, - ], [ 'name' => 'body', 'value' => '[{"cells":[{"content":"Value A","tag":"td"},{"content":"Value B","tag":"td"}]},{"cells":[{"content":"Value C","tag":"td"},{"content":"Value D","tag":"td"}]}]', @@ -324,6 +319,11 @@ public function test_array_data_in_attribute() { 'value' => '[{"cells":[{"content":"Footer A","tag":"td"},{"content":"Footer B","tag":"td"}]}]', 'isValueJsonEncoded' => true, ], + [ + 'name' => 'head', + 'value' => '[{"cells":[{"content":"Header A","tag":"th"},{"content":"Header B","tag":"th"}]}]', + 'isValueJsonEncoded' => true, + ], ], ], ], @@ -339,7 +339,7 @@ public function test_array_data_in_attribute() { } public function test_get_block_data_with_boolean_attributes() { - $this->register_global_block_with_attributes( 'test/toggle-text', [ + $this->register_block_with_attributes( 'test/toggle-text', [ 'isVisible' => [ 'type' => 'boolean', ], @@ -362,13 +362,13 @@ public function test_get_block_data_with_boolean_attributes() { 'name' => 'test/toggle-text', 'attributes' => [ [ - 'name' => 'isVisible', - 'value' => 'true', + 'name' => 'isBordered', + 'value' => 'false', 'isValueJsonEncoded' => true, ], [ - 'name' => 'isBordered', - 'value' => 'false', + 'name' => 'isVisible', + 'value' => 'true', 'isValueJsonEncoded' => true, ], ], @@ -386,7 +386,7 @@ public function test_get_block_data_with_boolean_attributes() { } public function test_get_block_data_with_number_attributes() { - $this->register_global_block_with_attributes( 'test/gallery-block', [ + $this->register_block_with_attributes( 'test/gallery-block', [ 'tileCount' => [ 'type' => 'number', ], @@ -417,13 +417,13 @@ public function test_get_block_data_with_number_attributes() { 'isValueJsonEncoded' => true, ], [ - 'name' => 'tileWidthPx', - 'value' => '300', + 'name' => 'tileOpacity', + 'value' => '0.5', 'isValueJsonEncoded' => true, ], [ - 'name' => 'tileOpacity', - 'value' => '0.5', + 'name' => 'tileWidthPx', + 'value' => '300', 'isValueJsonEncoded' => true, ], ], @@ -441,7 +441,7 @@ public function test_get_block_data_with_number_attributes() { } public function test_get_block_data_with_string_attribute() { - $this->register_global_block_with_attributes( 'test/custom-block', [ + $this->register_block_with_attributes( 'test/custom-block', [ 'myComment' => [ 'type' => 'string', ], diff --git a/tests/parser/blocks/test-unregistered-block.php b/tests/parser/blocks/test-unregistered-block.php index e884d37d..2cc5a39a 100644 --- a/tests/parser/blocks/test-unregistered-block.php +++ b/tests/parser/blocks/test-unregistered-block.php @@ -31,7 +31,7 @@ public function test_parse_unregistered_block() { 'Block type "test/unknown-block" is not server-side registered. Sourced block attributes will not be available.', ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArrayHasKey( 'warnings', $blocks, sprintf( 'Expected parser to have warnings, none received: %s', wp_json_encode( $blocks ) ) ); diff --git a/tests/parser/sources/test-source-attribute.php b/tests/parser/sources/test-source-attribute.php index 5214695a..2273127b 100644 --- a/tests/parser/sources/test-source-attribute.php +++ b/tests/parser/sources/test-source-attribute.php @@ -37,7 +37,7 @@ public function test_parse_attribute_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -69,7 +69,7 @@ public function test_parse_attribute_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -100,7 +100,7 @@ public function test_parse_attribute_source__with_asterisk_selector() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-children.php b/tests/parser/sources/test-source-children.php index d5a5506e..198fd61f 100644 --- a/tests/parser/sources/test-source-children.php +++ b/tests/parser/sources/test-source-children.php @@ -52,7 +52,7 @@ public function test_parse_children__with_list_elements() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -86,7 +86,7 @@ public function test_parse_children__with_single_child() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -126,7 +126,7 @@ public function test_parse_children__with_mixed_nodes_and_text() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -157,7 +157,7 @@ public function test_parse_children__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-delimiter.php b/tests/parser/sources/test-source-delimiter.php index 5ee0b5fe..93e08cf2 100644 --- a/tests/parser/sources/test-source-delimiter.php +++ b/tests/parser/sources/test-source-delimiter.php @@ -45,7 +45,7 @@ public function test_parse_block_delimiter_attributes() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -75,7 +75,7 @@ public function test_parse_block_delimiter_attributes__are_overridden_by_sourced ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-html.php b/tests/parser/sources/test-source-html.php index 3b5ef245..61d6d87c 100644 --- a/tests/parser/sources/test-source-html.php +++ b/tests/parser/sources/test-source-html.php @@ -36,7 +36,7 @@ public function test_parse_html_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -71,7 +71,7 @@ public function test_parse_html_source__with_multiline_selector() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -102,7 +102,7 @@ public function test_parse_html_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-meta.php b/tests/parser/sources/test-source-meta.php index 972fd3e1..758d6704 100644 --- a/tests/parser/sources/test-source-meta.php +++ b/tests/parser/sources/test-source-meta.php @@ -39,7 +39,7 @@ public function test_parse_meta_source() { return $post_id; }; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html, $post_id ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); @@ -69,7 +69,7 @@ public function test_parse_meta_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html, $post_id ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); diff --git a/tests/parser/sources/test-source-node.php b/tests/parser/sources/test-source-node.php index 602532e7..875277ea 100644 --- a/tests/parser/sources/test-source-node.php +++ b/tests/parser/sources/test-source-node.php @@ -43,7 +43,7 @@ public function test_parse_node__with_object_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-query.php b/tests/parser/sources/test-source-query.php index 42b9263c..58548c1c 100644 --- a/tests/parser/sources/test-source-query.php +++ b/tests/parser/sources/test-source-query.php @@ -59,7 +59,7 @@ public function test_parse_query_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); @@ -145,7 +145,7 @@ public function test_parse_query_source__with_nested_query() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); @@ -193,7 +193,7 @@ public function test_parse_query_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); diff --git a/tests/parser/sources/test-source-raw.php b/tests/parser/sources/test-source-raw.php index 1fd4723b..f6d52b97 100644 --- a/tests/parser/sources/test-source-raw.php +++ b/tests/parser/sources/test-source-raw.php @@ -33,7 +33,7 @@ public function test_parse_raw_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -74,7 +74,7 @@ public function test_parse_raw_source__nested() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-rich-text.php b/tests/parser/sources/test-source-rich-text.php index 286bc691..26421cc3 100644 --- a/tests/parser/sources/test-source-rich-text.php +++ b/tests/parser/sources/test-source-rich-text.php @@ -36,7 +36,7 @@ public function test_parse_rich_text_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -69,7 +69,7 @@ public function test_parse_rich_text_source__with_formatting() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -100,7 +100,7 @@ public function test_parse_rich_text_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -131,7 +131,7 @@ public function test_parse_rich_text_source__with_default_value_with_formatting( ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-tag.php b/tests/parser/sources/test-source-tag.php index 93f4c4a9..51cfb2e1 100644 --- a/tests/parser/sources/test-source-tag.php +++ b/tests/parser/sources/test-source-tag.php @@ -35,7 +35,7 @@ public function test_parse_tag_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -79,7 +79,7 @@ public function test_parse_tag_source__in_query() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -106,7 +106,7 @@ public function test_parse_tag_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/sources/test-source-text.php b/tests/parser/sources/test-source-text.php index 6432d8eb..b63e12ce 100644 --- a/tests/parser/sources/test-source-text.php +++ b/tests/parser/sources/test-source-text.php @@ -38,7 +38,7 @@ public function test_parse_text_source() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -72,7 +72,7 @@ public function test_parse_text_source__with_html_tags() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -104,7 +104,7 @@ public function test_parse_text_source__with_default_value() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/test-block-bindings.php b/tests/parser/test-block-bindings.php new file mode 100644 index 00000000..757add11 --- /dev/null +++ b/tests/parser/test-block-bindings.php @@ -0,0 +1,295 @@ +markTestSkipped( 'This test suite requires WP_Block_Bindings_Registry (WordPress 6.5 or higher).' ); + } + } + + /* Single paragraph block binding */ + + public function test_single_paragraph_block_binding() { + + $this->register_block_bindings_source( + 'test/block-binding', + [ + 'label' => 'Test paragraph block binding', + 'get_value_callback' => static function ( array $args, WP_Block $block ) { + return sprintf( 'Block binding for %s with arg foo=%s', $block->name, $args['foo'] ); + }, + ] + ); + + $html = ' + +

Fallback content

+ + '; + + $expected_blocks = [ + [ + 'name' => 'core/paragraph', + 'attributes' => [ + 'content' => 'Block binding for core/paragraph with arg foo=bar', + 'metadata' => [ + 'bindings' => [ + 'content' => [ + 'source' => 'test/block-binding', + 'args' => [ 'foo' => 'bar' ], + ], + ], + ], + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + + // Block bindings are currently only supported for specific core blocks. + // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/ + // + // Core block attributes can change, so we use assertArraySubset to avoid + // brittle assertions. + $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) ); + $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' ); + } + + /* Image block with multiple bindings */ + + public function test_image_block_with_multiple_bindings() { + $this->register_block_bindings_source( + 'test/block-binding-image-url', + [ + 'label' => 'Test image block binding for URL', + 'get_value_callback' => static function ( array $args, WP_Block $block ) { + return sprintf( 'https://example.com/image.webp?block=%s&foo=%s', $block->name, $args['foo'] ); + }, + ] + ); + + $this->register_block_bindings_source( + 'test/block-binding-image-alt', + [ + 'label' => 'Test image block binding for alt text', + 'get_value_callback' => static function ( array $args, WP_Block $block ) { + return sprintf( 'Block binding for %s with arg foo=%s', $block->name, $args['foo'] ); + }, + ] + ); + + $html = ' + + Fallback alt text + + '; + + $expected_blocks = [ + [ + 'name' => 'core/image', + 'attributes' => [ + 'alt' => 'Block binding for core/image with arg foo=bar', + 'url' => 'https://example.com/image.webp?block=core/image&foo=bar', + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + + // Block bindings are currently only supported for specific core blocks. + // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/ + // + // Core block attributes can change, so we use assertArraySubset to avoid + // brittle assertions. + $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) ); + $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' ); + } + + /* Paragraph block binding with default post context */ + + public function test_button_block_binding_with_default_context() { + $this->register_block_bindings_source( + 'test/block-binding-with-default-context', [ + 'label' => 'Test paragraph block binding with default context', + 'get_value_callback' => static function ( array $args, WP_Block $block ) { + return sprintf( 'Block binding for %s with arg foo=%s in %s %d', $block->name, $args['foo'], $block->context['postType'], $block->context['postId'] ); + }, + 'uses_context' => [ 'postId', 'postType' ], + ] + ); + + $html = ' + +

Fallback content

+ + '; + + $post = $this->factory()->post->create_and_get(); + + $expected_blocks = [ + [ + 'name' => 'core/paragraph', + 'attributes' => [ + 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ), + 'metadata' => [ + 'bindings' => [ + 'content' => [ + 'source' => 'test/block-binding-with-default-context', + 'args' => [ 'foo' => 'bar' ], + ], + ], + ], + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html, $post->ID ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + + // Block bindings are currently only supported for specific core blocks. + // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/ + // + // Core block attributes can change, so we use assertArraySubset to avoid + // brittle assertions. + $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) ); + $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' ); + } + + /* Nested paragraph block binding with context */ + + public function test_nested_paragraph_block_binding_with_custom_context() { + $this->register_block_bindings_source( + 'test/block-binding-with-custom-context', [ + 'label' => 'Test paragraph block binding with custom context', + 'get_value_callback' => static function ( array $args, WP_Block $block ) { + return sprintf( 'Block binding for %s with arg foo=%s and context fizz=%s', $block->name, $args['foo'], $block->context['my-context/fizz'] ?? 'missing' ); + }, + 'uses_context' => [ 'my-context/fizz' ], + ] + ); + + $this->register_block_with_attributes( + 'test/context-provider', + [ + 'fizz' => [ + 'type' => 'string', + ], + ], + [ + 'provides_context' => [ + 'my-context/fizz' => 'fizz', + ], + ] + ); + + $html = ' + + +

Fallback content

+ + + '; + + $expected_blocks = [ + [ + 'name' => 'test/context-provider', + 'attributes' => [ + 'fizz' => 'buzz', + ], + 'innerBlocks' => [ + [ + 'name' => 'core/paragraph', + 'attributes' => [ + 'content' => 'Block binding for core/paragraph with arg foo=bar and context fizz=buzz', + 'metadata' => [ + 'bindings' => [ + 'content' => [ + 'source' => 'test/block-binding-with-custom-context', + 'args' => [ 'foo' => 'bar' ], + ], + ], + ], + ], + ], + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + + // Block bindings are currently only supported for specific core blocks. + // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/ + // + // Core block attributes can change, so we use assertArraySubset to avoid + // brittle assertions. + $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) ); + $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' ); + $this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'] ), 'Too many inner blocks in result set' ); + } + + /* Missing block binding */ + + public function test_missing_block_binding() { + + $html = ' + +

Fallback content

+ + '; + + $expected_blocks = [ + [ + 'name' => 'core/paragraph', + 'attributes' => [ + 'content' => 'Fallback content', + 'metadata' => [ + 'bindings' => [ + 'content' => [ + 'source' => 'test/missing-block-binding', + 'args' => [ 'foo' => 'bar' ], + ], + ], + ], + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + + // Block bindings are currently only supported for specific core blocks. + // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/ + // + // Core block attributes can change, so we use assertArraySubset to avoid + // brittle assertions. + $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) ); + $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' ); + } +} diff --git a/tests/parser/test-content-parser.php b/tests/parser/test-content-parser.php index d11b65ec..9852b7c0 100644 --- a/tests/parser/test-content-parser.php +++ b/tests/parser/test-content-parser.php @@ -47,7 +47,7 @@ public function test_parse_multiple_attributes_from_block() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -98,7 +98,7 @@ public function test_parse_multiple_blocks() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -157,9 +157,62 @@ public function test_parse_block_missing_attributes_and_defaults() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); } + + /* Whitespace block removal */ + + public function test_parse_whitespace_block_removal() { + $this->register_block_with_attributes( 'test/block', [ + 'content' => [ + 'type' => 'string', + 'source' => 'html', + 'selector' => 'p', + ], + ] ); + + $html = join( [ + // Some intentional whitespace + ' + ', + ' +

Block 1

+ + ', + // Some intentional whitespace + ' + ', + ' +

Block 2

+ + ', + // Some intentional whitespace + ' + ', + ] ); + + $expected_blocks = [ + [ + 'name' => 'test/block', + 'attributes' => [ + 'content' => 'Block 1', + ], + ], + [ + 'name' => 'test/block', + 'attributes' => [ + 'content' => 'Block 2', + ], + ], + ]; + + $content_parser = new ContentParser( $this->get_block_registry() ); + $blocks = $content_parser->parse( $html ); + + $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); + $this->assertEquals( $expected_blocks, $blocks['blocks'], sprintf( 'Blocks do not match: %s', wp_json_encode( $blocks ) ) ); + } } diff --git a/tests/parser/test-inner-blocks.php b/tests/parser/test-inner-blocks.php index 07f5d016..2348761c 100644 --- a/tests/parser/test-inner-blocks.php +++ b/tests/parser/test-inner-blocks.php @@ -88,7 +88,7 @@ public function test_parse_inner_blocks__one_layer() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); @@ -181,7 +181,7 @@ public function test_parse_inner_blocks__two_layers() { ], ]; - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) ); $this->assertArraySubset( $expected_blocks, $blocks['blocks'], true ); diff --git a/tests/parser/test-parser-filters.php b/tests/parser/test-parser-filters.php index de99543f..8b993e8e 100644 --- a/tests/parser/test-parser-filters.php +++ b/tests/parser/test-parser-filters.php @@ -59,7 +59,7 @@ public function test_allow_block_filter_via_code() { }; add_filter( 'vip_block_data_api__allow_block', $block_filter_function, 10, 2 ); - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); remove_filter( 'vip_block_data_api__allow_block', $block_filter_function, 10, 2 ); @@ -100,7 +100,7 @@ public function test_before_parse_post_content_filter() { }; add_filter( 'vip_block_data_api__before_parse_post_content', $replace_post_content_filter ); - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $blocks = $content_parser->parse( $html ); remove_filter( 'vip_block_data_api__before_parse_post_content', $replace_post_content_filter ); @@ -143,7 +143,7 @@ public function test_after_parse_filter() { }; add_filter( 'vip_block_data_api__after_parse_blocks', $add_extra_data_filter ); - $content_parser = new ContentParser( $this->registry ); + $content_parser = new ContentParser( $this->get_block_registry() ); $result = $content_parser->parse( $html ); remove_filter( 'vip_block_data_api__after_parse_blocks', $add_extra_data_filter ); diff --git a/tests/registry-test-case.php b/tests/registry-test-case.php index d2d4004c..7abfafbf 100644 --- a/tests/registry-test-case.php +++ b/tests/registry-test-case.php @@ -3,6 +3,7 @@ namespace WPCOMVIP\BlockDataApi; use WP_Block_Type_Registry; +use WP_Block_Bindings_Registry; use WP_UnitTestCase; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; @@ -12,18 +13,27 @@ class RegistryTestCase extends WP_UnitTestCase { use ArraySubsetAsserts; - protected $registry; - protected $globally_registered_blocks = []; - - protected function setUp(): void { - parent::setUp(); + protected function tearDown(): void { + // Unregister non-core blocks. + $block_registry = WP_Block_Type_Registry::get_instance(); + foreach ( $block_registry->get_all_registered() as $block_type ) { + if ( 'core/' === substr( $block_type->name, 0, 5 ) ) { + continue; + } + + $block_registry->unregister( $block_type->name ); + } - $this->registry = new WP_Block_Type_Registry(); - } + if ( class_exists( 'WP_Block_Bindings_Registry' ) ) { + // Unregister non-core block bindings. + $block_bindings_registry = WP_Block_Bindings_Registry::get_instance(); + foreach ( $block_bindings_registry->get_all_registered() as $source ) { + if ( 'core/' === substr( $source->name, 0, 5 ) ) { + continue; + } - protected function tearDown(): void { - foreach ( $this->globally_registered_blocks as $block_name ) { - $this->unregister_global_block( $block_name ); + $block_bindings_registry->unregister( $source->name ); + } } parent::tearDown(); @@ -31,31 +41,20 @@ protected function tearDown(): void { /* Helper methods */ - protected function register_block_with_attributes( $block_name, $attributes ) { - $this->registry->register( $block_name, [ - 'apiVersion' => 2, - 'attributes' => $attributes, - ] ); + protected function get_block_registry(): WP_Block_Type_Registry { + return WP_Block_Type_Registry::get_instance(); } - /* Global registrations */ - - protected function register_global_block_with_attributes( $block_name, $attributes ) { - // Use this function for mocking blocks definitions that need to persist across HTTP requests, like GraphQL tests. - - WP_Block_Type_Registry::get_instance()->register( $block_name, [ + protected function register_block_with_attributes( string $block_name, array $attributes, array $additional_args = [] ): void { + $block_type_args = array_merge( [ 'apiVersion' => 2, 'attributes' => $attributes, - ] ); + ], $additional_args ); - $this->globally_registered_blocks[] = $block_name; + $this->get_block_registry()->register( $block_name, $block_type_args ); } - protected function unregister_global_block( $block_name ) { - $registry = WP_Block_Type_Registry::get_instance(); - - if ( $registry->is_registered( $block_name ) ) { - $registry->unregister( $block_name ); - } + protected function register_block_bindings_source( string $source, array $args ): void { + WP_Block_Bindings_Registry::get_instance()->register( $source, $args ); } } diff --git a/tests/rest/test-rest-api.php b/tests/rest/test-rest-api.php index 0955bdb4..bc6b5386 100644 --- a/tests/rest/test-rest-api.php +++ b/tests/rest/test-rest-api.php @@ -8,17 +8,14 @@ namespace WPCOMVIP\BlockDataApi; use Exception; -use WP_Block_Type_Registry; -use WP_UnitTestCase; use WP_REST_Server; use WP_REST_Request; /** * e2e tests to ensure that the REST API endpoint is available. */ -class RestApiTest extends WP_UnitTestCase { +class RestApiTest extends RegistryTestCase { private $server; - private $globally_registered_blocks = []; protected function setUp(): void { parent::setUp(); @@ -34,15 +31,11 @@ protected function tearDown(): void { global $wp_rest_server; $wp_rest_server = null; - foreach ( $this->globally_registered_blocks as $block_name ) { - $this->unregister_global_block( $block_name ); - } - parent::tearDown(); } public function test_rest_api_returns_blocks_for_post() { - $this->register_global_block_with_attributes( 'test/custom-heading', [ + $this->register_block_with_attributes( 'test/custom-heading', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -55,7 +48,7 @@ public function test_rest_api_returns_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-quote', [ + $this->register_block_with_attributes( 'test/custom-quote', [ 'value' => [ 'type' => 'string', 'source' => 'html', @@ -72,7 +65,7 @@ public function test_rest_api_returns_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -88,14 +81,14 @@ public function test_rest_api_returns_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-separator', [ + $this->register_block_with_attributes( 'test/custom-separator', [ 'opacity' => [ 'type' => 'string', 'default' => 'alpha-channel', ], ] ); - $this->register_global_block_with_attributes( 'test/custom-media-text', [ + $this->register_block_with_attributes( 'test/custom-media-text', [ 'align' => [ 'type' => 'string', 'default' => 'none', @@ -245,7 +238,7 @@ public function test_rest_api_returns_blocks_for_post() { } public function test_rest_api_does_not_return_excluded_blocks_for_post() { - $this->register_global_block_with_attributes( 'test/custom-heading', [ + $this->register_block_with_attributes( 'test/custom-heading', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -258,7 +251,7 @@ public function test_rest_api_does_not_return_excluded_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-quote', [ + $this->register_block_with_attributes( 'test/custom-quote', [ 'value' => [ 'type' => 'string', 'source' => 'html', @@ -275,7 +268,7 @@ public function test_rest_api_does_not_return_excluded_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -291,14 +284,14 @@ public function test_rest_api_does_not_return_excluded_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-separator', [ + $this->register_block_with_attributes( 'test/custom-separator', [ 'opacity' => [ 'type' => 'string', 'default' => 'alpha-channel', ], ] ); - $this->register_global_block_with_attributes( 'test/custom-media-text', [ + $this->register_block_with_attributes( 'test/custom-media-text', [ 'align' => [ 'type' => 'string', 'default' => 'none', @@ -426,7 +419,7 @@ public function test_rest_api_does_not_return_excluded_blocks_for_post() { } public function test_rest_api_only_returns_included_blocks_for_post() { - $this->register_global_block_with_attributes( 'test/custom-heading', [ + $this->register_block_with_attributes( 'test/custom-heading', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -439,7 +432,7 @@ public function test_rest_api_only_returns_included_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-quote', [ + $this->register_block_with_attributes( 'test/custom-quote', [ 'value' => [ 'type' => 'string', 'source' => 'html', @@ -456,7 +449,7 @@ public function test_rest_api_only_returns_included_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -472,14 +465,14 @@ public function test_rest_api_only_returns_included_blocks_for_post() { ], ] ); - $this->register_global_block_with_attributes( 'test/custom-separator', [ + $this->register_block_with_attributes( 'test/custom-separator', [ 'opacity' => [ 'type' => 'string', 'default' => 'alpha-channel', ], ] ); - $this->register_global_block_with_attributes( 'test/custom-media-text', [ + $this->register_block_with_attributes( 'test/custom-media-text', [ 'align' => [ 'type' => 'string', 'default' => 'none', @@ -590,7 +583,7 @@ public function test_rest_api_returns_blocks_for_custom_post_type() { 'show_in_rest' => true, ]); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -645,7 +638,7 @@ public function test_rest_api_returns_error_for_non_public_post_type() { 'public' => false, ]); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -681,7 +674,7 @@ public function test_rest_api_returns_error_for_non_rest_post_type() { 'show_in_rest' => false, ]); - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -712,7 +705,7 @@ public function test_rest_api_returns_error_for_non_rest_post_type() { } public function test_rest_api_returns_error_for_unpublished_post() { - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -763,7 +756,7 @@ public function test_rest_api_returns_error_for_classic_content() { } public function test_rest_api_returns_error_for_include_and_exclude_filter() { - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -798,7 +791,7 @@ public function test_rest_api_returns_error_for_include_and_exclude_filter() { } public function test_rest_api_returns_error_for_unexpected_exception() { - $this->register_global_block_with_attributes( 'test/custom-paragraph', [ + $this->register_block_with_attributes( 'test/custom-paragraph', [ 'content' => [ 'type' => 'rich-text', 'source' => 'rich-text', @@ -857,21 +850,4 @@ static function ( int $errno, string $errstr ): never { E_USER_WARNING ); } - - private function register_global_block_with_attributes( $block_name, $attributes ) { - WP_Block_Type_Registry::get_instance()->register( $block_name, [ - 'apiVersion' => 2, - 'attributes' => $attributes, - ] ); - - $this->globally_registered_blocks[] = $block_name; - } - - private function unregister_global_block( $block_name ) { - $registry = WP_Block_Type_Registry::get_instance(); - - if ( $registry->is_registered( $block_name ) ) { - $registry->unregister( $block_name ); - } - } }