Skip to content

Commit 659ba67

Browse files
committed
Editor: Add unit tests for _wp_apply_block_content_filters().
Adds PHPUnit coverage for the private _wp_apply_block_content_filters() helper, which is used by the Latest Posts block's full-content rendering support. The helper itself and the Latest Posts change have already landed separately. The new tests cover applying the expected content filters, rendering block markup returned by shortcodes, passing the image filter context through, processing embeds, and clearing recursion-guard state after both successful and exceptional block rendering. Props get_dave, wildworks, scruffian. See #65586. git-svn-id: https://develop.svn.wordpress.org/trunk@62758 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 144d701 commit 659ba67

1 file changed

Lines changed: 262 additions & 0 deletions

File tree

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
<?php
2+
/**
3+
* Tests for _wp_apply_block_content_filters().
4+
*
5+
* @package WordPress
6+
* @subpackage Blocks
7+
*
8+
* @group blocks
9+
*
10+
* @covers ::_wp_apply_block_content_filters
11+
*/
12+
class Tests_Blocks_WpApplyBlockContentFilters extends WP_UnitTestCase {
13+
const TEST_BLOCK_NAME = 'tests/apply-block-content-filters';
14+
const TEST_EXCEPTION_BLOCK_NAME = 'tests/apply-block-content-filters-exception';
15+
const TEST_EMBED_HANDLER = 'apply_block_content_filters_embed';
16+
const TEST_SHORTCODE = 'apply_block_content_filters_shortcode';
17+
18+
/**
19+
* The original use_smilies option value.
20+
*
21+
* @var string
22+
*/
23+
private $original_use_smilies;
24+
25+
/**
26+
* Context received by the wp_content_img_tag filter.
27+
*
28+
* @var string|null
29+
*/
30+
private $filtered_image_context;
31+
32+
public function set_up() {
33+
parent::set_up();
34+
35+
$this->original_use_smilies = get_option( 'use_smilies' );
36+
$this->filtered_image_context = null;
37+
38+
update_option( 'use_smilies', 1 );
39+
smilies_init();
40+
41+
register_block_type(
42+
self::TEST_BLOCK_NAME,
43+
array(
44+
'render_callback' => static function ( $attributes ) {
45+
return sprintf(
46+
'<div class="apply-block-content-filters-block">%s</div>',
47+
esc_html( $attributes['label'] )
48+
);
49+
},
50+
)
51+
);
52+
53+
register_block_type(
54+
self::TEST_EXCEPTION_BLOCK_NAME,
55+
array(
56+
'render_callback' => static function () {
57+
throw new Exception( 'Block rendering failed.' );
58+
},
59+
)
60+
);
61+
}
62+
63+
public function tear_down() {
64+
remove_shortcode( self::TEST_SHORTCODE );
65+
wp_embed_unregister_handler( self::TEST_EMBED_HANDLER );
66+
remove_filter( 'wp_content_img_tag', array( $this, 'filter_content_image_tag' ) );
67+
68+
if ( WP_Block_Type_Registry::get_instance()->is_registered( self::TEST_BLOCK_NAME ) ) {
69+
unregister_block_type( self::TEST_BLOCK_NAME );
70+
}
71+
72+
if ( WP_Block_Type_Registry::get_instance()->is_registered( self::TEST_EXCEPTION_BLOCK_NAME ) ) {
73+
unregister_block_type( self::TEST_EXCEPTION_BLOCK_NAME );
74+
}
75+
76+
update_option( 'use_smilies', $this->original_use_smilies );
77+
78+
parent::tear_down();
79+
}
80+
81+
/**
82+
* Filters content image tags.
83+
*
84+
* @param string $filtered_image Full img tag with attributes.
85+
* @param string $context Context for the image tag.
86+
* @return string Filtered image tag.
87+
*/
88+
public function filter_content_image_tag( $filtered_image, $context ) {
89+
$this->filtered_image_context = $context;
90+
91+
if ( str_contains( $filtered_image, 'class="content-image"' ) ) {
92+
return str_replace( '<img ', '<img data-filtered="yes" ', $filtered_image );
93+
}
94+
95+
return $filtered_image;
96+
}
97+
98+
/**
99+
* Renders the test embed handler.
100+
*
101+
* @return string Embed markup.
102+
*/
103+
public function render_test_embed() {
104+
return '<div class="apply-block-content-filters-embed">Embedded content</div>';
105+
}
106+
107+
/**
108+
* Tests that each expected content filter is applied.
109+
*
110+
* @ticket 65586
111+
*/
112+
public function test_applies_content_filters() {
113+
add_filter( 'wp_content_img_tag', array( $this, 'filter_content_image_tag' ), 10, 2 );
114+
115+
wp_embed_register_handler(
116+
self::TEST_EMBED_HANDLER,
117+
'#https?://example\.com/apply-block-content-filters#i',
118+
array( $this, 'render_test_embed' )
119+
);
120+
121+
add_shortcode(
122+
self::TEST_SHORTCODE,
123+
static function () {
124+
return '<!-- wp:tests/apply-block-content-filters {"label":"Shortcode block output"} /-->';
125+
}
126+
);
127+
128+
$content = sprintf(
129+
"This shouldn't use a straight apostrophe, and this should become a smilie: :mrgreen:\n\n<p>[%s]</p>\n\n<!-- wp:tests/apply-block-content-filters {\"label\":\"Direct block output\"} /-->\n\n<img src=\"https://example.org/image.jpg\" alt=\"\" class=\"content-image\" />\n\nhttps://example.com/apply-block-content-filters",
130+
self::TEST_SHORTCODE
131+
);
132+
133+
$output = _wp_apply_block_content_filters( $content, 'test-context' );
134+
135+
$this->assertStringContainsString(
136+
'This shouldn&#8217;t use a straight apostrophe',
137+
$output,
138+
'wptexturize() should convert straight apostrophes to curly apostrophe entities.'
139+
);
140+
$this->assertStringContainsString(
141+
'class="wp-smiley"',
142+
$output,
143+
'convert_smilies() should process smilies in the filtered content.'
144+
);
145+
$this->assertStringContainsString(
146+
'<div class="apply-block-content-filters-block">Shortcode block output</div>',
147+
$output,
148+
'do_shortcode() should run before do_blocks() so block markup returned by a shortcode is rendered.'
149+
);
150+
$this->assertStringNotContainsString(
151+
'<p><div class="apply-block-content-filters-block">Shortcode block output</div></p>',
152+
$output,
153+
'shortcode_unautop() should remove paragraph wrappers around standalone shortcodes.'
154+
);
155+
$this->assertStringContainsString(
156+
'<div class="apply-block-content-filters-block">Direct block output</div>',
157+
$output,
158+
'do_blocks() should render block markup in the filtered content.'
159+
);
160+
$this->assertStringNotContainsString(
161+
'<!-- wp:tests/apply-block-content-filters',
162+
$output,
163+
'do_blocks() should remove block comments from rendered output.'
164+
);
165+
$this->assertStringContainsString(
166+
'data-filtered="yes"',
167+
$output,
168+
'wp_filter_content_tags() should filter image tags in the filtered content.'
169+
);
170+
$this->assertSame(
171+
'test-context',
172+
$this->filtered_image_context,
173+
'wp_filter_content_tags() should receive the context passed to _wp_apply_block_content_filters().'
174+
);
175+
$this->assertStringContainsString(
176+
'<div class="apply-block-content-filters-embed">Embedded content</div>',
177+
$output,
178+
'WP_Embed::autoembed() should process URLs on their own line.'
179+
);
180+
$this->assertStringNotContainsString(
181+
'https://example.com/apply-block-content-filters',
182+
$output,
183+
'WP_Embed::autoembed() should replace the original URL with embed markup.'
184+
);
185+
}
186+
187+
/**
188+
* Tests that seen IDs are set during block rendering and cleared afterward.
189+
*
190+
* @ticket 65586
191+
*/
192+
public function test_marks_and_clears_seen_id() {
193+
$seen_ids = array();
194+
$seen_ids_during_render = null;
195+
196+
unregister_block_type( self::TEST_BLOCK_NAME );
197+
register_block_type(
198+
self::TEST_BLOCK_NAME,
199+
array(
200+
'render_callback' => static function () use ( &$seen_ids, &$seen_ids_during_render ) {
201+
$seen_ids_during_render = $seen_ids;
202+
203+
return '<div class="apply-block-content-filters-block">Nested block rendered while recursion guard was active</div>';
204+
},
205+
)
206+
);
207+
208+
$output = _wp_apply_block_content_filters(
209+
'<!-- wp:tests/apply-block-content-filters /-->',
210+
'test-context',
211+
$seen_ids,
212+
'test-id'
213+
);
214+
215+
$this->assertStringContainsString(
216+
'Nested block rendered while recursion guard was active',
217+
$output,
218+
'The test block should render while the ID is marked as seen.'
219+
);
220+
$this->assertSame(
221+
array( 'test-id' => true ),
222+
$seen_ids_during_render,
223+
'The ID should be marked as seen while do_blocks() renders nested blocks.'
224+
);
225+
$this->assertSame(
226+
array(),
227+
$seen_ids,
228+
'The seen ID should be cleared after do_blocks() completes.'
229+
);
230+
}
231+
232+
/**
233+
* Tests that seen IDs are cleared when block rendering throws an exception.
234+
*
235+
* @ticket 65586
236+
*/
237+
public function test_clears_seen_id_when_block_rendering_throws() {
238+
$seen_ids = array();
239+
240+
try {
241+
_wp_apply_block_content_filters(
242+
'<!-- wp:tests/apply-block-content-filters-exception /-->',
243+
'test-context',
244+
$seen_ids,
245+
'test-id'
246+
);
247+
$this->fail( 'Expected block rendering to throw an exception.' );
248+
} catch ( Exception $exception ) {
249+
$this->assertSame(
250+
'Block rendering failed.',
251+
$exception->getMessage(),
252+
'The exception from the nested block render callback should be rethrown.'
253+
);
254+
}
255+
256+
$this->assertSame(
257+
array(),
258+
$seen_ids,
259+
'The seen ID should be cleared when do_blocks() throws.'
260+
);
261+
}
262+
}

0 commit comments

Comments
 (0)