From dbb9487255f2808eb5543cbc2dfa26b8757d3d04 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 14 Nov 2022 17:32:41 -0700 Subject: [PATCH] Tag Processor: Merge independent tests into single file --- .../WP_HTML_Tag_Processor_Isolated_Test.php | 145 -- phpunit/html/WP_HTML_Tag_Processor_Test.php | 1187 ----------------- ...est.php => wp-html-tag-processor-test.php} | 23 +- .../html/wp-html-tag-processor-wp-test.php | 91 -- 4 files changed, 2 insertions(+), 1444 deletions(-) delete mode 100644 phpunit/html/WP_HTML_Tag_Processor_Isolated_Test.php delete mode 100644 phpunit/html/WP_HTML_Tag_Processor_Test.php rename phpunit/html/{wp-html-tag-processor-standalone-test.php => wp-html-tag-processor-test.php} (98%) delete mode 100644 phpunit/html/wp-html-tag-processor-wp-test.php diff --git a/phpunit/html/WP_HTML_Tag_Processor_Isolated_Test.php b/phpunit/html/WP_HTML_Tag_Processor_Isolated_Test.php deleted file mode 100644 index d593c5d2f4fe92..00000000000000 --- a/phpunit/html/WP_HTML_Tag_Processor_Isolated_Test.php +++ /dev/null @@ -1,145 +0,0 @@ -' ); - - $this->expectException( Exception::class ); - - $p->next_tag(); - $p->set_attribute( $attribute_name, 'test' ); - - $this->assertEquals( '', (string) $p ); - } - - /** - * Attribute names with invalid characters should be rejected. - * - * When WP_DEBUG isn't set we want to quietly fail to set the - * invalid attribute to avoid breaking the HTML and to do so - * without breaking the entire page. - * - * @dataProvider data_invalid_attribute_names - * @covers set_attribute - */ - public function test_set_attribute_silently_fails_when_given_invalid_attribute_names_outside_of_debug_mode( $attribute_name ) { - $p = new WP_HTML_Tag_Processor( '' ); - - $p->next_tag(); - $p->set_attribute( $attribute_name, 'test' ); - - $this->assertEquals( '', (string) $p ); - } - - /** - * Data provider with invalid HTML attribute names. - * - * @return array { - * @type string $attribute_name Text considered invalid for HTML attribute names. - * } - */ - public function data_invalid_attribute_names() { - return array( - 'controls_null' => array( "i\x00d" ), - 'controls_newline' => array( "\nbroken-expectations" ), - 'space' => array( 'aria label' ), - 'double-quote' => array( '"id"' ), - 'single-quote' => array( "'id'" ), - 'greater-than' => array( 'sneaky>script' ), - 'solidus' => array( 'data/test-id' ), - 'equals' => array( 'checked=checked' ), - 'noncharacters_1' => array( html_entity_decode( 'anything﷐' ) ), - 'noncharacters_2' => array( html_entity_decode( 'te￿st' ) ), - 'noncharacters_3' => array( html_entity_decode( 'te𯿾st' ) ), - 'noncharacters_4' => array( html_entity_decode( 'te󟿿st' ) ), - 'noncharacters_5' => array( html_entity_decode( '􏿾' ) ), - 'wp_no_lt' => array( 'id array( 'class<script' ), - ); - } - - /** - * Attribute names with only valid characters should not be rejected. - * - * > Attributes have a name and a value. Attribute names must - * > consist of one or more characters other than controls, - * > U+0020 SPACE, U+0022 ("), U+0027 ('), U+003E (>), - * > U+002F (/), U+003D (=), and noncharacters. - * - * @see https://html.spec.whatwg.org/#attributes-2 - * - * @dataProvider data_valid_attribute_names - * @covers set_attribute - */ - public function test_set_attribute_does_not_reject_valid_attribute_names( $attribute_name ) { - define( 'WP_DEBUG', true ); - $p = new WP_HTML_Tag_Processor( '' ); - - $p->next_tag(); - $p->set_attribute( $attribute_name, 'test' ); - - $this->assertEquals( "", (string) $p ); - } - - /** - * Data provider with valid HTML attribute names. - * - * @return array { - * @type string $attribute_name Text considered valid for HTML attribute names. - * } - */ - public function data_valid_attribute_names() { - return array( - 'ascii_letters' => array( 'abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ' ), - 'ascii_numbers' => array( '0123456789' ), - 'symbols' => array( '!@#$%^*()[]{};:\\||,.?`~£§±' ), - 'emoji' => array( '❌' ), - 'utf8_diacritics' => array( 'ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆĞÍÌÎÏİŇÑÓÖÒÔÕØŘŔŠŞŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇğíìîïıňñóöòôõøðřŕšşťúůüùûýÿžþÞĐđßÆa' ), - 'hebrew_accents' => array( html_entity_decode( '֝a' ) ), - // See https://arxiv.org/abs/2111.00169. - 'rtl_magic' => array( html_entity_decode( '⁧⁦abc⁩⁦def⁩⁩' ) ), - // Only a single unicode "noncharacter" should be rejected. Specific byte segments used in the "noncharacter" sequence are valid. - 'noncharacter_segments' => array( "\xFF\xFE" ), - ); - } - -} diff --git a/phpunit/html/WP_HTML_Tag_Processor_Test.php b/phpunit/html/WP_HTML_Tag_Processor_Test.php deleted file mode 100644 index cec0fa555300a0..00000000000000 --- a/phpunit/html/WP_HTML_Tag_Processor_Test.php +++ /dev/null @@ -1,1187 +0,0 @@ -Text'; - const HTML_WITH_CLASSES = '
Text
'; - const HTML_MALFORMED = '
Back to notifications
'; - - /** - * @ticket 56299 - * - * @covers get_tag - */ - public function test_get_tag_returns_null_before_finding_tags() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertNull( $p->get_tag() ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_tag - */ - public function test_get_tag_returns_null_when_not_in_open_tag() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertFalse( $p->next_tag( 'p' ), 'Querying a non-existing tag did not return false' ); - $this->assertNull( $p->get_tag(), 'Accessing a non-existing tag did not return null' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_tag - */ - public function test_get_tag_returns_open_tag_name() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertTrue( $p->next_tag( 'div' ), 'Querying an existing tag did not return true' ); - $this->assertSame( 'DIV', $p->get_tag(), 'Accessing an existing tag name did not return "div"' ); - } - - /** - * @ticket 56299 - * - * @covers get_attribute - */ - public function test_get_attribute_returns_null_before_finding_tags() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertNull( $p->get_attribute( 'class' ) ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_get_attribute_returns_null_when_not_in_open_tag() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertFalse( $p->next_tag( 'p' ), 'Querying a non-existing tag did not return false' ); - $this->assertNull( $p->get_attribute( 'class' ), 'Accessing an attribute of a non-existing tag did not return null' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_get_attribute_returns_null_when_attribute_missing() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertTrue( $p->next_tag( 'div' ), 'Querying an existing tag did not return true' ); - $this->assertNull( $p->get_attribute( 'test-id' ), 'Accessing a non-existing attribute did not return null' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_get_attribute_returns_attribute_value() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertTrue( $p->next_tag( 'div' ), 'Querying an existing tag did not return true' ); - $this->assertSame( 'test', $p->get_attribute( 'class' ), 'Accessing a class="test" attribute value did not return "test"' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_get_attribute_returns_true_for_boolean_attribute() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertTrue( $p->next_tag( array( 'class_name' => 'test' ) ), 'Querying an existing tag did not return true' ); - $this->assertTrue( $p->get_attribute( 'enabled' ), 'Accessing a boolean "enabled" attribute value did not return true' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_get_attribute_returns_string_for_truthy_attributes() { - $p = new WP_HTML_Tag_Processor( '' ); - $this->assertTrue( $p->next_tag( array() ), 'Querying an existing tag did not return true' ); - $this->assertSame( 'enabled', $p->get_attribute( 'enabled' ), 'Accessing a boolean "enabled" attribute value did not return true' ); - $this->assertSame( '1', $p->get_attribute( 'checked' ), 'Accessing a checked=1 attribute value did not return "1"' ); - $this->assertSame( 'true', $p->get_attribute( 'hidden' ), 'Accessing a hidden="true" attribute value did not return "true"' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers get_attribute - */ - public function test_attributes_parser_treats_slash_as_attribute_separator() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $this->assertTrue( $p->next_tag( array() ), 'Querying an existing tag did not return true' ); - $this->assertTrue( $p->get_attribute( 'a' ), 'Accessing an existing attribute did not return true' ); - $this->assertTrue( $p->get_attribute( 'b' ), 'Accessing an existing attribute did not return true' ); - $this->assertTrue( $p->get_attribute( 'c' ), 'Accessing an existing attribute did not return true' ); - $this->assertTrue( $p->get_attribute( 'd' ), 'Accessing an existing attribute did not return true' ); - $this->assertSame( 'test', $p->get_attribute( 'e' ), 'Accessing an existing e="test" did not return "test"' ); - } - - /** - * @ticket 56299 - * - * @covers __toString - */ - public function test_tostring_applies_the_updates_so_far_and_keeps_the_processor_on_the_current_tag() { - $p = new WP_HTML_Tag_Processor( '
Test
' ); - $p->next_tag(); - $p->remove_attribute( 'id' ); - - $p->next_tag(); - $p->set_attribute( 'id', 'div-id-1' ); - $p->add_class( 'new_class_1' ); - $this->assertSame( - '
Test
', - (string) $p, - 'Calling __toString after updating the attributes of the second tag returned different HTML than expected' - ); - - $p->set_attribute( 'id', 'div-id-2' ); - $p->add_class( 'new_class_2' ); - $this->assertSame( - '
Test
', - (string) $p, - 'Calling __toString after updating the attributes of the second tag for the second time returned different HTML than expected' - ); - - $p->next_tag(); - $p->remove_attribute( 'id' ); - $this->assertSame( - '
Test
', - (string) $p, - 'Calling __toString after removing the id attribute of the third tag returned different HTML than expected' - ); - - } - - /** - * @ticket 56299 - * - * @covers __toString - */ - public function test_tostring_without_updating_any_attributes_returns_the_original_html() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $this->assertSame( self::HTML_SIMPLE, (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - */ - public function test_next_tag_with_no_arguments_should_find_the_next_existing_tag() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $this->assertTrue( $p->next_tag(), 'Querying an existing tag did not return true' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - */ - public function test_next_tag_should_return_false_for_a_non_existing_tag() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $this->assertFalse( $p->next_tag( 'p' ), 'Querying a non-existing tag did not return false' ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers __toString - */ - public function test_set_attribute_on_a_non_existing_tag_does_not_change_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $this->assertFalse( $p->next_tag( 'p' ), 'Querying a non-existing tag did not return false' ); - $this->assertFalse( $p->next_tag( 'div' ), 'Querying a non-existing tag did not return false' ); - $p->set_attribute( 'id', 'primary' ); - $this->assertSame( - self::HTML_SIMPLE, - (string) $p, - 'Calling __toString after updating a non-existing tag returned an HTML that was different from the original HTML' - ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_set_attribute_with_a_non_existing_attribute_adds_a_new_attribute_to_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->set_attribute( 'test-attribute', 'test-value' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * According to HTML spec, only the first instance of an attribute counts. - * The other ones are ignored. - * - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_update_first_when_duplicated_attribute() { - $p = new WP_HTML_Tag_Processor( '
Text
' ); - $p->next_tag(); - $p->set_attribute( 'id', 'updated-id' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_set_attribute_with_an_existing_attribute_name_updates_its_value_in_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->set_attribute( 'id', 'new-id' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_next_tag_and_set_attribute_in_a_loop_update_all_tags_in_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - while ( $p->next_tag() ) { - $p->set_attribute( 'data-foo', 'bar' ); - } - - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * Removing an attribute that's listed many times, e.g. `
` should remove - * all its instances and output just `
`. - * - * Today, however, WP_HTML_Tag_Processor only removes the first such attribute. It seems like a corner case - * and introducing additional complexity to correctly handle this scenario doesn't seem to be worth it. - * Let's revisit if and when this becomes a problem. - * - * This test is in place to confirm this behavior, while incorrect, is well-defined. - * - * @ticket 56299 - * - * @covers remove_attribute - * @covers __toString - */ - public function test_remove_first_when_duplicated_attribute() { - $p = new WP_HTML_Tag_Processor( '
Text
' ); - $p->next_tag(); - $p->remove_attribute( 'id' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers remove_attribute - * @covers __toString - */ - public function test_remove_attribute_with_an_existing_attribute_name_removes_it_from_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->remove_attribute( 'id' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers remove_attribute - * @covers __toString - */ - public function test_remove_attribute_with_a_non_existing_attribute_name_does_not_change_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->remove_attribute( 'no-such-attribute' ); - $this->assertSame( self::HTML_SIMPLE, (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_add_class_creates_a_class_attribute_when_there_is_none() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->add_class( 'foo-class' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_calling_add_class_twice_creates_a_class_attribute_with_both_class_names_when_there_is_no_class_attribute() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->add_class( 'foo-class' ); - $p->add_class( 'bar-class' ); - $this->assertSame( '
Text
', (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers remove_class - * @covers __toString - */ - public function test_remove_class_does_not_change_the_markup_when_there_is_no_class_attribute() { - $p = new WP_HTML_Tag_Processor( self::HTML_SIMPLE ); - $p->next_tag(); - $p->remove_class( 'foo-class' ); - $this->assertSame( self::HTML_SIMPLE, (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_add_class_appends_class_names_to_the_existing_class_attribute_when_one_already_exists() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->add_class( 'foo-class' ); - $p->add_class( 'bar-class' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers remove_class - * @covers __toString - */ - public function test_remove_class_removes_a_single_class_from_the_class_attribute_when_one_exists() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->remove_class( 'main' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers remove_class - * @covers __toString - */ - public function test_calling_remove_class_with_all_listed_class_names_removes_the_existing_class_attribute_from_the_markup() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->remove_class( 'main' ); - $p->remove_class( 'with-border' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_add_class_does_not_add_duplicate_class_names() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->add_class( 'with-border' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_add_class_preserves_class_name_order_when_a_duplicate_class_name_is_added() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->add_class( 'main' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers add_class - * @covers __toString - */ - public function test_add_class_when_there_is_a_class_attribute_with_excessive_whitespaces() { - $p = new WP_HTML_Tag_Processor( - '
Text
' - ); - $p->next_tag(); - $p->add_class( 'foo-class' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers remove_class - * @covers __toString - */ - public function test_remove_class_preserves_whitespaces_when_there_is_a_class_attribute_with_excessive_whitespaces() { - $p = new WP_HTML_Tag_Processor( - '
Text
' - ); - $p->next_tag(); - $p->remove_class( 'with-border' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers remove_class - * @covers __toString - */ - public function test_removing_all_classes_removes_the_existing_class_attribute_from_the_markup_even_when_excessive_whitespaces_are_present() { - $p = new WP_HTML_Tag_Processor( - '
Text
' - ); - $p->next_tag(); - $p->remove_class( 'main' ); - $p->remove_class( 'with-border' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * When both set_attribute('class', $value) and add_class( $different_value ) are called, - * the final class name should be $value. In other words, the `add_class` call should be ignored, - * and the `set_attribute` call should win. This holds regardless of the order in which these methods - * are called. - * - * @ticket 56299 - * - * @covers add_class - * @covers set_attribute - * @covers __toString - */ - public function test_set_attribute_takes_priority_over_add_class() { - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->add_class( 'add_class' ); - $p->set_attribute( 'class', 'set_attribute' ); - $this->assertSame( - '
Text
', - (string) $p, - 'Calling __toString after updating first tag\'s attributes did not return the expected HTML' - ); - - $p = new WP_HTML_Tag_Processor( self::HTML_WITH_CLASSES ); - $p->next_tag(); - $p->set_attribute( 'class', 'set_attribute' ); - $p->add_class( 'add_class' ); - $this->assertSame( - '
Text
', - (string) $p, - 'Calling __toString after updating second tag\'s attributes did not return the expected HTML' - ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers remove_attribute - * @covers add_class - * @covers remove_class - * @covers __toString - */ - public function test_advanced_use_case() { - $input = << -
-
-
- - - - - - - -
-
-
-HTML; - - $expected_output = << -
-
-
- - - - - - - -
-
-
-HTML; - - $p = new WP_HTML_Tag_Processor( $input ); - $this->assertTrue( $p->next_tag( 'div' ), 'Querying an existing tag did not return true' ); - $p->set_attribute( 'data-details', '{ "key": "value" }' ); - $p->add_class( 'is-processed' ); - $this->assertTrue( - $p->next_tag( - array( - 'tag_name' => 'div', - 'class_name' => 'BtnGroup', - ) - ), - 'Querying an existing tag did not return true' - ); - $p->remove_class( 'BtnGroup' ); - $p->add_class( 'button-group' ); - $p->add_class( 'Another-Mixed-Case' ); - $this->assertTrue( - $p->next_tag( - array( - 'tag_name' => 'div', - 'class_name' => 'BtnGroup', - ) - ), - 'Querying an existing tag did not return true' - ); - $p->remove_class( 'BtnGroup' ); - $p->add_class( 'button-group' ); - $p->add_class( 'Another-Mixed-Case' ); - $this->assertTrue( - $p->next_tag( - array( - 'tag_name' => 'button', - 'class_name' => 'btn', - 'match_offset' => 3, - ) - ), - 'Querying an existing tag did not return true' - ); - $p->remove_attribute( 'class' ); - $this->assertFalse( $p->next_tag( 'non-existent' ), 'Querying a non-existing tag did not return false' ); - $p->set_attribute( 'class', 'test' ); - $this->assertSame( $expected_output, (string) $p, 'Calling __toString after updating the attributes did not return the expected HTML' ); - } - - /** - * @ticket 56299 - * - * @covers remove_attribute - * @covers set_attribute - * @covers __toString - */ - public function test_correctly_parses_html_attributes_wrapped_in_single_quotation_marks() { - $p = new WP_HTML_Tag_Processor( - '
Text
' - ); - $p->next_tag( - array( - 'tag_name' => 'div', - 'id' => 'first', - ) - ); - $p->remove_attribute( 'id' ); - $p->next_tag( - array( - 'tag_name' => 'span', - 'id' => 'second', - ) - ); - $p->set_attribute( 'id', 'single-quote' ); - $this->assertSame( - '
Text
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_set_attribute_with_value_equals_to_true_adds_a_boolean_html_attribute_with_implicit_value() { - $p = new WP_HTML_Tag_Processor( - '
' - ); - $p->next_tag( 'input' ); - $p->set_attribute( 'checked', true ); - $this->assertSame( - '
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_setting_a_boolean_attribute_to_false_removes_it_from_the_markup() { - $p = new WP_HTML_Tag_Processor( - '
' - ); - $p->next_tag( 'input' ); - $p->set_attribute( 'checked', false ); - $this->assertSame( - '
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_setting_a_missing_attribute_to_false_does_not_change_the_markup() { - $html_input = '
'; - $p = new WP_HTML_Tag_Processor( $html_input ); - $p->next_tag( 'input' ); - $p->set_attribute( 'checked', false ); - $this->assertSame( $html_input, (string) $p ); - } - - /** - * @ticket 56299 - * - * @covers set_attribute - * @covers __toString - */ - public function test_setting_a_boolean_attribute_to_a_string_value_adds_explicit_value_to_the_markup() { - $p = new WP_HTML_Tag_Processor( - '
' - ); - $p->next_tag( 'input' ); - $p->set_attribute( 'checked', 'checked' ); - $this->assertSame( - '
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers get_tag - * @covers next_tag - */ - public function test_unclosed_script_tag_should_not_cause_an_infinite_loop() { - $p = new WP_HTML_Tag_Processor( '
', - ); - - $examples['Simple uppercase script tag'] = array( - '
', - ); - - $examples['Script with a comment opener inside should end at the next script tag closer (dash dash escaped state)'] = array( - '
-->', - ); - - $examples['Script with a comment opener and a script tag opener inside should end two script tag closer later (double escaped state)'] = array( - '
-->', - ); - - $examples['Double escaped script with a tricky opener'] = array( - '">
', - ); - - $examples['Double escaped script with a tricky closer'] = array( - '">
', - ); - - $examples['Double escaped, then escaped, then double escaped'] = array( - '
', - ); - - $examples['Script with a commented a script tag opener inside should at the next tag closer (dash dash escaped state)'] = array( - '
-->', - ); - - $examples['Script closer with another script tag in closer attributes'] = array( - '
', - ); - - $examples['Script closer with attributes'] = array( - '
', - ); - - $examples['Script opener with title closer inside'] = array( - '
', - ); - - $examples['Complex script with many parsing states'] = array( - '-->
-->', - ); - return $examples; - } - - /** - * @ticket 56299 - * - * @covers next_tag - * - * @dataProvider data_rcdata_state - */ - public function test_next_tag_ignores_the_contents_of_a_rcdata_tag( $rcdata_then_div, $rcdata_tag ) { - $p = new WP_HTML_Tag_Processor( $rcdata_then_div ); - $p->next_tag(); - $this->assertSame( strtoupper( $rcdata_tag ), $p->get_tag(), "The first found tag was not '$rcdata_tag'" ); - $p->next_tag(); - $this->assertSame( 'DIV', $p->get_tag(), "The second found tag was not 'div'" ); - } - - /** - * Data provider for test_ignores_contents_of_a_rcdata_tag(). - * - * @return array { - * @type array { - * @type string $rcdata_then_div The HTML snippet containing RCDATA and div tags. - * @type string $rcdata_tag The RCDATA tag. - * } - * } - */ - public function data_rcdata_state() { - $examples = array(); - $examples['Simple textarea'] = array( - '
', - 'textarea', - ); - - $examples['Simple title'] = array( - '<span class="d-none d-md-inline">Back to notifications</title</span>
', - 'title', - ); - - $examples['Comment opener inside a textarea tag should be ignored'] = array( - '
-->', - 'textarea', - ); - - $examples['Textarea closer with another textarea tag in closer attributes'] = array( - '
', - 'textarea', - ); - - $examples['Textarea closer with attributes'] = array( - '
', - 'textarea', - ); - - $examples['Textarea opener with title closer inside'] = array( - '
', - 'textarea', - ); - return $examples; - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers set_attribute - * @covers __toString - */ - public function test_can_query_and_update_wrongly_nested_tags() { - $p = new WP_HTML_Tag_Processor( - '123

456789

' - ); - $p->next_tag( 'span' ); - $p->set_attribute( 'class', 'span-class' ); - $p->next_tag( 'p' ); - $p->set_attribute( 'class', 'p-class' ); - $this->assertSame( - '123

456789

', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers remove_attribute - * @covers __toString - */ - public function test_removing_attributes_works_even_in_malformed_html() { - $p = new WP_HTML_Tag_Processor( self::HTML_MALFORMED ); - $p->next_tag( 'span' ); - $p->remove_attribute( 'Notifications<' ); - $this->assertSame( - '
Back to notifications
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers next_Tag - * @covers set_attribute - * @covers __toString - */ - public function test_updating_attributes_works_even_in_malformed_html_1() { - $p = new WP_HTML_Tag_Processor( self::HTML_MALFORMED ); - $p->next_tag( 'span' ); - $p->set_attribute( 'id', 'first' ); - $p->next_tag( 'span' ); - $p->set_attribute( 'id', 'second' ); - $this->assertSame( - '
Back to notifications
', - (string) $p - ); - } - - /** - * @ticket 56299 - * - * @covers next_tag - * @covers set_attribute - * @covers add_class - * @covers __toString - * - * @dataProvider data_malformed_tag - */ - public function test_updating_attributes_works_even_in_malformed_html_2( $html_input, $html_expected ) { - $p = new WP_HTML_Tag_Processor( $html_input ); - $p->next_tag(); - $p->set_attribute( 'foo', 'bar' ); - $p->add_class( 'firstTag' ); - $p->next_tag(); - $p->add_class( 'secondTag' ); - $this->assertSame( - $html_expected, - (string) $p - ); - } - - /** - * Data provider for test_updates_when_malformed_tag(). - * - * @return array { - * @type array { - * @type string $html_input The input HTML snippet. - * @type string $html_expected The expected HTML snippet after processing. - * } - * } - */ - public function data_malformed_tag() { - $null_byte = chr( 0 ); - $examples = array(); - $examples['Invalid entity inside attribute value'] = array( - 'test', - 'test', - ); - - $examples['HTML tag opening inside attribute value'] = array( - '
This <is> a <strong is="true">thing.
test', - '
This <is> a <strong is="true">thing.
test', - ); - - $examples['HTML tag brackets in attribute values and data markup'] = array( - '
This <is> a <strong is="true">thing.
test', - '
This <is> a <strong is="true">thing.
test', - ); - - $examples['Single and double quotes in attribute value'] = array( - '

test', - '

test', - ); - - $examples['Unquoted attribute values'] = array( - '


test', - '
test', - ); - - $examples['Double-quotes escaped in double-quote attribute value'] = array( - '
test', - '
test', - ); - - $examples['Unquoted attribute value'] = array( - '
test', - '
test', - ); - - $examples['Unquoted attribute value with tag-like value'] = array( - '
>test', - '
>test', - ); - - $examples['Unquoted attribute value with tag-like value followed by tag-like data'] = array( - '
>test', - '
>test', - ); - - $examples['1'] = array( - '
test', - '
test', - ); - - $examples['2'] = array( - '
test', - '
test', - ); - - $examples['4'] = array( - '
test', - '
test', - ); - - $examples['5'] = array( - '
code>test', - '
code>test', - ); - - $examples['6'] = array( - '
test', - '
test', - ); - - $examples['7'] = array( - '
test', - '
test', - ); - - $examples['8'] = array( - '
id="test">test', - '
id="test">test', - ); - - $examples['9'] = array( - '
test', - '
test', - ); - - $examples['10'] = array( - 'test', - 'test', - ); - - $examples['11'] = array( - 'The applicative operator <* works well in Haskell; is what?test', - 'The applicative operator <* works well in Haskell; is what?test', - ); - - $examples['12'] = array( - '<3 is a heart but is a tag.test', - '<3 is a heart but is a tag.test', - ); - - $examples['13'] = array( - 'test', - 'test', - ); - - $examples['14'] = array( - 'test', - 'test', - ); - - $examples['15'] = array( - ' a HTML Tag]]>test', - ' a HTML Tag]]>test', - ); - - $examples['16'] = array( - '
test', - '
test', - ); - - $examples['17'] = array( - '
test', - '
test', - ); - - $examples['18'] = array( - '
test', - '
test', - ); - - $examples['19'] = array( - '
test', - '
test', - ); - - $examples['20'] = array( - '
test', - '
test', - ); - - $examples['21'] = array( - '
test', - '
test', - ); - - $examples['22'] = array( - '
test', - '
test', - ); - - $examples['23'] = array( - '
test', - '
test', - ); - - $examples['24'] = array( - '
test', - '
test', - ); - - $examples['25'] = array( - '
test', - '
test', - ); - - $examples['Multiple unclosed tags treated as a single tag'] = array( - '
-test', - '
-test', - ); - - $examples['27'] = array( - '
test', - '
test', - ); - - $examples['28'] = array( - '
test', - '
test', - ); - - return $examples; - } -} diff --git a/phpunit/html/wp-html-tag-processor-standalone-test.php b/phpunit/html/wp-html-tag-processor-test.php similarity index 98% rename from phpunit/html/wp-html-tag-processor-standalone-test.php rename to phpunit/html/wp-html-tag-processor-test.php index 8079db28f52be7..e66b1d50758d0b 100644 --- a/phpunit/html/wp-html-tag-processor-standalone-test.php +++ b/phpunit/html/wp-html-tag-processor-test.php @@ -1,30 +1,11 @@ Text
'; const HTML_WITH_CLASSES = '
Text
'; const HTML_MALFORMED = '
Back to notifications
'; @@ -951,7 +932,7 @@ public function data_script_state() { public function test_next_tag_ignores_the_contents_of_a_rcdata_tag( $rcdata_then_div, $rcdata_tag ) { $p = new WP_HTML_Tag_Processor( $rcdata_then_div ); $p->next_tag(); - $this->assertSame( $rcdata_tag, $p->get_tag(), "The first found tag was not '$rcdata_tag'" ); + $this->assertSame( strtoupper( $rcdata_tag ), $p->get_tag(), "The first found tag was not '$rcdata_tag'" ); $p->next_tag(); $this->assertSame( 'DIV', $p->get_tag(), "The second found tag was not 'div'" ); } diff --git a/phpunit/html/wp-html-tag-processor-wp-test.php b/phpunit/html/wp-html-tag-processor-wp-test.php deleted file mode 100644 index 41008800d0b751..00000000000000 --- a/phpunit/html/wp-html-tag-processor-wp-test.php +++ /dev/null @@ -1,91 +0,0 @@ - - * $p = new WP_HTML_Tag_Processor( '
' ); - * $p->next_tag(); - * $p->set_attribute('class', '" onclick="alert'); - * echo $p; - * //
- * - * - * To prevent it, `set_attribute` calls `esc_attr()` on its given values. - * - * - *
- *
- * - * @ticket 56299 - * - * @dataProvider data_set_attribute_escapable_values - * @covers set_attribute - */ - public function test_set_attribute_prevents_xss( $value_to_set, $expected_result ) { - $p = new WP_HTML_Tag_Processor( '
' ); - $p->next_tag(); - $p->set_attribute( 'test', $value_to_set ); - - /* - * Testing the escaping is hard using tools that properly parse - * HTML because they might interpret the escaped values. It's hard - * with tools that don't understand HTML because they might get - * confused by improperly-escaped values. - * - * For this test, since we control the input HTML we're going to - * do what looks like the opposite of what we want to be doing with - * this library but are only doing so because we have full control - * over the content and because we want to look at the raw values. - */ - $match = null; - preg_match( '~^
$~', $p->get_updated_html(), $match ); - list( , $actual_value ) = $match; - - $this->assertEquals( $actual_value, '"' . $expected_result . '"' ); - } - - /** - * Data provider with HTML attribute values that might need escaping. - */ - public function data_set_attribute_escapable_values() { - return array( - array( '"', '"' ), - array( '"', '"' ), - array( '&', '&' ), - array( '&', '&' ), - array( '€', '€' ), - array( "'", ''' ), - array( '<>', '<>' ), - array( '"";', '&quot";' ), - array( - '" onclick="alert(\'1\');">', - '" onclick="alert('1');"><span onclick=""></span><script>alert("1")</script>', - ), - ); - } - -}