| | 463 | /** |
| | 464 | * Ensures that an attribute added via set_attribute() is reported by |
| | 465 | * get_attribute_names_with_prefix() without first flushing the updates |
| | 466 | * through get_updated_html(). |
| | 467 | * |
| | 468 | * @ticket 64567 |
| | 469 | * |
| | 470 | * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix |
| | 471 | */ |
| | 472 | public function test_get_attribute_names_with_prefix_reflects_set_attribute_without_flushing() { |
| | 473 | $processor = new WP_HTML_Tag_Processor( '<div data-foo="bar">Test</div>' ); |
| | 474 | $processor->next_tag(); |
| | 475 | $processor->set_attribute( 'data-test-id', '14' ); |
| | 476 | |
| | 477 | $this->assertSame( |
| | 478 | array( 'data-foo', 'data-test-id' ), |
| | 479 | $processor->get_attribute_names_with_prefix( 'data-' ), |
| | 480 | 'Enqueued attribute added via set_attribute() was not reflected before flushing updates.' |
| | 481 | ); |
| | 482 | } |
| | 483 | |
| | 484 | /** |
| | 485 | * Ensures that an attribute removed via remove_attribute() is no longer |
| | 486 | * reported by get_attribute_names_with_prefix() without first flushing the |
| | 487 | * updates through get_updated_html(). |
| | 488 | * |
| | 489 | * @ticket 64567 |
| | 490 | * |
| | 491 | * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix |
| | 492 | */ |
| | 493 | public function test_get_attribute_names_with_prefix_reflects_remove_attribute_without_flushing() { |
| | 494 | $processor = new WP_HTML_Tag_Processor( '<div data-foo="bar" data-keep="1">Test</div>' ); |
| | 495 | $processor->next_tag(); |
| | 496 | $processor->remove_attribute( 'data-foo' ); |
| | 497 | |
| | 498 | $this->assertSame( |
| | 499 | array( 'data-keep' ), |
| | 500 | $processor->get_attribute_names_with_prefix( 'data-' ), |
| | 501 | 'Enqueued attribute removal via remove_attribute() was not reflected before flushing updates.' |
| | 502 | ); |
| | 503 | } |
| | 504 | |
| | 505 | /** |
| | 506 | * Ensures that a class enqueued via add_class() surfaces the `class` |
| | 507 | * attribute in get_attribute_names_with_prefix() without first flushing the |
| | 508 | * updates through get_updated_html(). |
| | 509 | * |
| | 510 | * @ticket 64567 |
| | 511 | * |
| | 512 | * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix |
| | 513 | */ |
| | 514 | public function test_get_attribute_names_with_prefix_reflects_added_class_without_flushing() { |
| | 515 | $processor = new WP_HTML_Tag_Processor( '<div data-foo="bar">Test</div>' ); |
| | 516 | $processor->next_tag(); |
| | 517 | $processor->add_class( 'highlight' ); |
| | 518 | |
| | 519 | $this->assertSame( |
| | 520 | array( 'class' ), |
| | 521 | $processor->get_attribute_names_with_prefix( 'class' ), |
| | 522 | 'Enqueued class attribute created via add_class() was not reflected before flushing updates.' |
| | 523 | ); |
| | 524 | } |
| | 525 | |
| | 526 | /** |
| | 527 | * Ensures get_attribute_names_with_prefix() agrees with get_attribute() |
| | 528 | * after pending updates, returning each name once with no stale entries. |
| | 529 | * |
| | 530 | * @ticket 64567 |
| | 531 | * |
| | 532 | * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix |
| | 533 | */ |
| | 534 | public function test_get_attribute_names_with_prefix_agrees_with_get_attribute_after_updates() { |
| | 535 | $processor = new WP_HTML_Tag_Processor( '<div data-a="1" data-b="2" data-c="3">Test</div>' ); |
| | 536 | $processor->next_tag(); |
| | 537 | $processor->set_attribute( 'data-b', 'updated' ); // Change an existing attribute. |
| | 538 | $processor->remove_attribute( 'data-c' ); // Remove an existing attribute. |
| | 539 | $processor->set_attribute( 'data-d', 'new' ); // Add a new attribute. |
| | 540 | |
| | 541 | $names = $processor->get_attribute_names_with_prefix( 'data-' ); |
| | 542 | sort( $names ); |
| | 543 | |
| | 544 | $this->assertSame( |
| | 545 | array( 'data-a', 'data-b', 'data-d' ), |
| | 546 | $names, |
| | 547 | 'Reported attribute names did not reflect the enqueued changes before flushing updates.' |
| | 548 | ); |
| | 549 | |
| | 550 | foreach ( $names as $name ) { |
| | 551 | $this->assertNotNull( |
| | 552 | $processor->get_attribute( $name ), |
| | 553 | "get_attribute_names_with_prefix() reported '{$name}' but get_attribute() reports it as absent." |
| | 554 | ); |
| | 555 | } |
| | 556 | |
| | 557 | $this->assertNull( |
| | 558 | $processor->get_attribute( 'data-c' ), |
| | 559 | 'Removed attribute data-c should be null via get_attribute().' |
| | 560 | ); |
| | 561 | $this->assertNotContains( |
| | 562 | 'data-c', |
| | 563 | $names, |
| | 564 | 'Removed attribute data-c should not appear in get_attribute_names_with_prefix().' |
| | 565 | ); |
| | 566 | } |
| | 567 | |
| | 568 | /** |
| | 569 | * Ensures the internal 'modifiable text' lexical update enqueued by |
| | 570 | * set_modifiable_text() does not leak into get_attribute_names_with_prefix() |
| | 571 | * as if it were an attribute name. |
| | 572 | * |
| | 573 | * The update is stored in the same lexical_updates array as attribute |
| | 574 | * changes but under the string key 'modifiable text', so a naive skip that |
| | 575 | * only ignores integer keys would report it for any matching prefix. |
| | 576 | * |
| | 577 | * @ticket 64567 |
| | 578 | * |
| | 579 | * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix |
| | 580 | */ |
| | 581 | public function test_get_attribute_names_with_prefix_ignores_modifiable_text_update() { |
| | 582 | $processor = new WP_HTML_Tag_Processor( '<script id="x">old</script>' ); |
| | 583 | $processor->next_tag(); |
| | 584 | $processor->set_modifiable_text( 'new content' ); |
| | 585 | |
| | 586 | $this->assertSame( |
| | 587 | array( 'id' ), |
| | 588 | $processor->get_attribute_names_with_prefix( '' ), |
| | 589 | "The internal 'modifiable text' update key leaked as an attribute name." |
| | 590 | ); |
| | 591 | } |
| | 592 | |