diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/block.json b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/block.json
new file mode 100644
index 0000000000..c9c49a5296
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/block.json
@@ -0,0 +1,7 @@
+{
+ "name": "tests/hooked-after",
+ "blockHooks": {
+ "core/post-content": "after"
+ },
+ "render": "file:render.php"
+}
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/render.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/render.php
new file mode 100644
index 0000000000..11842e2fac
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-after/render.php
@@ -0,0 +1 @@
+
Block hooked after.
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/block.json b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/block.json
new file mode 100644
index 0000000000..5fbe3e43c4
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/block.json
@@ -0,0 +1,7 @@
+{
+ "name": "tests/hooked-before",
+ "blockHooks": {
+ "core/navigation": "before"
+ },
+ "render": "file:render.php"
+}
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/render.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/render.php
new file mode 100644
index 0000000000..11cf764e0c
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-before/render.php
@@ -0,0 +1 @@
+
Block hooked before.
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/block.json b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/block.json
new file mode 100644
index 0000000000..b45c292b79
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/block.json
@@ -0,0 +1,7 @@
+{
+ "name": "tests/hooked-first-child",
+ "blockHooks": {
+ "core/comments": "firstChild"
+ },
+ "render": "file:render.php"
+}
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/render.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/render.php
new file mode 100644
index 0000000000..cf18dbec73
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-first-child/render.php
@@ -0,0 +1 @@
+
Block hooked as the first child.
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/block.json b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/block.json
new file mode 100644
index 0000000000..188c173db0
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/block.json
@@ -0,0 +1,7 @@
+{
+ "name": "tests/hooked-last-child",
+ "blockHooks": {
+ "core/comment-template": "lastChild"
+ },
+ "render": "file:render.php"
+}
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/render.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/render.php
new file mode 100644
index 0000000000..6a33b30c4c
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/blocks/hooked-last-child/render.php
@@ -0,0 +1 @@
+
Block hooked as the last child.
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/comments.html b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/comments.html
new file mode 100644
index 0000000000..09938c14cf
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/comments.html
@@ -0,0 +1 @@
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/footer.html b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/footer.html
new file mode 100644
index 0000000000..e7657167e5
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/footer.html
@@ -0,0 +1 @@
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/header.html b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/header.html
new file mode 100644
index 0000000000..475e811571
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/parts/header.html
@@ -0,0 +1,10 @@
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/footer-default.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/footer-default.php
new file mode 100644
index 0000000000..353149ec47
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/footer-default.php
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+ WordPress'
+ )
+ ?>
+
+
+
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/hidden-comments.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/hidden-comments.php
new file mode 100644
index 0000000000..243eb13029
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/hidden-comments.php
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/post-meta.php b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/post-meta.php
new file mode 100644
index 0000000000..48bb51c720
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/patterns/post-meta.php
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/style.css b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/style.css
new file mode 100644
index 0000000000..cbfd71f6b6
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/style.css
@@ -0,0 +1,7 @@
+/*
+Theme Name: Block Theme with Hooked Blocks
+Theme URI: https://wordpress.org/
+Description: For testing purposes only.
+Version: 1.0.0
+Text Domain: block-theme-with-hooked-blocks
+*/
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/index.html b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/index.html
new file mode 100644
index 0000000000..5ebe02764b
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/single.html b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/single.html
new file mode 100644
index 0000000000..742c3aba5e
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/templates/single.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/theme.json b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/theme.json
new file mode 100644
index 0000000000..28f3d7aa84
--- /dev/null
+++ b/tests/phpunit/data/themedir1/block-theme-with-hooked-blocks/theme.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/theme.json",
+ "version": 2,
+ "templateParts": [
+ {
+ "area": "header",
+ "name": "header",
+ "title": "Header"
+ },
+ {
+ "area": "footer",
+ "name": "footer",
+ "title": "Footer"
+ },
+ {
+ "area": "uncategorized",
+ "name": "comments",
+ "title": "Comments"
+ }
+ ]
+}
diff --git a/tests/phpunit/tests/blocks/blockHooks.php b/tests/phpunit/tests/blocks/blockHooks.php
index 4f7f419337..e69de29bb2 100644
--- a/tests/phpunit/tests/blocks/blockHooks.php
+++ b/tests/phpunit/tests/blocks/blockHooks.php
@@ -1,122 +0,0 @@
-is_registered( $block_name ) ) {
- $registry->unregister( $block_name );
- }
- }
-
- parent::tear_down();
- }
-
- /**
- * @ticket 59383
- *
- * @covers ::get_hooked_blocks
- */
- public function test_get_hooked_blocks_no_match_found() {
- $result = get_hooked_blocks( 'tests/no-hooked-blocks' );
-
- $this->assertSame( array(), $result );
- }
-
- /**
- * @ticket 59383
- *
- * @covers ::get_hooked_blocks
- */
- public function test_get_hooked_blocks_matches_found() {
- register_block_type(
- 'tests/injected-one',
- array(
- 'block_hooks' => array(
- 'tests/hooked-at-before' => 'before',
- 'tests/hooked-at-after' => 'after',
- 'tests/hooked-at-before-and-after' => 'before',
- ),
- )
- );
- register_block_type(
- 'tests/injected-two',
- array(
- 'block_hooks' => array(
- 'tests/hooked-at-before' => 'before',
- 'tests/hooked-at-after' => 'after',
- 'tests/hooked-at-before-and-after' => 'after',
- 'tests/hooked-at-first-child' => 'first_child',
- 'tests/hooked-at-last-child' => 'last_child',
- ),
- )
- );
-
- $this->assertSame(
- array(
- 'before' => array(
- 'tests/injected-one',
- 'tests/injected-two',
- ),
- ),
- get_hooked_blocks( 'tests/hooked-at-before' ),
- 'block hooked at the before position'
- );
- $this->assertSame(
- array(
- 'after' => array(
- 'tests/injected-one',
- 'tests/injected-two',
- ),
- ),
- get_hooked_blocks( 'tests/hooked-at-after' ),
- 'block hooked at the after position'
- );
- $this->assertSame(
- array(
- 'first_child' => array(
- 'tests/injected-two',
- ),
- ),
- get_hooked_blocks( 'tests/hooked-at-first-child' ),
- 'block hooked at the first child position'
- );
- $this->assertSame(
- array(
- 'last_child' => array(
- 'tests/injected-two',
- ),
- ),
- get_hooked_blocks( 'tests/hooked-at-last-child' ),
- 'block hooked at the last child position'
- );
- $this->assertSame(
- array(
- 'before' => array(
- 'tests/injected-one',
- ),
- 'after' => array(
- 'tests/injected-two',
- ),
- ),
- get_hooked_blocks( 'tests/hooked-at-before-and-after' ),
- 'block hooked before one block and after another'
- );
- }
-}
diff --git a/tests/phpunit/tests/blocks/getHookedBlocks.php b/tests/phpunit/tests/blocks/getHookedBlocks.php
new file mode 100644
index 0000000000..e20bc6d34c
--- /dev/null
+++ b/tests/phpunit/tests/blocks/getHookedBlocks.php
@@ -0,0 +1,244 @@
+get_all_registered();
+ foreach ( $block_types as $block_type ) {
+ $block_name = $block_type->name;
+ if ( str_starts_with( $block_name, 'tests/' ) ) {
+ unregister_block_type( $block_name );
+ }
+ }
+
+ // Removes test block patterns registered with the test theme.
+ $patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered();
+ foreach ( $patterns as $pattern ) {
+ if ( empty( $pattern['slug'] ) ) {
+ continue;
+ }
+ $pattern_name = $pattern['slug'];
+ if ( str_starts_with( $pattern_name, self::TEST_THEME_NAME ) ) {
+ unregister_block_pattern( $pattern_name );
+ }
+ }
+
+ parent::tear_down();
+ }
+
+ private function switch_to_block_theme_hooked_blocks() {
+ switch_theme( self::TEST_THEME_NAME );
+
+ _register_theme_block_patterns();
+
+ $theme_blocks_dir = wp_normalize_path( realpath( get_theme_file_path( 'blocks' ) ) );
+ register_block_type( $theme_blocks_dir . '/hooked-before' );
+ register_block_type( $theme_blocks_dir . '/hooked-after' );
+ register_block_type( $theme_blocks_dir . '/hooked-first-child' );
+ register_block_type( $theme_blocks_dir . '/hooked-last-child' );
+ }
+
+ /**
+ * @ticket 59383
+ *
+ * @covers ::get_hooked_blocks
+ */
+ public function test_get_hooked_blocks_no_match_found() {
+ $result = get_hooked_blocks( 'tests/no-hooked-blocks' );
+
+ $this->assertSame( array(), $result );
+ }
+
+ /**
+ * @ticket 59383
+ *
+ * @covers ::get_hooked_blocks
+ */
+ public function test_get_hooked_blocks_matches_found() {
+ register_block_type(
+ 'tests/injected-one',
+ array(
+ 'block_hooks' => array(
+ 'tests/hooked-at-before' => 'before',
+ 'tests/hooked-at-after' => 'after',
+ 'tests/hooked-at-before-and-after' => 'before',
+ ),
+ )
+ );
+ register_block_type(
+ 'tests/injected-two',
+ array(
+ 'block_hooks' => array(
+ 'tests/hooked-at-before' => 'before',
+ 'tests/hooked-at-after' => 'after',
+ 'tests/hooked-at-before-and-after' => 'after',
+ 'tests/hooked-at-first-child' => 'first_child',
+ 'tests/hooked-at-last-child' => 'last_child',
+ ),
+ )
+ );
+
+ $this->assertSame(
+ array(
+ 'before' => array(
+ 'tests/injected-one',
+ 'tests/injected-two',
+ ),
+ ),
+ get_hooked_blocks( 'tests/hooked-at-before' ),
+ 'block hooked at the before position'
+ );
+ $this->assertSame(
+ array(
+ 'after' => array(
+ 'tests/injected-one',
+ 'tests/injected-two',
+ ),
+ ),
+ get_hooked_blocks( 'tests/hooked-at-after' ),
+ 'block hooked at the after position'
+ );
+ $this->assertSame(
+ array(
+ 'first_child' => array(
+ 'tests/injected-two',
+ ),
+ ),
+ get_hooked_blocks( 'tests/hooked-at-first-child' ),
+ 'block hooked at the first child position'
+ );
+ $this->assertSame(
+ array(
+ 'last_child' => array(
+ 'tests/injected-two',
+ ),
+ ),
+ get_hooked_blocks( 'tests/hooked-at-last-child' ),
+ 'block hooked at the last child position'
+ );
+ $this->assertSame(
+ array(
+ 'before' => array(
+ 'tests/injected-one',
+ ),
+ 'after' => array(
+ 'tests/injected-two',
+ ),
+ ),
+ get_hooked_blocks( 'tests/hooked-at-before-and-after' ),
+ 'block hooked before one block and after another'
+ );
+ }
+
+ /**
+ * @ticket 59313
+ *
+ * @covers ::get_hooked_blocks
+ * @covers ::get_block_file_template
+ */
+ public function test_loading_template_with_hooked_blocks() {
+ $this->switch_to_block_theme_hooked_blocks();
+
+ $template = get_block_file_template( get_stylesheet() . '//single' );
+
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ $this->assertStringContainsString(
+ ''
+ . '',
+ $template->content
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ }
+
+ /**
+ * @ticket 59313
+ *
+ * @covers ::get_hooked_blocks
+ * @covers ::get_block_file_template
+ */
+ public function test_loading_template_part_with_hooked_blocks() {
+ $this->switch_to_block_theme_hooked_blocks();
+
+ $template = get_block_file_template( get_stylesheet() . '//header', 'wp_template_part' );
+
+ $this->assertStringContainsString(
+ ''
+ . '',
+ $template->content
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $template->content
+ );
+ }
+
+ /**
+ * @ticket 59313
+ *
+ * @covers ::get_hooked_blocks
+ * @covers WP_Block_Patterns_Registry::get_registered
+ */
+ public function test_loading_pattern_with_hooked_blocks() {
+ $this->switch_to_block_theme_hooked_blocks();
+
+ $pattern = WP_Block_Patterns_Registry::get_instance()->get_registered(
+ get_stylesheet() . '/hidden-comments'
+ );
+
+ $this->assertStringNotContainsString(
+ '',
+ $pattern['content']
+ );
+ $this->assertStringNotContainsString(
+ '',
+ $pattern['content']
+ );
+ $this->assertStringContainsString(
+ ''
+ . '