@@ -1406,4 +1406,123 @@ public function camel_snake_case_provider() {
14061406 public function test_camel_to_snake_case ( $ original , $ expected ) {
14071407 $ this ->assertSame ( $ expected , \Activitypub \camel_to_snake_case ( $ original ) );
14081408 }
1409+
1410+ /**
1411+ * Data provider for esc_hashtag tests.
1412+ *
1413+ * @return array Test cases with input and expected output.
1414+ */
1415+ public function esc_hashtag_provider () {
1416+ return array (
1417+ 'simple_word ' => array ( 'test ' , '#test ' ),
1418+ 'word_with_spaces ' => array ( 'test tag ' , '#testTag ' ),
1419+ 'multiple_spaces ' => array ( 'test multiple spaces ' , '#testMultipleSpaces ' ),
1420+ 'with_special_chars ' => array ( 'test@tag! ' , '#testTag ' ),
1421+ 'with_underscores ' => array ( 'test_tag ' , '#testTag ' ),
1422+ 'with_leading_hashtag ' => array ( '#test ' , '#Test ' ),
1423+ 'with_multiple_hashtags ' => array ( '##test ' , '#Test ' ),
1424+ 'with_leading_hyphen ' => array ( '-test ' , '#Test ' ),
1425+ 'with_trailing_hyphen ' => array ( 'test- ' , '#test ' ),
1426+ 'mixed_case ' => array ( 'TestTag ' , '#TestTag ' ),
1427+ 'with_numbers ' => array ( 'test123 ' , '#test123 ' ),
1428+ 'with_unicode ' => array ( 'tëst ' , '#tëst ' ),
1429+ 'with_unicode_spaces ' => array ( 'tëst tàg ' , '#tëstTàg ' ),
1430+ 'german_umlauts ' => array ( 'über straße ' , '#überStraße ' ),
1431+ 'japanese_characters ' => array ( 'テスト ' , '#テスト ' ),
1432+ 'arabic_characters ' => array ( 'اختبار ' , '#اختبار ' ),
1433+ 'cyrillic_characters ' => array ( 'тест ' , '#тест ' ),
1434+ 'empty_string ' => array ( '' , '# ' ),
1435+ 'only_spaces ' => array ( ' ' , '# ' ),
1436+ 'only_special_chars ' => array ( '@!#$% ' , '# ' ),
1437+ 'hyphenated_words ' => array ( 'foo-bar-baz ' , '#fooBarBaz ' ),
1438+ 'quotes ' => array ( "test'tag " , '#testTag ' ),
1439+ 'double_quotes ' => array ( 'test"tag ' , '#testTag ' ),
1440+ 'ampersand ' => array ( 'test&tag ' , '#testTag ' ),
1441+ 'html_entities ' => array ( 'test&tag ' , '#testTag ' ),
1442+ 'leading_trailing_spaces ' => array ( ' test ' , '#Test ' ),
1443+ 'multiple_hyphens ' => array ( 'test--tag ' , '#testTag ' ),
1444+ 'camelCase_preservation ' => array ( 'testTag ' , '#testTag ' ),
1445+ 'with_dots ' => array ( 'test.tag ' , '#testTag ' ),
1446+ 'with_commas ' => array ( 'test,tag ' , '#testTag ' ),
1447+ 'with_semicolons ' => array ( 'test;tag ' , '#testTag ' ),
1448+ 'with_slashes ' => array ( 'test/tag ' , '#testTag ' ),
1449+ 'with_backslashes ' => array ( 'test \\tag ' , '#testTag ' ),
1450+ 'with_parentheses ' => array ( 'test(tag) ' , '#testTag ' ),
1451+ 'with_brackets ' => array ( 'test[tag] ' , '#testTag ' ),
1452+ 'with_braces ' => array ( 'test{tag} ' , '#testTag ' ),
1453+ 'emoji_mixed ' => array ( 'test 😀 tag ' , '#testTag ' ),
1454+ 'chinese_characters ' => array ( '测试 标签 ' , '#测试标签 ' ),
1455+ 'korean_characters ' => array ( '테스트 태그 ' , '#테스트태그 ' ),
1456+ 'greek_characters ' => array ( 'δοκιμή ' , '#δοκιμή ' ),
1457+ 'hebrew_characters ' => array ( 'בדיקה ' , '#בדיקה ' ),
1458+ 'thai_characters ' => array ( 'ทดสอบ ' , '#ทดสอบ ' ),
1459+ );
1460+ }
1461+
1462+ /**
1463+ * Test esc_hashtag function.
1464+ *
1465+ * @dataProvider esc_hashtag_provider
1466+ * @covers \Activitypub\esc_hashtag
1467+ *
1468+ * @param string $input The input string.
1469+ * @param string $expected The expected hashtag output.
1470+ */
1471+ public function test_esc_hashtag ( $ input , $ expected ) {
1472+ $ result = \Activitypub \esc_hashtag ( $ input );
1473+ $ this ->assertSame ( $ expected , $ result );
1474+ }
1475+
1476+ /**
1477+ * Test esc_hashtag filter hook.
1478+ *
1479+ * @covers \Activitypub\esc_hashtag
1480+ */
1481+ public function test_esc_hashtag_filter () {
1482+ $ filter_callback = function ( $ hashtag , $ input ) {
1483+ if ( 'custom ' === $ input ) {
1484+ return '#CustomTag ' ;
1485+ }
1486+ return $ hashtag ;
1487+ };
1488+
1489+ \add_filter ( 'activitypub_esc_hashtag ' , $ filter_callback , 10 , 2 );
1490+
1491+ $ result = \Activitypub \esc_hashtag ( 'custom ' );
1492+ $ this ->assertSame ( '#CustomTag ' , $ result );
1493+
1494+ \remove_filter ( 'activitypub_esc_hashtag ' , $ filter_callback , 10 );
1495+ }
1496+
1497+ /**
1498+ * Test esc_hashtag with HTML special characters.
1499+ *
1500+ * @covers \Activitypub\esc_hashtag
1501+ */
1502+ public function test_esc_hashtag_html_escaping () {
1503+ $ result = \Activitypub \esc_hashtag ( '<script>alert("xss")</script> ' );
1504+ $ this ->assertStringNotContainsString ( '<script> ' , $ result );
1505+ $ this ->assertStringNotContainsString ( 'alert ' , $ result );
1506+ // The result should be HTML-escaped.
1507+ $ this ->assertStringStartsWith ( '# ' , $ result );
1508+ }
1509+
1510+ /**
1511+ * Test esc_hashtag with quoted strings.
1512+ *
1513+ * @covers \Activitypub\esc_hashtag
1514+ */
1515+ public function test_esc_hashtag_with_quotes () {
1516+ // Test single quotes.
1517+ $ result = \Activitypub \esc_hashtag ( "test's tag " );
1518+ $ this ->assertSame ( '#testSTag ' , $ result );
1519+
1520+ // Test double quotes.
1521+ $ result = \Activitypub \esc_hashtag ( 'test"s tag ' );
1522+ $ this ->assertSame ( '#testSTag ' , $ result );
1523+
1524+ // Test HTML entities for quotes.
1525+ $ result = \Activitypub \esc_hashtag ( 'test's tag ' );
1526+ $ this ->assertSame ( '#testSTag ' , $ result );
1527+ }
14091528}
0 commit comments