From b08dd682275dc436ef9c0bd51b8f1535bcef1396 Mon Sep 17 00:00:00 2001
From: Mariusz Szatkowski <19728801+wppoland@users.noreply.github.com>
Date: Fri, 28 Aug 2026 16:22:27 +0200
Subject: [PATCH] HTML API: Add a serialization regression test for enqueued
attribute updates.
[62960] made get_attribute_names_with_prefix() respect enqueued updates, but
its tests only cover WP_HTML_Tag_Processor. WP_HTML_Processor::serialize_token()
consumed the same stale name list while reading values through get_attribute(),
so a removed attribute was re-emitted as a value-less attribute and an added one
was dropped. That path gained no coverage.
Fails at [62960]^ with '
', passes on trunk.
Both assertions pin the full serialized output. A newly added attribute is
emitted before the existing ones, so the set_attribute() case is deterministic
and does not need a substring match (per @irozum's review).
Rebased onto trunk.
See #64567.
---
.../html-api/wpHtmlProcessor-serialize.php | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
index 1ce8855305ae9..100ee38cab2ea 100644
--- a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
+++ b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
@@ -853,4 +853,39 @@ public static function data_provider_normalize_special_leading_newline_cases():
),
);
}
+
+ /**
+ * Ensures that serialize_token() reflects enqueued attribute updates instead
+ * of emitting removed attributes as value-less attributes or omitting added ones.
+ *
+ * serialize_token() iterates the names from get_attribute_names_with_prefix( '' )
+ * but reads each value through get_attribute(). Before #64567 the name list did not
+ * reflect enqueued updates while the values did, so a removed attribute survived in
+ * the output as a boolean attribute and an added attribute was dropped entirely.
+ *
+ * @ticket 64567
+ *
+ * @covers WP_HTML_Processor::serialize_token
+ */
+ public function test_serialize_token_reflects_enqueued_attribute_updates() {
+ $processor = WP_HTML_Processor::create_fragment( '
Text
' );
+ $processor->next_tag();
+ $processor->remove_attribute( 'onclick' );
+
+ $this->assertSame(
+ '
',
+ $processor->serialize_token(),
+ 'An attribute enqueued for removal was serialized as a value-less attribute.'
+ );
+
+ $processor = WP_HTML_Processor::create_fragment( '
Text
' );
+ $processor->next_tag();
+ $processor->set_attribute( 'id', 'new' );
+
+ $this->assertSame(
+ '
',
+ $processor->serialize_token(),
+ 'An attribute enqueued via set_attribute() was not serialized. A newly added attribute is emitted before the existing ones.'
+ );
+ }
}