From 3a1157b23703af176fc55035583b67410a505bfd Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Thu, 26 Sep 2024 12:39:16 +0300 Subject: [PATCH] fix: Correctly parse nested attribute and tag sources. (#293) * fix: php error for null $attribute_value in parse_query_source() * fix: resolve missing bool/rich-text attributes * tests: backfill tests for `CoreTable` attributes * tests: CoreTableAttributes.hasFixedLayout defaults true in 6.6 * fix: Correctly parse nested attribute and tag sources. * test: make CoreTableTest compatible with WP6.1 --------- Co-authored-by: DDEV User --- .changeset/neat-books-kick.md | 5 + .changeset/pink-months-flash.md | 5 + includes/Blocks/Block.php | 10 +- includes/Data/BlockAttributeResolver.php | 53 +- includes/Utilities/DOMHelpers.php | 27 + tests/unit/CoreTableTest.php | 714 +++++++++++++++++++++-- tests/unit/CoreVideoTest.php | 6 +- 7 files changed, 750 insertions(+), 70 deletions(-) create mode 100644 .changeset/neat-books-kick.md create mode 100644 .changeset/pink-months-flash.md diff --git a/.changeset/neat-books-kick.md b/.changeset/neat-books-kick.md new file mode 100644 index 00000000..202bcfd7 --- /dev/null +++ b/.changeset/neat-books-kick.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wp-graphql-content-blocks": patch +--- + +fix: Correctly parse nested attribute and tag sources. diff --git a/.changeset/pink-months-flash.md b/.changeset/pink-months-flash.md new file mode 100644 index 00000000..72d261dc --- /dev/null +++ b/.changeset/pink-months-flash.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wp-graphql-content-blocks": patch +--- + +tests: backfill tests for `CoreTable` attributes. diff --git a/includes/Blocks/Block.php b/includes/Blocks/Block.php index 85360087..94258282 100644 --- a/includes/Blocks/Block.php +++ b/includes/Blocks/Block.php @@ -279,9 +279,10 @@ private function create_attributes_fields( $attributes, $prefix ): array { $name, $prefix ), - 'resolve' => function ( $attributes ) use ( $name, $default_value ) { + 'resolve' => function ( $attributes ) use ( $name, $default_value, $type ) { $value = $attributes[ $name ] ?? $default_value; - return $this->normalize_attribute_value( $value, $attributes['__type'][ $name ]['type'] ); + + return $this->normalize_attribute_value( $value, $type ); }, ]; } @@ -305,10 +306,11 @@ private function normalize_attribute_value( $value, $type ) { } switch ( $type ) { - case 'rich-text': case 'array': // If we're here, we want an array type, even though the value is not an array. - return isset( $value ) ? [ $value ] : []; + // @todo This should return null if the value is empty. + return ! empty( $value ) ? [ $value ] : []; + case 'rich-text': case 'string': return (string) $value; case 'number': diff --git a/includes/Data/BlockAttributeResolver.php b/includes/Data/BlockAttributeResolver.php index bd6c2453..cd8e0dc7 100644 --- a/includes/Data/BlockAttributeResolver.php +++ b/includes/Data/BlockAttributeResolver.php @@ -28,15 +28,16 @@ public static function resolve_block_attribute( $attribute, string $html, $attri $value = null; if ( isset( $attribute['source'] ) ) { + // @todo parse remaining sources: https://github.com/WordPress/gutenberg/blob/trunk/packages/blocks/src/api/parser/get-block-attributes.js#L198 switch ( $attribute['source'] ) { case 'attribute': $value = self::parse_attribute_source( $html, $attribute ); break; case 'html': case 'rich-text': - // If there is no selector, we are dealing with single source. + // If there is no selector, the source is the node's innerHTML. if ( ! isset( $attribute['selector'] ) ) { - $value = self::parse_single_source( $html, $attribute['source'] ); + $value = ! empty( $html ) ? DOMHelpers::find_nodes( $html )->innerHTML() : null; break; } $value = self::parse_html_source( $html, $attribute ); @@ -47,6 +48,9 @@ public static function resolve_block_attribute( $attribute, string $html, $attri case 'query': $value = self::parse_query_source( $html, $attribute, $attribute_value ); break; + case 'tag': + $value = self::parse_tag_source( $html ); + break; case 'meta': $value = self::parse_meta_source( $attribute ); break; @@ -59,14 +63,15 @@ public static function resolve_block_attribute( $attribute, string $html, $attri $value = intval( $value ); break; case 'boolean': - $value = ! empty( $value ); + // Boolean attributes can be an empty string. + $value = ( ! is_array( $value ) && isset( $value ) ) || ! empty( $value ); break; } } } // Fallback to the attributes or default value if the result is empty. - if ( empty( $value ) ) { + if ( null === $value ) { $default = $attribute['default'] ?? null; $value = $attribute_value ?? $default; @@ -75,25 +80,6 @@ public static function resolve_block_attribute( $attribute, string $html, $attri return $value; } - /** - * Parses the block content of a source only block type - * - * @param string $html The html value - * @param string $source The source type - */ - private static function parse_single_source( string $html, $source ): ?string { - if ( empty( $html ) ) { - return null; - } - - switch ( $source ) { - case 'html': - return DOMHelpers::find_nodes( $html )->innerHTML(); - } - - return null; - } - /** * Parses the block content of an HTML source block type. * @@ -124,11 +110,11 @@ private static function parse_html_source( string $html, array $config ): ?strin * @param array $config The value configuration. */ private static function parse_attribute_source( string $html, array $config ): ?string { - if ( empty( $html ) || ! isset( $config['selector'] ) || ! isset( $config['attribute'] ) ) { + if ( empty( $html ) || ! isset( $config['attribute'] ) ) { return null; } - return DOMHelpers::parse_attribute( $html, $config['selector'], $config['attribute'] ); + return DOMHelpers::parse_attribute( $html, $config['selector'] ?? '', $config['attribute'] ); } /** @@ -148,13 +134,13 @@ private static function parse_text_source( string $html, $config ): ?string { /** * Parses a query source block type. * - * @param string $html The html value. - * @param array $config The value configuration. - * @param array $attribute_values The attribute values for the block. + * @param string $html The html value. + * @param array $config The value configuration. + * @param ?array $attribute_values The attribute values for the block. * * @return ?mixed[] */ - private static function parse_query_source( string $html, array $config, array $attribute_values ): ?array { + private static function parse_query_source( string $html, array $config, ?array $attribute_values ): ?array { if ( ! isset( $config['selector'] ) || ! isset( $config['query'] ) ) { return null; } @@ -203,4 +189,13 @@ private static function parse_meta_source( array $config ): ?string { return get_post_meta( $post_id, $config['meta'], true ); } + + /** + * Parses a tag source. + * + * @param string $html The html value. + */ + private static function parse_tag_source( string $html ): ?string { + return DOMHelpers::get_first_node_tag_name( $html ); + } } diff --git a/includes/Utilities/DOMHelpers.php b/includes/Utilities/DOMHelpers.php index 8a0bb075..7f5cec79 100644 --- a/includes/Utilities/DOMHelpers.php +++ b/includes/Utilities/DOMHelpers.php @@ -164,6 +164,33 @@ public static function parse_text( string $html, string $selector ): ?string { return $nodes[0]->text(); } + /** + * Gets the tag name of the first node. + * + * @internal This method should only be used internally. There are no guarantees for backwards compatibility. + * + * @param string $html The HTML string to parse. + */ + public static function get_first_node_tag_name( string $html ): ?string { + // Bail early if there's no html to parse. + if ( empty( trim( $html ) ) ) { + return null; + } + + $doc = new Document(); + $doc->loadHtml( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); + + /** @var \DiDom\Element[] $nodes */ + $nodes = $doc->find( '*' ); + + if ( count( $nodes ) === 0 ) { + return null; + } + + // Lowercase the tag name. + return strtolower( $nodes[0]->tagName() ); + } + /** * Parses the html into DOMElement and searches the DOM tree for a given XPath expression or CSS selector. * diff --git a/tests/unit/CoreTableTest.php b/tests/unit/CoreTableTest.php index 56dee256..351cb997 100644 --- a/tests/unit/CoreTableTest.php +++ b/tests/unit/CoreTableTest.php @@ -3,29 +3,15 @@ namespace WPGraphQL\ContentBlocks\Unit; final class CoreTableTest extends PluginTestCase { - public $instance; - public $post_id; + private $post_id; public function setUp(): void { parent::setUp(); $this->post_id = wp_insert_post( [ - 'post_title' => 'Post Title', - 'post_content' => preg_replace( - '/\s+/', - ' ', - trim( - ' - -
- -
Header 1Header 2
Footer 1Footer 2
-
Caption
- - ' - ) - ), + 'post_title' => 'Table', + 'post_content' => '', 'post_status' => 'publish', ] ); @@ -40,42 +26,702 @@ public function tearDown(): void { parent::tearDown(); } - public function test_retrieve_core_table_attribute_fields() { - $query = ' + private function query(): string { + return ' fragment CoreTableBlockFragment on CoreTable { attributes { - caption align anchor + backgroundColor + body { + cells { + align + content + scope + tag + } + } + borderColor + caption + className + fontFamily + fontSize + foot { + cells { + align + content + scope + tag + } + } + gradient + hasFixedLayout + head { + cells { + align + content + scope + tag + } + } + lock + # metadata + style + textColor } } - - query GetPosts { - posts(first: 1) { - nodes { - editorBlocks { + query Post( $id: ID! ) { + post(id: $id, idType: DATABASE_ID) { + databaseId + editorBlocks { + apiVersion + blockEditorCategoryName + clientId + cssClassNames + innerBlocks { name - ...CoreTableBlockFragment } + isDynamic + name + parentClientId + renderedHtml + ... on BlockWithSupportsAnchor { + anchor + } + ...CoreTableBlockFragment } } } '; + } + + /** + * Test that the CoreTable block can be retrieved and basic fields are present. + * + * Tested attributes: + * - caption + * - hasFixedLayout + * - body > cells: + * - content + * - tag + */ + public function test_retrieve_core_table_attribute_fields() { + $block_content = ' + +
Cell 1Cell 2
Cell 3Cell 4
Caption
+ + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $block_content, + ] + ); + + $query = $this->query(); + $variables = [ + 'id' => $this->post_id, + ]; + + $actual = graphql( compact( 'query', 'variables' ) ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'There should not be any errors' ); + $this->assertArrayHasKey( 'data', $actual, 'The data key should be present' ); + $this->assertArrayHasKey( 'post', $actual['data'], 'The post key should be present' ); + + $this->assertEquals( $this->post_id, $actual['data']['post']['databaseId'], 'The post ID should match' ); + + $this->assertEquals( 1, count( $actual['data']['post']['editorBlocks'] ) ); + + $block = $actual['data']['post']['editorBlocks'][0]; - $actual = graphql( [ 'query' => $query ] ); + $this->assertNotEmpty( $block['apiVersion'], 'The apiVersion should be present' ); + $this->assertEquals( 'text', $block['blockEditorCategoryName'], 'The blockEditorCategoryName should be text' ); + $this->assertNotEmpty( $block['clientId'], 'The clientId should be present' ); + + // @todo this is not working + // $this->assertNotEmpty( $block['cssClassNames'], 'The cssClassNames should be present' ); + + $this->assertEmpty( $block['innerBlocks'], 'There should be no inner blocks' ); + $this->assertEquals( 'core/table', $block['name'], 'The block name should be core/table' ); + $this->assertEmpty( $block['parentClientId'], 'There should be no parentClientId' ); + $this->assertNotEmpty( $block['renderedHtml'], 'The renderedHtml should be present' ); + + // We'll test the body cells separately. + $body = $block['attributes']['body']; + unset( $block['attributes']['body'] ); + + $this->assertEquals( + [ + 'align' => null, + 'anchor' => null, + 'backgroundColor' => null, + 'borderColor' => null, + 'caption' => 'Caption', + 'className' => null, + 'fontFamily' => null, + 'fontSize' => null, + 'foot' => [], + 'gradient' => null, + 'hasFixedLayout' => is_wp_version_compatible( '6.6' ) ? true : false, // WP 6.6 changes the unset default value to true. + 'head' => [], + 'lock' => null, + 'style' => null, + 'textColor' => null, + ], + $block['attributes'] + ); + + // Test the body cells. + $this->assertNotEmpty( $body, 'The body should have cells' ); + $this->assertCount( 2, $body, 'There should be 2 rows' ); + + // Test the first row + $this->assertCount( 2, $body[0]['cells'], 'There should be 2 cells in the first row' ); + $this->assertEquals( + [ + 'align' => null, + 'content' => 'Cell 1', + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][0], + 'The first cell in the first row does not match' + ); + $this->assertEquals( + [ // @todo These should be filled in + 'align' => null, + 'content' => 'Cell 2', + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][1], + 'The second cell in the first row does not match' + ); + + // Test the second row + $this->assertCount( 2, $body[1]['cells'], 'There should be 2 cells in the second row' ); + $this->assertEquals( + [ + 'align' => null, + 'content' => 'Cell 3', + 'scope' => null, + 'tag' => 'td', + ], + $body[1]['cells'][0], + 'The first cell in the second row does not match' + ); + $this->assertEquals( + [ + 'align' => null, + 'content' => 'Cell 4', + 'scope' => null, + 'tag' => 'td', + ], + $body[1]['cells'][1], + 'The second cell in the second row does not match' + ); + } + + /** + * Tests additional attributes of the CoreTable block along with the header and footer cells. + * + * Tested attributes: + * - align + * - borderColor + * - fontFamily + * - fontSize + * - style + * - body > cells > align + * - head > cells: + * - align + * - content + * - tag + * - foot > cells: + * - align + * - content + * - tag + */ + public function test_retrieve_core_table_attribute_fields_header_footer() { + $block_content = ' + +
Header labelHeader label
This column has "align column left"This column has "align column center"
Cell 3Cell 4
Footer labelFooter label
Caption
+ + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $block_content, + ] + ); + + $query = $this->query(); + $variables = [ + 'id' => $this->post_id, + ]; + + $actual = graphql( compact( 'query', 'variables' ) ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'There should not be any errors' ); + $this->assertArrayHasKey( 'data', $actual, 'The data key should be present' ); + $this->assertArrayHasKey( 'post', $actual['data'], 'The post key should be present' ); + + $this->assertEquals( $this->post_id, $actual['data']['post']['databaseId'], 'The post ID should match' ); + + $block = $actual['data']['post']['editorBlocks'][0]; + + // We'll test the cells separately. + $body = $block['attributes']['body']; + $head = $block['attributes']['head']; + $foot = $block['attributes']['foot']; + unset( $block['attributes']['body'] ); + unset( $block['attributes']['head'] ); + unset( $block['attributes']['foot'] ); + + $this->assertEquals( + [ + 'align' => 'wide', // Previously untested + 'anchor' => null, + 'backgroundColor' => null, + 'borderColor' => 'accent-4', // Previously untested + 'caption' => 'Caption', + 'className' => null, + 'fontFamily' => 'system-serif', // Previously untested + 'fontSize' => 'medium', // Previously untested + 'gradient' => null, + 'hasFixedLayout' => false, + 'lock' => null, + 'style' => wp_json_encode( + [ // Previously untested + 'border' => [ + 'width' => '1px', + ], + ] + ), + 'textColor' => null, + ], + $block['attributes'], + 'The block attributes do not match' + ); + + // Test the head cells. + $this->assertNotEmpty( $head, 'The head should have cells' ); + $this->assertCount( 1, $head, 'There should be 1 row in the head' ); + $this->assertCount( 2, $head[0]['cells'], 'There should be 2 cells in the head' ); + + $this->assertEquals( // Previously untested + [ + 'align' => 'left', + 'content' => 'Header label', + 'scope' => null, + 'tag' => 'th', + ], + $head[0]['cells'][0], + 'The first cell in the head does not match' + ); + $this->assertEquals( + [ + 'align' => 'right', + 'content' => 'Header label', + 'scope' => null, + 'tag' => 'th', + ], + $head[0]['cells'][1], + 'The second cell in the head does not match' + ); + + // Test the body cells. + $this->assertNotEmpty( $body, 'The body should have cells' ); + $this->assertCount( 2, $body, 'There should be 2 rows' ); + + // Test the left cell. + $this->assertEquals( + [ + 'align' => 'left', // Previously untested + 'content' => 'This column has "align column left"', + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][0], + 'The first cell in the first row does not match' + ); + + // Test right cell. + $this->assertEquals( + [ + 'align' => 'right', + 'content' => 'This column has "align column center"', + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][1], + 'The second cell in the first row does not match' + ); + + // Test the foot cells. + $this->assertNotEmpty( $foot, 'The foot should have cells' ); + $this->assertCount( 1, $foot, 'There should be 1 row in the foot' ); + $this->assertCount( 2, $foot[0]['cells'], 'There should be 2 cells in the foot' ); + + $this->assertEquals( // Previously untested + [ + 'align' => 'left', + 'content' => 'Footer label', + 'scope' => null, + 'tag' => 'td', + ], + $foot[0]['cells'][0], + 'The first cell in the foot does not match' + ); + + $this->assertEquals( + [ + 'align' => 'right', + 'content' => 'Footer label', + 'scope' => null, + 'tag' => 'td', + ], + $foot[0]['cells'][1], + 'The second cell in the foot does not match' + ); + } + + /** + * Tests additional style attributes of the CoreTable block. + * + * Tested attributes: + * - backgroundColor + * - className + * - textColor + */ + public function test_retrieve_core_table_attribute_styles() { + $block_content = ' + +
Caption
+ + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $block_content, + ] + ); + + $query = $this->query(); + $variables = [ + 'id' => $this->post_id, + ]; + + $actual = graphql( compact( 'query', 'variables' ) ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'There should not be any errors' ); + $this->assertArrayHasKey( 'data', $actual, 'The data key should be present' ); + $this->assertArrayHasKey( 'post', $actual['data'], 'The post key should be present' ); + + $this->assertEquals( $this->post_id, $actual['data']['post']['databaseId'], 'The post ID should match' ); + + $block = $actual['data']['post']['editorBlocks'][0]; + + // No need to test the cells again. + unset( $block['attributes']['body'] ); + unset( $block['attributes']['head'] ); + unset( $block['attributes']['foot'] ); + + $this->assertEquals( + [ + 'align' => null, + 'anchor' => null, + 'backgroundColor' => 'base', // Previously untested + 'borderColor' => null, + 'caption' => 'Caption', + 'className' => 'is-style-stripes', // Previously untested + 'fontFamily' => null, + 'fontSize' => null, + 'gradient' => null, + 'hasFixedLayout' => false, + 'lock' => null, + 'style' => wp_json_encode( + [ + 'elements' => [ + 'link' => [ + 'color' => [ + 'text' => 'var:preset|color|vivid-red', + ], + ], + ], + ] + ), + 'textColor' => 'vivid-red', // Previously untested + ], + $block['attributes'], + 'The block attributes do not match' + ); + } + + /** + * Tests the `lock` and gradient attributes of the CoreTable block. + * + * Tested attributes: + * - lock + * - gradient + */ + public function test_retrieve_core_table_attribute_lock_gradient(): void { + $block_content = ' + +
Header labelHeader label
Cell 1Cell 2
Cell 3Cell 4
Footer labelFooter label
Caption
+ + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $block_content, + ] + ); + + $query = $this->query(); + $variables = [ + 'id' => $this->post_id, + ]; + + $actual = graphql( compact( 'query', 'variables' ) ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'There should not be any errors' ); + $this->assertArrayHasKey( 'data', $actual, 'The data key should be present' ); + $this->assertArrayHasKey( 'post', $actual['data'], 'The post key should be present' ); + + $this->assertEquals( $this->post_id, $actual['data']['post']['databaseId'], 'The post ID should match' ); + + $block = $actual['data']['post']['editorBlocks'][0]; + + // No need to test the cells again. + unset( $block['attributes']['body'] ); + unset( $block['attributes']['head'] ); + unset( $block['attributes']['foot'] ); + + $this->assertEquals( + [ + 'align' => null, + 'anchor' => null, + 'backgroundColor' => null, + 'borderColor' => null, + 'caption' => 'Caption', + 'className' => null, + 'fontFamily' => null, + 'fontSize' => null, + 'gradient' => 'gradient-3', // Previously untested + 'hasFixedLayout' => false, + 'lock' => wp_json_encode( + [ // Previously untested + 'move' => true, + 'remove' => false, + ] + ), + 'style' => wp_json_encode( + [ + 'typography' => [ + 'letterSpacing' => '7px', + ], + ] + ), + 'textColor' => null, + ], + $block['attributes'], + 'The block attributes do not match' + ); + } + + /** + * Test custom cell markup in the CoreTable block. + * + * Tested attributes: + * - body > cells: + * - colspan + * - rowspan + * - scope + * - foot > cells: + * - colspan + * - rowspan + * - scope + * - head > cells: + * - colspan + * - rowspan + * - scope + */ + public function test_retrieve_core_table_custom_cell_markup(): void { + // colspan and rowspan are only supported in WP 6.2+. + if ( ! is_wp_version_compatible( '6.2' ) ) { + $this->markTestSkipped( 'This test requires WP 6.2 or higher.' ); + } + + $block_markup = ' + +
Header labelHeader label
Cell 1Cell 2
Cell 3Cell 4
Footer label
Caption
+ + '; + + wp_update_post( + [ + 'ID' => $this->post_id, + 'post_content' => $block_markup, + ] + ); + + $query = ' + fragment CoreTableBlockFragment on CoreTable { + attributes { + body { + cells { + align + colspan + content + rowspan + scope + tag + } + } + foot { + cells { + align + colspan + content + rowspan + scope + tag + } + } + head { + cells { + align + colspan + content + rowspan + scope + tag + } + } + } + } + query Post( $id: ID! ) { + post(id: $id, idType: DATABASE_ID) { + databaseId + editorBlocks { + ...CoreTableBlockFragment + } + } + } + '; + + $variables = [ + 'id' => $this->post_id, + ]; + + $actual = graphql( compact( 'query', 'variables' ) ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'There should not be any errors' ); + $this->assertArrayHasKey( 'data', $actual, 'The data key should be present' ); + $this->assertArrayHasKey( 'post', $actual['data'], 'The post key should be present' ); + + $this->assertEquals( $this->post_id, $actual['data']['post']['databaseId'], 'The post ID should match' ); + + $block = $actual['data']['post']['editorBlocks'][0]; + + // We only need to test the cells. + $body = $block['attributes']['body']; + $head = $block['attributes']['head']; + $foot = $block['attributes']['foot']; + + // No need to test the cells again. + + // Test the head cells. + $this->assertNotEmpty( $head, 'The head should have cells' ); + $this->assertCount( 1, $head, 'There should be 1 row in the head' ); + $this->assertCount( 2, $head[0]['cells'], 'There should be 2 cells in the head' ); + + // Test the first cell in the head. + $this->assertEquals( + [ + 'align' => null, + 'colspan' => 2, // Previously untested + 'content' => 'Header label', + 'rowspan' => null, + 'scope' => 'col', // Previously untested + 'tag' => 'th', + ], + $head[0]['cells'][0], + 'The first cell in the head does not match' + ); + + // Test the second cell in the head. + $this->assertEquals( + [ + 'align' => null, + 'colspan' => null, + 'content' => 'Header label', + 'rowspan' => null, + 'scope' => null, + 'tag' => 'th', + ], + $head[0]['cells'][1], + 'The second cell in the head does not match' + ); + + // Test the body cells. + $this->assertNotEmpty( $body, 'The body should have cells' ); + $this->assertCount( 2, $body, 'There should be 2 rows' ); + + // Test the first row. + $this->assertCount( 2, $body[0]['cells'], 'There should be 2 cells in the first row' ); + + // Test the first cell in the first row. + $this->assertEquals( + [ + 'align' => null, + 'colspan' => null, + 'content' => 'Cell 1', + 'rowspan' => 2, // Previously untested + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][0], + 'The first cell in the first row does not match' + ); + + // Test the second cell in the first row. + + $this->assertEquals( + [ + 'align' => null, + 'colspan' => 2, + 'content' => 'Cell 2', + 'rowspan' => null, + 'scope' => null, + 'tag' => 'td', + ], + $body[0]['cells'][1], + 'The second cell in the first row does not match' + ); - $node = $actual['data']['posts']['nodes'][0]; + // Test the footer cells. + $this->assertNotEmpty( $foot, 'The foot should have cells' ); + $this->assertCount( 1, $foot, 'There should be 1 row in the foot' ); + $this->assertCount( 1, $foot[0]['cells'], 'There should be 1 cell in the foot' ); - $this->assertEquals( 'core/table', $node['editorBlocks'][0]['name'] ); - // There should be only one block using that query when not using flat: true - $this->assertEquals( 1, count( $node['editorBlocks'] ) ); + // Test the first cell in the foot. $this->assertEquals( [ - 'caption' => 'Caption', 'align' => null, - 'anchor' => null, + 'colspan' => 3, + 'content' => 'Footer label', + 'rowspan' => null, + 'scope' => null, + 'tag' => 'td', ], - $node['editorBlocks'][0]['attributes'] + $foot[0]['cells'][0], + 'The first cell in the foot does not match' ); } } diff --git a/tests/unit/CoreVideoTest.php b/tests/unit/CoreVideoTest.php index 7a455b38..5796e36b 100644 --- a/tests/unit/CoreVideoTest.php +++ b/tests/unit/CoreVideoTest.php @@ -85,12 +85,12 @@ public function test_retrieve_core_video_attributes() { 'anchor' => null, 'autoplay' => true, 'tracks' => [], - 'muted' => null, - 'caption' => null, + 'muted' => false, + 'caption' => '', 'preload' => 'auto', 'src' => 'http://mysite.local/wp-content/uploads/2023/07/pexels_videos_1860684-1440p.mp4', 'playsInline' => true, - 'controls' => true, + 'controls' => false, 'loop' => true, 'poster' => 'http://mysite.local/wp-content/uploads/2023/05/pexels-egor-komarov-14420089-scaled.jpg', 'id' => 1636.0,