diff --git a/.changeset/weak-days-suffer.md b/.changeset/weak-days-suffer.md
new file mode 100644
index 00000000..a6297612
--- /dev/null
+++ b/.changeset/weak-days-suffer.md
@@ -0,0 +1,5 @@
+---
+"@wpengine/wp-graphql-content-blocks": patch
+---
+
+tests : backfill tests for ContentBlockResolver
diff --git a/tests/unit/ContentBlocksResolverTest.php b/tests/unit/ContentBlocksResolverTest.php
index ae7f4d4c..e9331a02 100644
--- a/tests/unit/ContentBlocksResolverTest.php
+++ b/tests/unit/ContentBlocksResolverTest.php
@@ -139,4 +139,214 @@ static function ( $parsed_block ) {
$this->assertEquals( 4, count( $parsed_blocks ) );
$this->assertEquals( $allowed, $actual_block_names );
}
+
+ /**
+ * Test the wpgraphql_content_blocks_pre_resolve_blocks filter.
+ */
+ public function test_pre_resolved_blocks_filter_returns_non_null() {
+ add_filter(
+ 'wpgraphql_content_blocks_pre_resolve_blocks',
+ static function () {
+ return [
+ [
+ 'blockName' => 'core/paragraph',
+ 'attrs' => [ 'content' => 'Test content' ],
+ ],
+ ];
+ },
+ 10,
+ );
+
+ $post = new Post( get_post( $this->post_id ) );
+
+ $resolved_blocks = $this->instance->resolve_content_blocks( $post, [] );
+ // The filter should return a block.
+ $this->assertCount( 1, $resolved_blocks );
+ $this->assertEquals( 'core/paragraph', $resolved_blocks[0]['blockName'] );
+ $this->assertEquals( 'Test content', $resolved_blocks[0]['attrs']['content'] );
+
+ // Cleanup.
+ remove_all_filters( 'wpgraphql_content_blocks_pre_resolve_blocks' );
+ }
+
+ /**
+ * Tests that an empty array is returned when the post content is empty.
+ */
+ public function test_returns_empty_array_for_empty_content() {
+ $post_id = self::factory()->post->create( [ 'post_content' => '' ] );
+ $post = new Post( get_post( $post_id ) );
+
+ $resolved_blocks = $this->instance->resolve_content_blocks( $post, [] );
+
+ // The block should be resolved from the post node.
+ $this->assertIsArray( $resolved_blocks );
+ $this->assertEmpty( $resolved_blocks );
+
+ // Cleanup.
+ wp_delete_post( $post_id, true );
+ }
+
+ /**
+ * Tests that the wpgraphql_content_blocks_resolve_blocks filter is applied.
+ */
+ public function test_filters_wpgraphql_content_blocks_resolve_blocks() {
+ add_filter(
+ 'wpgraphql_content_blocks_resolve_blocks',
+ static function ( $blocks, $node, $args, $allowed_block_names ) {
+ return [ [ 'blockName' => 'core/test-filter' ] ];
+ },
+ 10,
+ 4
+ );
+
+ $post = new Post( get_post( $this->post_id ) );
+
+ $resolved_blocks = $this->instance->resolve_content_blocks( $post, [] );
+
+ // The block should be resolved from the post node.
+ $this->assertCount( 1, $resolved_blocks );
+ $this->assertEquals( 'core/test-filter', $resolved_blocks[0]['blockName'] );
+
+ // Cleanup.
+ remove_all_filters( 'wpgraphql_content_blocks_resolve_blocks' );
+ }
+
+ /**
+ * Tests that flat and nested blocks are resolved correctly.
+ */
+ public function test_inner_blocks() {
+ $post_content = '
+
+
+
+
+
+
Heading
+
+
+
Paragraph
+
+
+
+
+
+
+
Heading
+
+
+
Paragraph
+
+
+
+
+ ';
+
+ wp_update_post(
+ [
+ 'ID' => $this->post_id,
+ 'post_content' => $post_content,
+ ]
+ );
+
+ $post = new Post( get_post( $this->post_id ) );
+
+ // Resolve blocks as nested.
+ $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->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'];
+
+ 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.
+
+ // Check the inner inner blocks.
+ $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.
+ }
+ }
+
+ // Resolve blocks as flat.
+ $expected_parent_client_id = null;
+ $expected_blocks = $resolved_blocks;
+
+ $resolved_blocks = $this->instance->resolve_content_blocks( $post, [ 'flat' => true ] );
+
+ $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.' );
+
+ // 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.' );
+
+ // 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.' );
+
+ $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.' );
+
+ // 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.' );
+
+ // 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.' );
+ }
+
+ /**
+ * Asserts two blocks are equal, ignoring clientId and parentClientId.
+ *
+ * @param array $expected The expected block.
+ * @param array $actual The actual block.
+ * @param string $message The message to display if the assertion fails.
+ */
+ protected function assertEqualBlocks( $expected, $actual, $message = '' ) {
+ // Remove clientId and parentClientId from comparison.
+ unset( $expected['clientId'] );
+ unset( $expected['parentClientId'] );
+ unset( $actual['clientId'] );
+ unset( $actual['parentClientId'] );
+
+ $expected_inner_blocks = $expected['innerBlocks'] ?? [];
+ $actual_inner_blocks = $actual['innerBlocks'] ?? [];
+
+ unset( $expected['innerBlocks'] );
+ unset( $actual['innerBlocks'] );
+
+ $this->assertEquals( $expected, $actual, $message );
+
+ foreach ( $expected_inner_blocks as $index => $expected_inner_block ) {
+ $this->assertEqualBlocks( $expected_inner_block, $actual_inner_blocks[ $index ], $message );
+ }
+ }
}