@@ -16,6 +16,110 @@ class Tests_HtmlApi_WpHtmlProcessorSemanticRules extends WP_UnitTestCase {
1616 * RULES FOR "IN BODY" MODE
1717 *******************************************************************/
1818
19+ /**
20+ * Verifies that when encountering an end tag for which there is no corresponding
21+ * element in scope, that it skips the tag entirely.
22+ *
23+ * @ticket {TICKET_NUMBER}
24+ *
25+ * @since 6.4.0
26+ *
27+ * @throws Exception
28+ */
29+ public function test_in_body_skips_unexpected_button_closer () {
30+ $ p = WP_HTML_Processor::createFragment ( '<div>Test</button></div> ' );
31+
32+ $ p ->step ();
33+ $ this ->assertEquals ( 'DIV ' , $ p ->get_tag (), 'Did not stop at initial DIV tag. ' );
34+ $ this ->assertFalse ( $ p ->is_tag_closer (), 'Did not find that initial DIV tag is an opener. ' );
35+
36+ $ this ->assertTrue ( $ p ->step (), 'Found no further tags when it should have found the closing DIV ' );
37+ $ this ->assertEquals ( 'DIV ' , $ p ->get_tag (), "Did not skip unexpected BUTTON; stopped at {$ p ->get_tag ()}. " );
38+ $ this ->assertTrue ( $ p ->is_tag_closer (), 'Did not find that the terminal DIV tag is a closer. ' );
39+ }
40+
41+ /**
42+ * Verifies insertion of a BUTTON element when no existing BUTTON is already in scope.
43+ *
44+ * @ticket 58961
45+ *
46+ * @since 6.4.0
47+ *
48+ * @throws WP_HTML_Unsupported_Exception
49+ */
50+ public function test_in_body_button_with_no_button_in_scope () {
51+ $ p = WP_HTML_Processor::createFragment ( '<div><p>Click the button <button one>here</button>!</p></div><button two>not here</button> ' );
52+
53+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected first button. ' );
54+ $ this ->assertTrue ( $ p ->get_attribute ( 'one ' ), 'Failed to match expected attribute on first button. ' );
55+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'P ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for first button. ' );
56+
57+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected second button. ' );
58+ $ this ->assertTrue ( $ p ->get_attribute ( 'two ' ), 'Failed to match expected attribute on second button. ' );
59+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for second button. ' );
60+ }
61+
62+ /**
63+ * Verifies what when inserting a BUTTON element, when a BUTTON is already in scope,
64+ * that the open button is closed with all other elements inside of it.
65+ *
66+ * @ticket 58961
67+ *
68+ * @since 6.4.0
69+ *
70+ * @throws WP_HTML_Unsupported_Exception
71+ */
72+ public function test_in_body_button_with_button_in_scope_as_parent () {
73+ $ p = WP_HTML_Processor::createFragment ( '<div><p>Click the button <button one>almost<button two>here</button>!</p></div><button three>not here</button> ' );
74+
75+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected first button. ' );
76+ $ this ->assertTrue ( $ p ->get_attribute ( 'one ' ), 'Failed to match expected attribute on first button. ' );
77+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'P ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for first button. ' );
78+
79+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected second button. ' );
80+ $ this ->assertTrue ( $ p ->get_attribute ( 'two ' ), 'Failed to match expected attribute on second button. ' );
81+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'P ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for second button. ' );
82+
83+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected third button. ' );
84+ $ this ->assertTrue ( $ p ->get_attribute ( 'three ' ), 'Failed to match expected attribute on third button. ' );
85+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for third button. ' );
86+ }
87+
88+ /**
89+ * Verifies what when inserting a BUTTON element, when a BUTTON is already in scope,
90+ * that the open button is closed with all other elements inside of it, even if the
91+ * BUTTON in scope is not a direct parent of the new BUTTON element.
92+ *
93+ * @ticket 58961
94+ *
95+ * @since 6.4.0
96+ *
97+ * @throws WP_HTML_Unsupported_Exception
98+ */
99+ public function test_in_body_button_with_button_in_scope_as_ancestor () {
100+ $ p = WP_HTML_Processor::createFragment ( '<div><button one><p>Click the button <span><button two>here</button>!</span></p></div><button three>not here</button> ' );
101+
102+ // This button finds itself normally nesting inside the DIV.
103+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected first button. ' );
104+ $ this ->assertTrue ( $ p ->get_attribute ( 'one ' ), 'Failed to match expected attribute on first button. ' );
105+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for first button. ' );
106+
107+ /*
108+ * Because the second button appears while a BUTTON is in scope, it generates implied end tags and closes
109+ * the BUTTON, P, and SPAN elements. It looks like the BUTTON is inside the SPAN, but we have another case
110+ * of an unexpected closing SPAN tag because the SPAN was closed by the second BUTTON. This element finds
111+ * itself a child of the most-recent open element above the most-recent BUTTON, or the DIV.
112+ */
113+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected second button. ' );
114+ $ this ->assertTrue ( $ p ->get_attribute ( 'two ' ), 'Failed to match expected attribute on second button. ' );
115+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for second button. ' );
116+
117+ // The third button is back to normal, because everything has been implicitly or explicitly closed by now.
118+ $ this ->assertTrue ( $ p ->next_tag ( 'BUTTON ' ), 'Could not find expected third button. ' );
119+ $ this ->assertTrue ( $ p ->get_attribute ( 'three ' ), 'Failed to match expected attribute on third button. ' );
120+ $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'BUTTON ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting for third button. ' );
121+ }
122+
19123 /*
20124 * Verifies that when "in body" and encountering "any other end tag"
21125 * that the HTML processor ignores the end tag if there's a special
@@ -57,7 +161,7 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element
57161 $ this ->assertSame ( 'CODE ' , $ p ->get_tag (), "Expected to start test on CODE element but found {$ p ->get_tag ()} instead. " );
58162 $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' , 'SPAN ' , 'CODE ' ), $ p ->get_breadcrumbs (), 'Failed to produce expected DOM nesting. ' );
59163
60- $ this ->assertTrue ( $ p ->next_tag (), 'Failed to advance past CODE tag to expected SPAN closer. ' );
164+ $ this ->assertTrue ( $ p ->step (), 'Failed to advance past CODE tag to expected SPAN closer. ' );
61165 $ this ->assertTrue ( $ p ->is_tag_closer (), 'Expected to find closing SPAN, but found opener instead. ' );
62166 $ this ->assertSame ( array ( 'HTML ' , 'BODY ' , 'DIV ' ), $ p ->get_breadcrumbs (), 'Failed to advance past CODE tag to expected DIV opener. ' );
63167
0 commit comments