diff --git a/includes/collections/class-content-inserter.php b/includes/collections/class-content-inserter.php index ab8095c100..a45d097b61 100644 --- a/includes/collections/class-content-inserter.php +++ b/includes/collections/class-content-inserter.php @@ -264,7 +264,8 @@ function ( $block ) use ( $blocks_to_skip_empty ) { // Insert after the nth block. $rendered_content = ''; foreach ( $parsed_blocks as $index => $block ) { - $rendered_content .= $block['innerHTML']; + // Use render_block() so inner blocks (e.g. core/list-item children of core/list) and dynamic blocks are emitted. + $rendered_content .= render_block( $block ); // Insert after the nth block (index is 0-based). if ( $index === $nth_block - 1 ) { diff --git a/tests/unit-tests/collections/class-test-content-inserter.php b/tests/unit-tests/collections/class-test-content-inserter.php index 6bea31c0f7..55f4ab8d8c 100644 --- a/tests/unit-tests/collections/class-test-content-inserter.php +++ b/tests/unit-tests/collections/class-test-content-inserter.php @@ -264,20 +264,44 @@ public function test_insert_after_nth_block() { $block_content = "\n

First paragraph.

\n\n\n\n

Second paragraph.

\n\n\n\n

Third paragraph.

\n"; $block_result = Content_Inserter::insert_after_nth_block( $block_content, $insert_html, 2 ); - $this->assertStringContainsString( '

First paragraph.

', $block_result, 'First paragraph should be present.' ); - $this->assertStringContainsString( '

Second paragraph.

', $block_result, 'Second paragraph should be present.' ); + // WP 7.0+ adds a `wp-block-paragraph` class to rendered paragraph blocks. + $this->assertStringContainsString( '

First paragraph.

', $block_result, 'First paragraph should be present.' ); + $this->assertStringContainsString( '

Second paragraph.

', $block_result, 'Second paragraph should be present.' ); $this->assertStringContainsString( $insert_html, $block_result, 'Inserted content should be present.' ); - $this->assertStringContainsString( '

Third paragraph.

', $block_result, 'Third paragraph should be present.' ); + $this->assertStringContainsString( '

Third paragraph.

', $block_result, 'Third paragraph should be present.' ); // Verify proper insertion order for Gutenberg blocks. - $second_pos = strpos( $block_result, '

Second paragraph.

' ); + $second_pos = strpos( $block_result, '

Second paragraph.

' ); $insert_pos = strpos( $block_result, $insert_html ); - $third_pos = strpos( $block_result, '

Third paragraph.

' ); + $third_pos = strpos( $block_result, '

Third paragraph.

' ); $this->assertGreaterThan( $second_pos, $insert_pos, 'Inserted content should come after second paragraph.' ); $this->assertLessThan( $third_pos, $insert_pos, 'Inserted content should come before third paragraph.' ); } + /** + * Test insert_after_nth_block preserves inner blocks (e.g., list items). + * + * Regression: a `core/list` block stores its `
  • ` items as `core/list-item` + * inner blocks, not in `innerHTML`. The same applies to columns, groups, + * buttons, etc. Reassembling parsed blocks must use `render_block()` so + * inner-block content (and dynamic blocks) are emitted. + * + * @covers \Newspack\Collections\Content_Inserter::insert_after_nth_block + */ + public function test_insert_after_nth_block_preserves_inner_blocks() { + $insert_html = '
    Inserted content
    '; + + $block_content = "\n

    First paragraph.

    \n\n\n" + . "\n

    Second paragraph.

    \n\n\n" + . "\n\n"; + + $result = Content_Inserter::insert_after_nth_block( $block_content, $insert_html, 2 ); + + $this->assertStringContainsString( '
  • Item one
  • ', $result, 'First list item should be preserved.' ); + $this->assertStringContainsString( '
  • Item two
  • ', $result, 'Second list item should be preserved.' ); + } + /** * Test check_if_post_is_in_collection excludes draft collections. *