@@ -464,6 +464,214 @@ public function test_get_attribute_names_with_prefix_returns_attribute_added_by_
464464 );
465465 }
466466
467+ /**
468+ * Ensures that a new attribute added via set_attribute() is reported by
469+ * get_attribute_names_with_prefix() immediately after being added.
470+ *
471+ * @ticket 64567
472+ *
473+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
474+ */
475+ public function test_get_attribute_names_with_prefix_immediately_reflects_new_attributes () {
476+ $ processor = new WP_HTML_Tag_Processor ( '<div existing> ' );
477+ $ processor ->next_tag ();
478+
479+ $ this ->assertSame (
480+ array ( 'existing ' ),
481+ $ processor ->get_attribute_names_with_prefix ( '' ),
482+ 'Failed to report existing attribute names: check test setup. '
483+ );
484+
485+ $ processor ->set_attribute ( 'new ' , true );
486+
487+ $ this ->assertSame (
488+ array ( 'new ' , 'existing ' ),
489+ $ processor ->get_attribute_names_with_prefix ( '' ),
490+ 'Failed to report newly-added attribute. '
491+ );
492+
493+ $ this ->assertSame (
494+ array ( 'existing ' ),
495+ $ processor ->get_attribute_names_with_prefix ( 'exist ' ),
496+ 'Should have only reported existing attribute matching given prefix. '
497+ );
498+ }
499+
500+ /**
501+ * Ensures that changes enqueued for modifiable text do not get reported as added attributes.
502+ *
503+ * This is a fairly-specific test against an internal implementation detail, but is worth
504+ * adding to catch potential regressions since modifiable text updates share a namespace with
505+ * attribute updates.
506+ *
507+ * @ticket 64567
508+ *
509+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
510+ */
511+ public function test_get_attribute_names_with_prefix_ignores_immediately_added_modifiable_text () {
512+ $ processor = new WP_HTML_Tag_Processor ( '<title existing></title> ' );
513+ $ processor ->next_tag ();
514+
515+ $ this ->assertSame (
516+ array ( 'existing ' ),
517+ $ processor ->get_attribute_names_with_prefix ( '' ),
518+ 'Failed to report existing attribute names: check test setup. '
519+ );
520+
521+ $ processor ->set_modifiable_text ( 'content! ' );
522+
523+ $ this ->assertSame (
524+ array ( 'existing ' ),
525+ $ processor ->get_attribute_names_with_prefix ( '' ),
526+ 'Failed to report that the attributes were unchanged. '
527+ );
528+ }
529+
530+ /**
531+ * Ensures that an attribute removed via remove_attribute() is no longer reported
532+ * by get_attribute_names_with_prefix() immediately after being removed.
533+ *
534+ * @ticket 64567
535+ *
536+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
537+ */
538+ public function test_get_attribute_names_with_prefix_immediately_reflects_removed_attributes () {
539+ $ processor = new WP_HTML_Tag_Processor ( '<div existing data-removed> ' );
540+ $ processor ->next_tag ();
541+
542+ $ this ->assertSame (
543+ array ( 'existing ' , 'data-removed ' ),
544+ $ processor ->get_attribute_names_with_prefix ( '' ),
545+ 'Failed to report all existing attributes: check test setup. '
546+ );
547+
548+ $ processor ->remove_attribute ( 'data-removed ' );
549+
550+ $ this ->assertSame (
551+ array (),
552+ $ processor ->get_attribute_names_with_prefix ( 'data- ' ),
553+ 'Expected no custom data attributes after removing the only one. '
554+ );
555+
556+ $ this ->assertSame (
557+ array ( 'existing ' ),
558+ $ processor ->get_attribute_names_with_prefix ( '' ),
559+ 'Expected to report only the attribute which wasn’t removed. '
560+ );
561+ }
562+
563+ /**
564+ * Ensures that when the `class` attribute is newly added via add_class(), that
565+ * it’s reported by get_attribute_names_with_prefix() immediately after being added.
566+ *
567+ * @ticket 64567
568+ *
569+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
570+ */
571+ public function test_get_attribute_names_with_prefix_immediately_reflects_class_after_adding_classes () {
572+ $ processor = new WP_HTML_Tag_Processor ( '<div existing> ' );
573+ $ processor ->next_tag ();
574+
575+ $ this ->assertSame (
576+ array ( 'existing ' ),
577+ $ processor ->get_attribute_names_with_prefix ( '' ),
578+ 'Expected to only report the existing attribute: check test setup. '
579+ );
580+
581+ $ processor ->add_class ( 'added ' );
582+
583+ $ this ->assertSame (
584+ array ( 'class ' ),
585+ $ processor ->get_attribute_names_with_prefix ( 'class ' ),
586+ 'Failed to report the newly-added `class` attribute. '
587+ );
588+ }
589+
590+ /**
591+ * Ensures that when the `class` attribute is emptied via remove_class(), that
592+ * it’s reported by get_attribute_names_with_prefix() immediately after being added.
593+ *
594+ * @ticket 64567
595+ *
596+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
597+ */
598+ public function test_get_attribute_names_with_prefix_immediately_reflects_class_after_removing_all_classes () {
599+ $ processor = new WP_HTML_Tag_Processor ( '<div class="red green" existing class class=duplicate> ' );
600+ $ processor ->next_tag ();
601+
602+ $ this ->assertSame (
603+ array ( 'class ' , 'existing ' ),
604+ $ processor ->get_attribute_names_with_prefix ( '' ),
605+ 'Expected to find proper existing attributes: check test setup. '
606+ );
607+
608+ $ processor ->remove_class ( 'red ' );
609+
610+ $ this ->assertSame (
611+ array ( 'class ' , 'existing ' ),
612+ $ processor ->get_attribute_names_with_prefix ( '' ),
613+ 'Should have the same attributes after removing one of two classes. '
614+ );
615+
616+ $ processor ->remove_class ( 'green ' );
617+
618+ $ this ->assertSame (
619+ array ( 'existing ' ),
620+ $ processor ->get_attribute_names_with_prefix ( '' ),
621+ 'Should have removed the `class` attribute after removing all class names. '
622+ );
623+ }
624+
625+ /**
626+ * Ensures get_attribute_names_with_prefix() agrees with get_attribute()
627+ * after pending updates, returning each name once with no stale entries.
628+ *
629+ * @ticket 64567
630+ *
631+ * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
632+ */
633+ public function test_get_attribute_names_with_prefix_immediately_agrees_with_get_attribute_after_updates () {
634+ $ processor = new WP_HTML_Tag_Processor ( '<div data-keep="1" data-drop="2">Test</div> ' );
635+ $ processor ->next_tag ();
636+
637+ $ this ->assertSame (
638+ array ( 'data-keep ' , 'data-drop ' ),
639+ $ processor ->get_attribute_names_with_prefix ( '' ),
640+ 'Failed to find expected existing attributes: check test setup. '
641+ );
642+
643+ $ this ->assertSame (
644+ '1 ' ,
645+ $ processor ->get_attribute ( 'data-keep ' ),
646+ 'Failed to find expected existing `data-keep` attribute value: check test setup. '
647+ );
648+
649+ $ this ->assertSame (
650+ '2 ' ,
651+ $ processor ->get_attribute ( 'data-drop ' ),
652+ 'Failed to find expected existing `data-drop` attribute value: check test setup. '
653+ );
654+
655+ $ processor ->set_attribute ( 'data-keep ' , 'updated ' );
656+ $ processor ->set_attribute ( 'data-add ' , 'new ' );
657+ $ processor ->remove_attribute ( 'data-drop ' );
658+
659+ $ names = $ processor ->get_attribute_names_with_prefix ( 'data- ' );
660+
661+ $ this ->assertSame (
662+ array ( 'data-add ' , 'data-keep ' ),
663+ $ names ,
664+ 'Failed to report the expected attribute names after removing and adding attributes. '
665+ );
666+
667+ foreach ( $ names as $ name ) {
668+ $ this ->assertNotNull (
669+ $ processor ->get_attribute ( $ name ),
670+ "get_attribute_names_with_prefix() reported ' {$ name }' but get_attribute() did not agree. "
671+ );
672+ }
673+ }
674+
467675 /**
468676 * @ticket 56299
469677 *
0 commit comments