diff --git a/plugins/woocommerce/changelog/add-add-block-to-block-template b/plugins/woocommerce/changelog/add-add-block-to-block-template new file mode 100644 index 00000000000..f0994ad24c5 --- /dev/null +++ b/plugins/woocommerce/changelog/add-add-block-to-block-template @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +API for block-based templates. diff --git a/plugins/woocommerce/src/Admin/BlockTemplates/BlockContainerInterface.php b/plugins/woocommerce/src/Admin/BlockTemplates/BlockContainerInterface.php new file mode 100644 index 00000000000..7c221b6e4f6 --- /dev/null +++ b/plugins/woocommerce/src/Admin/BlockTemplates/BlockContainerInterface.php @@ -0,0 +1,8 @@ +validate( $config, $root_template, $parent ); + + $this->root_template = $root_template; + $this->parent = is_null( $parent ) ? $root_template : $parent; + + $this->name = $config[ self::NAME_KEY ]; + + if ( ! isset( $config[ self::ID_KEY ] ) ) { + $this->id = $this->root_template->generate_block_id( $this->get_name() ); + } else { + $this->id = $config[ self::ID_KEY ]; + } + + if ( isset( $config[ self::ORDER_KEY ] ) ) { + $this->order = $config[ self::ORDER_KEY ]; + } + + if ( isset( $config[ self::ATTRIBUTES_KEY ] ) ) { + $this->attributes = $config[ self::ATTRIBUTES_KEY ]; + } + } + + /** + * Validate block configuration. + * + * @param array $config The block configuration. + * @param BlockTemplateInterface $root_template The block template that this block belongs to. + * @param ContainerInterface|null $parent The parent block container. + * + * @throws \ValueError If the block configuration is invalid. + * @throws \ValueError If the parent block container does not belong to the same template as the block. + */ + protected function validate( array $config, BlockTemplateInterface &$root_template, ContainerInterface &$parent = null ) { + if ( isset( $parent ) && ( $parent->get_root_template() !== $root_template ) ) { + throw new \ValueError( 'The parent block must belong to the same template as the block.' ); + } + + if ( ! isset( $config[ self::NAME_KEY ] ) || ! is_string( $config[ self::NAME_KEY ] ) ) { + throw new \ValueError( 'The block name must be specified.' ); + } + + if ( isset( $config[ self::ORDER_KEY ] ) && ! is_int( $config[ self::ORDER_KEY ] ) ) { + throw new \ValueError( 'The block order must be an integer.' ); + } + + if ( isset( $config[ self::ATTRIBUTES_KEY ] ) && ! is_array( $config[ self::ATTRIBUTES_KEY ] ) ) { + throw new \ValueError( 'The block attributes must be an array.' ); + } + } + + /** + * Get the block name. + */ + public function get_name(): string { + return $this->name; + } + + /** + * Get the block ID. + */ + public function get_id(): string { + return $this->id; + } + + /** + * Get the block order. + */ + public function get_order(): int { + return $this->order; + } + + /** + * Set the block order. + * + * @param int $order The block order. + */ + public function set_order( int $order ) { + $this->order = $order; + } + + /** + * Get the block attributes. + */ + public function get_attributes(): array { + return $this->attributes; + } + + /** + * Set the block attributes. + * + * @param array $attributes The block attributes. + */ + public function set_attributes( array $attributes ) { + $this->attributes = $attributes; + } + + /** + * Get the template that this block belongs to. + */ + public function &get_root_template(): BlockTemplateInterface { + return $this->root_template; + } + + /** + * Get the parent block container. + */ + public function &get_parent(): ContainerInterface { + return $this->parent; + } + + /** + * Get the block configuration as a formatted template. + * + * @return array The block configuration as a formatted template. + */ + public function get_formatted_template(): array { + $arr = [ + $this->get_name(), + $this->get_attributes(), + ]; + + return $arr; + } +} diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlockTemplate.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlockTemplate.php new file mode 100644 index 00000000000..290c2cc7947 --- /dev/null +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/AbstractBlockTemplate.php @@ -0,0 +1,95 @@ +block_cache[ $block_id ] ?? null; + } + + /** + * Caches a block in the template. This is an internal method and should not be called directly + * except for classes that implement BlockContainerInterface, in their add_block() method. + * + * @param BlockInterface $block The block to cache. + * + * @throws \ValueError If a block with the specified ID already exists in the template. + * @throws \ValueError If the block template that the block belongs to is not this template. + * + * @ignore + */ + public function cache_block( BlockInterface &$block ) { + $id = $block->get_id(); + + if ( isset( $this->block_cache[ $id ] ) ) { + throw new \ValueError( 'A block with the specified ID already exists in the template.' ); + } + + if ( $block->get_root_template() !== $this ) { + throw new \ValueError( 'The block template that the block belongs to must be the same as this template.' ); + } + + $this->block_cache[ $id ] = $block; + } + + /** + * Generate a block ID based on a base. + * + * @param string $id_base The base to use when generating an ID. + * @return string + */ + public function generate_block_id( string $id_base ): string { + $instance_count = 0; + + do { + $instance_count++; + $block_id = $id_base . '-' . $instance_count; + } while ( isset( $this->block_cache[ $block_id ] ) ); + + return $block_id; + } + + /** + * Get the root template. + */ + public function &get_root_template(): BlockTemplateInterface { + return $this; + } + + /** + * Get the inner blocks as a formatted template. + */ + public function get_formatted_template(): array { + $inner_blocks = $this->get_inner_blocks_sorted_by_order(); + + $inner_blocks_formatted_template = array_map( + function( Block $block ) { + return $block->get_formatted_template(); + }, + $inner_blocks + ); + + return $inner_blocks_formatted_template; + } +} diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/Block.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/Block.php new file mode 100644 index 00000000000..bdd2c977c03 --- /dev/null +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/Block.php @@ -0,0 +1,49 @@ +get_name(), + $this->get_attributes(), + ]; + + $inner_blocks = $this->get_inner_blocks_sorted_by_order(); + + if ( ! empty( $inner_blocks ) ) { + $arr[] = array_map( + function( BlockInterface $block ) { + return $block->get_formatted_template(); + }, + $inner_blocks + ); + } + + return $arr; + } + + /** + * Add an inner block to this block. + * + * @param array $block_config The block data. + */ + public function &add_block( array $block_config ): BlockInterface { + $block = new Block( $block_config, $this->get_root_template(), $this ); + return $this->add_inner_block( $block ); + } +} diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php new file mode 100644 index 00000000000..731bef7cf4d --- /dev/null +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockContainerTrait.php @@ -0,0 +1,85 @@ +get_parent() !== $this ) { + throw new \UnexpectedValueException( 'The block container is not the parent of the block.' ); + } + + $root_template = $block->get_root_template(); + $root_template->cache_block( $block ); + $this->inner_blocks[] = &$block; + return $block; + } + + // phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber + + /** + * Get the inner blocks sorted by order. + */ + private function get_inner_blocks_sorted_by_order(): array { + $sorted_inner_blocks = $this->inner_blocks; + + usort( + $sorted_inner_blocks, + function( Block $a, Block $b ) { + return $a->get_order() <=> $b->get_order(); + } + ); + + return $sorted_inner_blocks; + } + + /** + * Get the inner blocks as a formatted template. + */ + public function get_formatted_template(): array { + $arr = [ + $this->get_name(), + $this->get_attributes(), + ]; + + $inner_blocks = $this->get_inner_blocks_sorted_by_order(); + + if ( ! empty( $inner_blocks ) ) { + $arr[] = array_map( + function( BlockInterface $block ) { + return $block->get_formatted_template(); + }, + $inner_blocks + ); + } + + return $arr; + } +} diff --git a/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplate.php b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplate.php new file mode 100644 index 00000000000..16eed6c32e7 --- /dev/null +++ b/plugins/woocommerce/src/Internal/Admin/BlockTemplates/BlockTemplate.php @@ -0,0 +1,22 @@ +get_root_template(), $this ); + return $this->add_inner_block( $block ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php new file mode 100644 index 00000000000..4b156d793d6 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTemplateTest.php @@ -0,0 +1,367 @@ +assertSame( 'test-block-id-1', $template->generate_block_id( 'test-block-id' ) ); + } + + /** + * Test adding a block. + */ + public function test_add_block() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $this->assertSame( $block, $template->get_block( 'test-block-id' ) ); + } + + /** + * Test adding a block throws an exception if a block with the same ID already exists. + */ + public function test_add_block_throws_exception_if_block_with_same_id_already_exists() { + $template = new BlockTemplate(); + + $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $this->expectException( \ValueError::class ); + + $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + } + + /** + * Test adding a block generates an ID if one is not provided. + */ + public function test_add_block_generates_id_if_not_provided() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $this->assertSame( 'test-block-name-1', $block->get_id() ); + } + + /** + * Test getting a block by ID returns null if the block does not exist. + */ + public function test_get_block_returns_null_if_block_does_not_exist() { + $template = new BlockTemplate(); + + $this->assertNull( $template->get_block( 'test-block-id' ) ); + } + + /** + * Test getting a block by ID returns a reference to the block. + */ + public function test_get_block_returns_reference() { + $template = new BlockTemplate(); + + $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $block = $template->get_block( 'test-block-id' ); + + $block->set_order( 23 ); + + $this->assertSame( 23, $template->get_block( 'test-block-id' )->get_order() ); + } + + /** + * Test that the formatted template representation of a block template is correct. + */ + public function test_get_formatted_template() { + $template = new BlockTemplate(); + + $template->add_block( + [ + 'blockName' => 'test-block-name-c', + 'order' => 100, + 'attributes' => [ + 'attr-c1' => 'value-c1', + 'attr-c2' => 'value-c2', + ], + ] + ); + + $block_b = $template->add_block( + [ + 'blockName' => 'test-block-name-b', + 'order' => 50, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $template->add_block( + [ + 'blockName' => 'test-block-name-a', + 'order' => 10, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-2', + 'order' => 20, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-1', + 'order' => 10, + 'attributes' => [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-3', + 'order' => 30, + ] + ); + + $this->assertSame( + [ + [ + 'test-block-name-a', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ], + [ + 'test-block-name-b', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + [ + [ + 'test-block-name-1', + [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ], + [ + 'test-block-name-2', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ], + [ + 'test-block-name-3', + [], + ], + ], + ], + [ + 'test-block-name-c', + [ + 'attr-c1' => 'value-c1', + 'attr-c2' => 'value-c2', + ], + ], + ], + $template->get_formatted_template(), + 'Failed asserting that the block is converted to a simple array correctly.' + ); + } + + /** + * Test that inserting a block to a parent in the template works. + */ + public function test_inserting_block_by_parent_id() { + $template = new BlockTemplate(); + + $template->add_block( + [ + 'blockName' => 'test-block-name-c', + 'order' => 100, + 'attributes' => [ + 'attr-c1' => 'value-c1', + 'attr-c2' => 'value-c2', + ], + ] + ); + + $block_b = $template->add_block( + [ + 'id' => 'b', + 'blockName' => 'test-block-name-b', + 'order' => 50, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $template->add_block( + [ + 'id' => 'a', + 'blockName' => 'test-block-name-a', + 'order' => 10, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-2', + 'order' => 20, + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-1', + 'order' => 10, + 'attributes' => [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ] + ); + + $block_b->add_block( + [ + 'blockName' => 'test-block-name-3', + 'order' => 30, + ] + ); + + $block_to_insert_in = $template->get_block( 'a' ); + + $block_to_insert_in->add_block( + [ + 'blockName' => 'inserted-block', + ] + ); + + $another_block_to_insert_in = $template->get_block( 'b' ); + + $another_block_to_insert_in->add_block( + [ + 'blockName' => 'another-inserted-block', + 'order' => 15, + ] + ); + + $this->assertSame( + [ + [ + 'test-block-name-a', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + [ + [ + 'inserted-block', + [], + ], + ], + ], + [ + 'test-block-name-b', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + [ + [ + 'test-block-name-1', + [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ], + [ + 'another-inserted-block', + [], + ], + [ + 'test-block-name-2', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ], + [ + 'test-block-name-3', + [], + ], + ], + ], + [ + 'test-block-name-c', + [ + 'attr-c1' => 'value-c1', + 'attr-c2' => 'value-c2', + ], + ], + ], + $template->get_formatted_template(), + 'Failed asserting that the template is converted to a formatted template correctly.' + ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php new file mode 100644 index 00000000000..79f5c054e1c --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/BlockTest.php @@ -0,0 +1,306 @@ +expectException( \ValueError::class ); + + new Block( [], $template ); + } + + /** + * Test that an ID is generated if not provided when creating a block. + */ + public function test_id_is_generated_if_not_provided() { + $template = new BlockTemplate(); + + $block = new Block( + [ + 'blockName' => 'test-block-name', + ], + $template + ); + + $this->assertSame( 'test-block-name-1', $block->get_id() ); + } + + /** + * Test that setting a parent from a different template is prevented. + */ + public function test_parent_from_different_template_throws_exception() { + $template = new BlockTemplate(); + $template_2 = new BlockTemplate(); + + $parent = new Block( + [ + 'blockName' => 'test-block-parent-name', + ], + $template_2 + ); + + $this->expectException( \ValueError::class ); + + new Block( + [ + 'blockName' => 'test-block-name', + ], + $template, + $parent + ); + } + + /** + * Test that adding a block to a block sets the parent and root template correctly + * and that the block is added to the root template. + */ + public function test_add_block() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + ] + ); + + $child_block = $block->add_block( + [ + 'id' => 'test-block-id-2', + 'blockName' => 'test-block-name-2', + ] + ); + + $this->assertSame( + $child_block->get_root_template(), + $block->get_root_template(), + 'Failed asserting that the child block has the same root template as the parent block.' + ); + + $this->assertSame( + $block, + $child_block->get_parent(), + 'Failed asserting that the child block\'s parent is the block it was added to.' + ); + $this->assertSame( + $child_block, + $template->get_block( 'test-block-id-2' ), + 'Failed asserting that the child block is in the root template.' + ); + } + + /** + * Test that adding nested blocks sets the parent and root template correctly. + */ + public function test_nested_add_block() { + $block_template = new BlockTemplate(); + + $block = $block_template->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $child_block_1 = $block->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $block->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $grandchild_block = $child_block_1->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $this->assertSame( + $block_template, + $grandchild_block->get_root_template(), + 'Failed asserting that the grandchild block has the same root template.' + ); + + $grandchild_parent = $grandchild_block->get_parent(); + + $this->assertSame( + $child_block_1, + $grandchild_parent, + 'Failed asserting that the grandchild block\'s parent is the block it was added to.' + ); + + $this->assertInstanceOf( + BlockContainerInterface::class, + $grandchild_parent, + 'Failed asserting that the grandchild block\'s parent is a BlockContainerInterface instance.' + ); + + $this->assertSame( + $block, + $grandchild_parent->get_parent(), + 'Failed asserting that the grandchild block\'s grandparent is correct.' + ); + } + + /** + * Test that getting the block as a formatted template is structured correctly. + */ + public function test_get_formatted_template() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'id' => 'test-block-id', + 'blockName' => 'test-block-name', + 'attributes' => [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + ] + ); + + $block->add_block( + [ + 'id' => 'test-block-id-2', + 'blockName' => 'test-block-name-2', + 'attributes' => [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ] + ); + + $block->add_block( + [ + 'id' => 'test-block-id-3', + 'blockName' => 'test-block-name-3', + ] + ); + + $formatted_template = $block->get_formatted_template(); + + $this->assertSame( + [ + 'test-block-name', + [ + 'attr-1' => 'value-1', + 'attr-2' => 'value-2', + ], + [ + [ + 'test-block-name-2', + [ + 'attr-3' => 'value-3', + 'attr-4' => 'value-4', + ], + ], + [ + 'test-block-name-3', + [], + ], + ], + ], + $formatted_template, + 'Failed asserting that the block is converted to a formatted template correctly.' + ); + } + + /** + * Test that getting the inner blocks as a sorted formatted template is ordered correctly. + */ + public function test_get_formatted_template_with_sorting() { + $template = new BlockTemplate(); + + $block = $template->add_block( + [ + 'blockName' => 'test-block-name', + ] + ); + + $block->add_block( + [ + 'blockName' => 'five', + 'order' => 5, + ] + ); + + $block->add_block( + [ + 'blockName' => 'three', + 'order' => 3, + ] + ); + + $block->add_block( + [ + 'blockName' => 'one', + 'order' => 1, + ] + ); + + $block->add_block( + [ + 'blockName' => 'four', + 'order' => 4, + ] + ); + + $block->add_block( + [ + 'blockName' => 'two', + 'order' => 2, + ] + ); + + $this->assertSame( + [ + 'test-block-name', + [], + [ + [ + 'one', + [], + ], + [ + 'two', + [], + ], + [ + 'three', + [], + ], + [ + 'four', + [], + ], + [ + 'five', + [], + ], + ], + ], + $block->get_formatted_template(), + 'Failed asserting that the inner blocks are sorted by order.' + ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlock.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlock.php new file mode 100644 index 00000000000..46f524aaa3f --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlock.php @@ -0,0 +1,36 @@ + 'custom-inner-block', + ], + $this->get_root_template(), + $this + ); + + $this->add_inner_block( $block ); + + return $block; + } +} + diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockInterface.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockInterface.php new file mode 100644 index 00000000000..dbaf133a8c8 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockInterface.php @@ -0,0 +1,13 @@ +get_root_template(), $this ); + return $this->add_inner_block( $block ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplateTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplateTest.php new file mode 100644 index 00000000000..6c6d9d86860 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTemplateTest.php @@ -0,0 +1,41 @@ +assertFalse( method_exists( $template, 'add_block' ) ); + } + + /** + * Test that a custom block inserter method inserts as expected. + */ + public function test_add_custom_block() { + $template = new CustomBlockTemplate(); + + $template->add_custom_block( + [ + 'id' => 'test-block-name', + 'blockName' => 'test-block-name', + ] + ); + + $block = $template->get_block( 'test-block-name' ); + $this->assertInstanceOf( CustomBlock::class, $block ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php new file mode 100644 index 00000000000..f6f81d52c31 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/BlockTemplates/CustomBlockTest.php @@ -0,0 +1,61 @@ + 'test-block-name', + ], + $template + ); + + $this->assertFalse( method_exists( $block, 'add_block' ) ); + } + + /** + * Test that a custom block inserter method inserts as expected. + */ + public function test_add_custom_inner_block() { + $template = new BlockTemplate(); + $block = new CustomBlock( + [ + 'blockName' => 'test-block-name', + ], + $template + ); + + $block->add_custom_inner_block(); + + $this->assertSame( + [ + 'test-block-name', + [], + [ + [ + 'custom-inner-block', + [], + ], + ], + ], + $block->get_formatted_template(), + 'Failed asserting that the inner block was added' + ); + } +}