diff --git a/includes/Blocks/Block.php b/includes/Blocks/Block.php index 94258282..8a4272b2 100644 --- a/includes/Blocks/Block.php +++ b/includes/Blocks/Block.php @@ -8,6 +8,7 @@ namespace WPGraphQL\ContentBlocks\Blocks; use WPGraphQL\ContentBlocks\Data\BlockAttributeResolver; +use \WPGraphQL\ContentBlocks\Model\Block as BlockModel; use WPGraphQL\ContentBlocks\Registry\Registry; use WPGraphQL\ContentBlocks\Type\Scalar\Scalar; use WPGraphQL\ContentBlocks\Utilities\WPGraphQLHelpers; @@ -108,6 +109,7 @@ private function register_block_attributes_as_fields(): void { 'fields' => $block_attribute_fields, ] ); + register_graphql_field( $this->type_name, 'attributes', @@ -219,8 +221,9 @@ private function get_block_attribute_fields( ?array $block_attributes, string $p $config = [ $attribute_name => $attribute_config, ]; - $result = $this->resolve_block_attributes_recursive( $block['attrs'], wp_unslash( render_block( $block ) ), $config ); + $result = $this->resolve_block_attributes_recursive( $block, $config ); + // Normalize the value. return $result[ $attribute_name ]; }, ]; @@ -325,7 +328,9 @@ private function normalize_attribute_value( $value, $type ) { } /** - * Register the Type for the block. This happens after all other object types are already registered. + * Register the Type for the block. + * + * This happens after all other object types are already registered. */ private function register_type(): void { register_graphql_object_type( @@ -338,9 +343,6 @@ private function register_type(): void { 'name' => [ 'type' => 'String', 'description' => __( 'The name of the block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( $block['blockName'] ) ? (string) $block['blockName'] : null; - }, ], ], ] @@ -368,18 +370,17 @@ private function get_block_attributes_interfaces(): array { /** * Resolved the value of the block attributes based on the specified config * - * @param array $attribute_values The block current attributes value. - * @param string $html The block rendered html. - * @param array $attribute_configs The block current attribute configuration, keyed to the attribute name. + * @param \WPGraphQL\ContentBlocks\Model\Block $block The block model instance. + * @param array $attribute_configs The block current attribute configuration, keyed to the attribute name. */ - private function resolve_block_attributes_recursive( $attribute_values, string $html, array $attribute_configs ): array { + private function resolve_block_attributes_recursive( BlockModel $block, array $attribute_configs ): array { $result = []; // Clean up the html. - $html = trim( $html ); + $html = isset( $block->renderedHtml ) ? trim( $block->renderedHtml ) : ''; foreach ( $attribute_configs as $key => $config ) { - $attribute_value = $attribute_values[ $key ] ?? null; + $attribute_value = $block->parsedAttributes[ $key ] ?? null; $result[ $key ] = BlockAttributeResolver::resolve_block_attribute( $config, $html, $attribute_value ); } diff --git a/includes/Blocks/CoreImage.php b/includes/Blocks/CoreImage.php index caf6c81a..532299c6 100644 --- a/includes/Blocks/CoreImage.php +++ b/includes/Blocks/CoreImage.php @@ -55,8 +55,9 @@ public function __construct( WP_Block_Type $block, Registry $block_registry ) { $this->type_name ), 'resolve' => static function ( $block ) { - $attrs = $block['attrs']; + $attrs = $block->parsedAttributes ?? []; $id = $attrs['id'] ?? null; + if ( $id ) { $media_details = wp_get_attachment_metadata( $id ); if ( ! empty( $media_details ) ) { diff --git a/includes/Data/ContentBlocksResolver.php b/includes/Data/ContentBlocksResolver.php index fa1e75e0..d18303a8 100644 --- a/includes/Data/ContentBlocksResolver.php +++ b/includes/Data/ContentBlocksResolver.php @@ -7,6 +7,7 @@ namespace WPGraphQL\ContentBlocks\Data; +use WPGraphQL\ContentBlocks\Model\Block; use WPGraphQL\Model\Post; /** @@ -20,7 +21,7 @@ final class ContentBlocksResolver { * @param array $args GraphQL query args to pass to the connection resolver. * @param string[] $allowed_block_names The list of allowed block names to filter. * - * @return array The resolved parsed blocks. + * @return \WPGraphQL\ContentBlocks\Model\Block[] */ public static function resolve_content_blocks( $node, $args, $allowed_block_names = [] ): array { /** @@ -39,7 +40,6 @@ public static function resolve_content_blocks( $node, $args, $allowed_block_name $content = null; if ( $node instanceof Post ) { - // @todo: this is restricted intentionally. // $content = $node->contentRaw; @@ -88,7 +88,13 @@ public static function resolve_content_blocks( $node, $args, $allowed_block_name */ $parsed_blocks = apply_filters( 'wpgraphql_content_blocks_resolve_blocks', $parsed_blocks, $node, $args, $allowed_block_names ); - return is_array( $parsed_blocks ) ? $parsed_blocks : []; + // Map the blocks to the Block model + return is_array( $parsed_blocks ) ? array_map( + static function ( $parsed_block ) { + return new Block( new \WP_Block( $parsed_block ) ); + }, + $parsed_blocks + ) : []; } /** diff --git a/includes/Field/BlockSupports/Anchor.php b/includes/Field/BlockSupports/Anchor.php index 4ca1ba02..49ad3409 100644 --- a/includes/Field/BlockSupports/Anchor.php +++ b/includes/Field/BlockSupports/Anchor.php @@ -27,10 +27,12 @@ public static function register(): void { 'type' => 'string', 'description' => __( 'The anchor field for the block.', 'wp-graphql-content-blocks' ), 'resolve' => static function ( $block ) { - $rendered_block = wp_unslash( render_block( $block ) ); + $rendered_block = wp_unslash( $block->renderedHtml ); + if ( empty( $rendered_block ) ) { return null; } + return DOMHelpers::parse_first_node_attribute( $rendered_block, 'id' ); }, ], diff --git a/includes/Model/Block.php b/includes/Model/Block.php new file mode 100644 index 00000000..b2bf64c8 --- /dev/null +++ b/includes/Model/Block.php @@ -0,0 +1,167 @@ + $parsedAttributes + * @property ?string $type + * @property \WP_Block $wpBlock + */ +class Block extends Model { + /** + * The underlying \WP_Block instance for the block data. + * + * @var \WP_Block + */ + protected $data; + + /** + * The rendered block html. + * + * @var ?string + */ + protected $rendered_block; + + /** + * {@inheritDoc} + * + * @param \WP_Block $block The block data to be modeled. + */ + public function __construct( \WP_Block $block ) { + $this->data = $block; + + // Log a Debug message if the block type is not found. + if ( ! $this->data->block_type ) { + graphql_debug( + sprintf( + __( 'Block type not found for block: %s', 'wp-graphql-content-blocks' ), + $this->data->name ?: 'Unknown' + ), + [ + 'parsed_block' => $block, + ] + ); + } + + parent::__construct(); + } + + /** + * {@inheritDoc} + */ + protected function is_private() { + return false; + } + + /** + * {@inheritDoc} + */ + protected function init() { + if ( empty( $this->fields ) ) { + $this->fields = [ + 'clientId' => fn (): ?string => $this->data->parsed_block['clientId'] ?? uniqid(), + 'parentClientId' => fn (): ?string => $this->data->parsed_block['parentClientId'] ?? null, + 'name' => fn (): ?string => $this->data->name ?: null, + 'blockEditorCategoryName' => fn () => isset( $this->data->block_type->category ) ? $this->data->block_type->category : null, + 'isDynamic' => fn (): bool => is_callable( $this->data->block_type->render_callback ), + 'apiVersion' => fn (): ?int => $this->data->block_type->api_version ?: null, + 'cssClassNames' => fn (): ?array => isset( $this->data->attributes['className'] ) ? explode( ' ', $this->data->attributes['className'] ) : null, + 'renderedHtml' => fn (): ?string => $this->get_rendered_block(), + 'innerBlocks' => function (): array { + $block_list = $this->data->inner_blocks ?: []; + + $models_to_return = []; + + foreach ( $block_list as $block ) { + $models_to_return[] = new self( $block ); + } + + return $models_to_return; + }, + 'parsedAttributes' => fn (): array => $this->data->attributes, + 'type' => function (): ?string { + $block_name = $this->name ?? null; + + return isset( $block_name ) ? WPGraphQLHelpers::format_type_name( $block_name ) : null; + }, + 'wpBlock' => function (): \WP_Block { + return $this->data; + }, + ]; + } + } + + /** + * Renders the block html - only once. + * + * The `render_block()` function causes side effects (such as globally-incrementing the counter used for layout styles), so we only want to call it once. + */ + protected function get_rendered_block(): ?string { + if ( ! isset( $this->rendered_block ) ) { + $rendered = $this->data->render(); + $this->rendered_block = do_shortcode( $rendered ); + } + + return $this->rendered_block; + } + + /** + * Resolves the block attributes. + * + * @return array):mixed> + */ + protected function resolve_attributes(): array { + $registered_attributes = $this->data->block_type->attributes ?? []; + + $resolvers = []; + + foreach ( array_keys( $registered_attributes ) as $attribute_name ) { + $resolvers[ $attribute_name ] = fn () => $this->resolve_attribute( $attribute_name ); + } + + return $resolvers; + } + + /** + * Resolves a single block attribute. + * + * @param string $attribute_name The name of the attribute being resolved. + * + * @return mixed + */ + protected function resolve_attribute( string $attribute_name ) { + $attribute_config = $this->data->block_type->attributes[ $attribute_name ] ?? []; + + return BlockAttributeResolver::resolve_block_attribute( $attribute_config, $this->get_rendered_block(), $this->data->attributes[ $attribute_name ] ); + + $allowed_types = [ 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ]; + // If attribute type is set and valid, sanitize value. + if ( isset( $attribute['type'] ) && in_array( $attribute_config['type'], $allowed_types, true ) && rest_validate_value_from_schema( $result, $attribute_config ) ) { + $result = rest_sanitize_value_from_schema( $result, $attribute_config ); + } + + return $result; + } +} diff --git a/includes/Type/InterfaceType/EditorBlockInterface.php b/includes/Type/InterfaceType/EditorBlockInterface.php index d12f86d9..3234499e 100644 --- a/includes/Type/InterfaceType/EditorBlockInterface.php +++ b/includes/Type/InterfaceType/EditorBlockInterface.php @@ -8,7 +8,6 @@ namespace WPGraphQL\ContentBlocks\Type\InterfaceType; use WPGraphQL\ContentBlocks\Data\ContentBlocksResolver; -use WPGraphQL\ContentBlocks\Utilities\WPGraphQLHelpers; use WP_Block_Type_Registry; /** @@ -75,16 +74,10 @@ public static function register_type(): void { 'clientId' => [ 'type' => 'String', 'description' => __( 'The id of the Block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( $block['clientId'] ) ? $block['clientId'] : uniqid(); - }, ], 'parentClientId' => [ 'type' => 'String', 'description' => __( 'The parent id of the Block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( $block['parentClientId'] ) ? $block['parentClientId'] : null; - }, ], 'name' => [ 'type' => 'String', @@ -93,22 +86,16 @@ public static function register_type(): void { 'blockEditorCategoryName' => [ 'type' => 'String', 'description' => __( 'The name of the category the Block belongs to', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( self::get_block( $block )->category ) ? self::get_block( $block )->category : null; - }, ], 'isDynamic' => [ 'type' => [ 'non_null' => 'Boolean' ], 'description' => __( 'Whether the block is Dynamic (server rendered)', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( self::get_block( $block )->render_callback ); - }, ], 'apiVersion' => [ 'type' => 'Integer', 'description' => __( 'The API version of the Gutenberg Block', 'wp-graphql-content-blocks' ), 'resolve' => static function ( $block ) { - return isset( self::get_block( $block )->api_version ) && absint( self::get_block( $block )->api_version ) ? absint( self::get_block( $block )->api_version ) : 2; + return $block->apiVersion ?? 2; }, ], 'innerBlocks' => [ @@ -116,27 +103,14 @@ public static function register_type(): void { 'list_of' => 'EditorBlock', ], 'description' => __( 'The inner blocks of the Block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return isset( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ? $block['innerBlocks'] : []; - }, ], 'cssClassNames' => [ 'type' => [ 'list_of' => 'String' ], 'description' => __( 'CSS Classnames to apply to the block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - if ( isset( $block['attrs']['className'] ) ) { - return explode( ' ', $block['attrs']['className'] ); - } - - return null; - }, ], 'renderedHtml' => [ 'type' => 'String', 'description' => __( 'The rendered HTML for the block', 'wp-graphql-content-blocks' ), - 'resolve' => static function ( $block ) { - return render_block( $block ); - }, ], 'type' => [ 'type' => 'String', @@ -144,7 +118,7 @@ public static function register_type(): void { ], ], 'resolveType' => static function ( $block ) { - return WPGraphQLHelpers::get_type_name_for_block( $block['blockName'] ?? null ); + return $block->type; }, ] ); diff --git a/includes/Type/InterfaceType/PostTypeBlockInterface.php b/includes/Type/InterfaceType/PostTypeBlockInterface.php index f65e4d88..e77e4cbf 100644 --- a/includes/Type/InterfaceType/PostTypeBlockInterface.php +++ b/includes/Type/InterfaceType/PostTypeBlockInterface.php @@ -8,7 +8,6 @@ namespace WPGraphQL\ContentBlocks\Type\InterfaceType; use WPGraphQL\ContentBlocks\Data\ContentBlocksResolver; -use WPGraphQL\ContentBlocks\Utilities\WPGraphQLHelpers; /** * Class PostTypeBlockInterface @@ -36,7 +35,7 @@ public static function register_type( string $post_type, array $block_names = [] ], ], 'resolveType' => static function ( $block ) { - return WPGraphQLHelpers::get_type_name_for_block( $block['blockName'] ?? null ); + return $block->type; }, ] ); diff --git a/includes/Utilities/DOMHelpers.php b/includes/Utilities/DOMHelpers.php index 7f5cec79..4f76b879 100644 --- a/includes/Utilities/DOMHelpers.php +++ b/includes/Utilities/DOMHelpers.php @@ -19,11 +19,10 @@ final class DOMHelpers { * @param string $html The HTML string to parse. * @param string $selector The selector to use. * @param string $attribute The attribute to extract. - * @param mixed $default_value The default value to return if the selector is not found. * * @return ?string extracted attribute */ - public static function parse_attribute( string $html, string $selector, string $attribute, $default_value = null ): ?string { + public static function parse_attribute( string $html, string $selector, string $attribute ): ?string { // Bail early if there's no html to parse. if ( empty( trim( $html ) ) ) { return null; @@ -39,7 +38,6 @@ public static function parse_attribute( string $html, string $selector, string $ } $nodes = $doc->find( $selector ); - $default_value = isset( $default_value ) ? $default_value : null; foreach ( $nodes as $node ) { if ( $node->hasAttribute( $attribute ) ) { @@ -47,7 +45,7 @@ public static function parse_attribute( string $html, string $selector, string $ } } - return $default_value; + return null; } /** @@ -240,7 +238,7 @@ public static function find_nodes( string $html, ?string $selector = null ) { public static function parseAttribute( $html, $selector, $attribute, $default_value = null ): ?string { _deprecated_function( __METHOD__, '4.2.0', self::class . '::parse_attribute' ); - return self::parse_attribute( $html, $selector, $attribute, $default_value ); + return self::parse_attribute( $html, $selector, $attribute ) ?: $default_value; } /** diff --git a/tests/unit/ContentBlocksResolverTest.php b/tests/unit/ContentBlocksResolverTest.php index e9331a02..982551bb 100644 --- a/tests/unit/ContentBlocksResolverTest.php +++ b/tests/unit/ContentBlocksResolverTest.php @@ -103,7 +103,7 @@ public function test_resolve_content_blocks_resolves_reusable_blocks() { // There should return only the non-empty blocks $this->assertEquals( 3, count( $actual ) ); - $this->assertEquals( 'core/columns', $actual[0]['blockName'] ); + $this->assertEquals( 'core/columns', $actual[0]->name ); } public function test_resolve_content_blocks_filters_empty_blocks() { @@ -111,32 +111,32 @@ public function test_resolve_content_blocks_filters_empty_blocks() { $actual = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ] ); // There should return only the non-empty blocks $this->assertEquals( 6, count( $actual ) ); - $this->assertEquals( 'core/columns', $actual[0]['blockName'] ); + $this->assertEquals( 'core/columns', $actual[0]->name ); } public function test_resolve_content_blocks_resolves_classic_blocks() { $post_model = new Post( get_post( $this->post_id ) ); $actual = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ] ); - $this->assertEquals( 'core/freeform', $actual[5]['blockName'] ); + $this->assertEquals( 'core/freeform', $actual[5]->name ); } public function test_resolve_content_blocks_filters_blocks_not_from_allow_list() { $post_model = new Post( get_post( $this->post_id ) ); $allowed = [ 'core/column', 'core/paragraph' ]; - $parsed_blocks = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ], $allowed ); + $actual = $this->instance->resolve_content_blocks( $post_model, [ 'flat' => true ], $allowed ); $actual_block_names = array_values( array_unique( array_map( - static function ( $parsed_block ) { - return $parsed_block['blockName']; + static function ( $block ) { + return $block->name; }, - $parsed_blocks, + $actual, ) ) ); // There should return only blocks from the allow list - $this->assertEquals( 4, count( $parsed_blocks ) ); + $this->assertEquals( 4, count( $actual ) ); $this->assertEquals( $allowed, $actual_block_names ); } @@ -205,7 +205,7 @@ static function ( $blocks, $node, $args, $allowed_block_names ) { // The block should be resolved from the post node. $this->assertCount( 1, $resolved_blocks ); - $this->assertEquals( 'core/test-filter', $resolved_blocks[0]['blockName'] ); + $this->assertEquals( 'core/test-filter', $resolved_blocks[0]->name ); // Cleanup. remove_all_filters( 'wpgraphql_content_blocks_resolve_blocks' ); @@ -254,27 +254,27 @@ public function test_inner_blocks() { $resolved_blocks = $this->instance->resolve_content_blocks( $post, [ 'flat' => false ] ); $this->assertCount( 1, $resolved_blocks, 'There should be only one top-level block (columns).' ); - $this->assertEquals( 'core/columns', $resolved_blocks[0]['blockName'] ); - $this->assertNotEmpty( $resolved_blocks[0]['clientId'], 'The clientId should be set.' ); - $this->assertArrayNotHasKey( 'parentClientId', $resolved_blocks[0], 'The parentClientId should be empty.' ); + $this->assertEquals( 'core/columns', $resolved_blocks[0]->name, 'The top-level block should be columns.' ); + $this->assertNotEmpty( $resolved_blocks[0]->clientId, 'The clientId should be set.' ); + $this->assertEmpty( $resolved_blocks[0]->parentClientId, 'The parentClientId should be empty.' ); - $this->assertCount( 2, $resolved_blocks[0]['innerBlocks'], 'There should be two inner blocks (columns).' ); + $this->assertCount( 2, $resolved_blocks[0]->innerBlocks, 'There should be two inner blocks (columns).' ); // Check the inner blocks. - $expected_parent_client_id = $resolved_blocks[0]['clientId']; + $expected_parent_client_id = $resolved_blocks[0]->clientId; - foreach ( $resolved_blocks[0]['innerBlocks'] as $inner_block ) { - $this->assertEquals( 'core/column', $inner_block['blockName'] ); - $this->assertCount( 2, $inner_block['innerBlocks'], 'There should be two inner blocks (column).' ); - $this->assertNotEmpty( $inner_block['clientId'], 'The clientId should be set.' ); - $this->assertArrayNotHasKey( 'parentClientId', $resolved_blocks[0], 'The parentClientId should only be set when flattening.' ); // @todo This is incorrect, the parentClientId should be set for nested blocks. + foreach ( $resolved_blocks[0]->innerBlocks as $inner_block ) { + $this->assertEquals( 'core/column', $inner_block->name, 'The inner block should be a column.' ); + $this->assertCount( 2, $inner_block->innerBlocks, 'There should be two inner blocks (column).' ); + $this->assertNotEmpty( $inner_block->clientId, 'The clientId should be set.' ); + $this->assertEmpty( $resolved_blocks[0]->parentClientId, 'The parentClientId should only be set when flattening.' ); // @todo This is incorrect, the parentClientId should be set for nested blocks. // Check the inner inner blocks. - $expected_parent_client_id = $inner_block['clientId']; + $expected_parent_client_id = $inner_block->clientId; - foreach ( $inner_block['innerBlocks'] as $inner_inner_block ) { - $this->assertNotEmpty( $inner_inner_block['clientId'], 'The clientId should be set.' ); - $this->assertArrayNotHasKey( 'parentClientId', $resolved_blocks[0], 'The parentClientId should only be set when flattening.' ); // @todo This is incorrect, the parentClientId should be set for nested blocks. + foreach ( $inner_block->innerBlocks as $inner_inner_block ) { + $this->assertNotEmpty( $inner_inner_block->clientId, 'The clientId should be set.' ); + $this->assertEmpty( $resolved_blocks[0]->parentClientId, 'The parentClientId should only be set when flattening.' ); // @todo This is incorrect, the parentClientId should be set for nested blocks. } } @@ -287,40 +287,40 @@ public function test_inner_blocks() { $this->assertCount( 7, $resolved_blocks, 'There should be five blocks when flattened.' ); // Check the top-level block (columns). - $this->assertNotEmpty( $resolved_blocks[0]['clientId'], 'The clientId should be set.' ); - $this->assertEqualBlocks( $expected_blocks[0], $resolved_blocks[0], 'The top-level block should match.' ); + $this->assertNotEmpty( $resolved_blocks[0]->clientId, 'The clientId should be set.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->wpBlock->attributes, $resolved_blocks[0]->wpBlock->attributes, 'The top-level block should match.' ); // Check first inner block (column). - $expected_parent_client_id = $resolved_blocks[0]['clientId']; - $this->assertNotEmpty( $resolved_blocks[1]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[1]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][0], $resolved_blocks[1], 'The first inner block should match.' ); + $expected_parent_client_id = $resolved_blocks[0]->clientId; + $this->assertNotEmpty( $resolved_blocks[1]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[1]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[0]->wpBlock->attributes, $resolved_blocks[1]->wpBlock->attributes, 'The first inner block should match.' ); // Check first inner block children. - $expected_parent_client_id = $resolved_blocks[1]['clientId']; - $this->assertNotEmpty( $resolved_blocks[2]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[2]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][0]['innerBlocks'][0], $resolved_blocks[2], 'The first inner inner block should match.' ); + $expected_parent_client_id = $resolved_blocks[1]->clientId; + $this->assertNotEmpty( $resolved_blocks[2]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[2]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[0]->innerBlocks[0]->wpBlock->attributes, $resolved_blocks[2]->wpBlock->attributes, 'The first inner inner block should match.' ); - $this->assertNotEmpty( $resolved_blocks[3]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[3]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][0]['innerBlocks'][1], $resolved_blocks[3], 'The second inner inner block should match.' ); + $this->assertNotEmpty( $resolved_blocks[3]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[3]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[0]->innerBlocks[1]->wpBlock->attributes, $resolved_blocks[3]->wpBlock->attributes, 'The second inner inner block should match.' ); // Check second inner block (column). - $expected_parent_client_id = $resolved_blocks[0]['clientId']; - $this->assertNotEmpty( $resolved_blocks[4]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[4]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][1], $resolved_blocks[4], 'The first inner block should match.' ); + $expected_parent_client_id = $resolved_blocks[0]->clientId; + $this->assertNotEmpty( $resolved_blocks[4]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[4]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[1]->wpBlock->attributes, $resolved_blocks[4]->wpBlock->attributes, 'The first inner block should match.' ); // Check second inner block children. - $expected_parent_client_id = $resolved_blocks[4]['clientId']; - $this->assertNotEmpty( $resolved_blocks[5]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[5]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][1]['innerBlocks'][0], $resolved_blocks[5], 'The first inner inner block should match.' ); - - $this->assertNotEmpty( $resolved_blocks[6]['clientId'], 'The clientId should be set.' ); - $this->assertEquals( $expected_parent_client_id, $resolved_blocks[6]['parentClientId'], 'The parentClientId should match.' ); - $this->assertEqualBlocks( $expected_blocks[0]['innerBlocks'][1]['innerBlocks'][1], $resolved_blocks[6], 'The second inner inner block should match.' ); + $expected_parent_client_id = $resolved_blocks[4]->clientId; + $this->assertNotEmpty( $resolved_blocks[5]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[5]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[1]->innerBlocks[0]->wpBlock->attributes, $resolved_blocks[5]->wpBlock->attributes, 'The first inner inner block should match.' ); + + $this->assertNotEmpty( $resolved_blocks[6]->clientId, 'The clientId should be set.' ); + $this->assertEquals( $expected_parent_client_id, $resolved_blocks[6]->parentClientId, 'The parentClientId should match.' ); + $this->assertEqualBlockAttributes( $expected_blocks[0]->innerBlocks[1]->innerBlocks[1]->wpBlock->attributes, $resolved_blocks[6]->wpBlock->attributes, 'The second inner inner block should match.' ); } /** @@ -330,7 +330,7 @@ public function test_inner_blocks() { * @param array $actual The actual block. * @param string $message The message to display if the assertion fails. */ - protected function assertEqualBlocks( $expected, $actual, $message = '' ) { + protected function assertEqualBlockAttributes( $expected, $actual, $message = '' ) { // Remove clientId and parentClientId from comparison. unset( $expected['clientId'] ); unset( $expected['parentClientId'] ); diff --git a/tests/unit/DOMHelpersTest.php b/tests/unit/DOMHelpersTest.php index ba2abd4d..5a2cc325 100644 --- a/tests/unit/DOMHelpersTest.php +++ b/tests/unit/DOMHelpersTest.php @@ -21,7 +21,6 @@ public function testParseAttribute(): void { // $html $this->assertNull( DOMHelpers::parse_attribute( '', $no_existent_selector, $data_attribute ) ); $this->assertNull( DOMHelpers::parse_attribute( $html, $no_existent_selector, $data_attribute ) ); - $this->assertEquals( 'Bar', DOMHelpers::parse_attribute( $html, $no_existent_selector, $data_attribute, 'Bar' ) ); $this->assertEquals( 'foo-data', DOMHelpers::parse_attribute( $html, $id_selector, $data_attribute ) ); $this->assertEquals( 'foo-class', DOMHelpers::parse_attribute( $html, $id_selector, $class_attribute ) ); $this->assertEquals( 'foo-id', DOMHelpers::parse_attribute( $html, $id_selector, $id_attribute ) ); @@ -36,7 +35,6 @@ public function testParseAttribute(): void { $this->assertEquals( 'center', DOMHelpers::parse_attribute( $html2, '*', 'data-align' ) ); $this->assertEquals( 'right', DOMHelpers::parse_attribute( $html2, '.has-text-align-right', 'data-align' ) ); $this->assertNull( DOMHelpers::parse_attribute( $html2, '.non-existent-class', 'data-align' ) ); - $this->assertEquals( 'default', DOMHelpers::parse_attribute( $html2, '.non-existent-class', 'data-align', 'default' ) ); // $htm3 $this->assertEquals( 'left', DOMHelpers::parse_attribute( $html3, 'span', 'data-align' ) );