assertEquals($originalSubject, $convertedSubject); $this->assertTrue(mb_check_encoding($convertedSubject, 'UTF-8')); // Verify emoji is preserved $this->assertStringContainsString('🚀', $convertedSubject); $this->assertStringContainsString('é', $convertedSubject); // Verify accented characters are preserved $this->assertStringContainsString('impayée', $convertedSubject); // Verify the string length is correct (emojis are multi-byte) $this->assertEquals(mb_strlen($originalSubject, 'UTF-8'), mb_strlen($convertedSubject, 'UTF-8')); } /** * Test various email subject scenarios with emojis */ public function testEmojiEmailSubjects() { $testCases = [ // Single emoji "Invoice Ready 📧" => "Invoice Ready 📧", // Multiple emojis "Payment Received ✅ 🎉" => "Payment Received ✅ 🎉", // Emoji at start "🚨 Urgent: Payment Overdue" => "🚨 Urgent: Payment Overdue", // Emoji at end "Welcome to our service! 🎯" => "Welcome to our service! 🎯", // Complex emojis (family, skin tones, etc.) "Team meeting 👨‍💻👩‍💻" => "Team meeting 👨‍💻👩‍💻", // Mixed flags and symbols "Conference in Paris 🇫🇷 ✈️" => "Conference in Paris 🇫🇷 ✈️", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input => $expected) { $result = Encode::convert($input); $this->assertEquals($expected, $result, "Failed for emoji test: {$input}"); $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Not valid UTF-8: {$input}"); } } /** * Test accented characters common in email subjects */ public function testAccentedCharacters() { $testCases = [ // French "Café résumé naïve façade" => "Café résumé naïve façade", // Spanish "Niño piñata mañana" => "Niño piñata mañana", // German "Größe Weiß Mädchen" => "Größe Weiß Mädchen", // Portuguese "Coração São Paulo" => "Coração São Paulo", // Mixed languages "Café & Niño résumé" => "Café & Niño résumé", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input => $expected) { $result = Encode::convert($input); $this->assertEquals($expected, $result, "Failed for accent test: {$input}"); $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Not valid UTF-8: {$input}"); } } /** * Test special symbols commonly used in email subjects */ public function testSpecialSymbols() { $testCases = [ // Currency symbols "Invoice €50.00 £25.99 ¥1000" => "Invoice €50.00 £25.99 ¥1000", // Smart quotes and dashes "Company's \"quoted\" text—dash…ellipsis" => "Company's \"quoted\" text—dash…ellipsis", // Copyright and trademark "Product™ Service© Brand®" => "Product™ Service© Brand®", // Mathematical symbols "Discount ≥ 20% ± 5%" => "Discount ≥ 20% ± 5%", // Arrows and symbols "Process → Complete ✓" => "Process → Complete ✓", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input => $expected) { $result = Encode::convert($input); $this->assertEquals($expected, $result, "Failed for symbol test: {$input}"); $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Not valid UTF-8: {$input}"); } } /** * Test email subjects with mixed content (the most realistic scenario) */ public function testMixedContentEmailSubjects() { $testCases = [ // User's exact example "Rappel facture impayée (\$invoice) 🚀" => "Rappel facture impayée (\$invoice) 🚀", // Invoice with currency and emoji "Facture #123 - €150.00 💰" => "Facture #123 - €150.00 💰", // Reminder with accents and emoji "Relance: paiement en retard 📅 ⚠️" => "Relance: paiement en retard 📅 ⚠️", // Welcome message "Bienvenue chez Café ☕ 🥐" => "Bienvenue chez Café ☕ 🥐", // Complex business scenario "Réunion équipe → 15h30 📊 🎯" => "Réunion équipe → 15h30 📊 🎯", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input => $expected) { $result = Encode::convert($input); $this->assertEquals($expected, $result, "Failed for mixed content test: {$input}"); $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Not valid UTF-8: {$input}"); // Verify character count is preserved (important for emojis) $this->assertEquals( mb_strlen($expected, 'UTF-8'), mb_strlen($result, 'UTF-8'), "Character count mismatch for: {$input}" ); } } /** * Test corrupted Windows-1252 content that needs conversion */ public function testCorruptedEncodingConversion() { // Simulate content that was incorrectly encoded as Windows-1252 $windows1252Input = mb_convert_encoding("Café résumé", 'WINDOWS-1252', 'UTF-8'); $result = Encode::convert($windows1252Input); $this->assertEquals("Café résumé", $result); $this->assertTrue(mb_check_encoding($result, 'UTF-8')); } /** * Test Gmail-specific email subject requirements */ public function testGmailCompatibility() { $testCases = [ // Long subject with emojis (Gmail truncates at ~70 chars in preview) "This is a long email subject with emojis 🚀 that might get truncated by Gmail 📧", // Subject with only emojis "🚀📧🎉✅⚠️💰", // Subject with special characters Gmail handles "Re: Fw: [URGENT] Company's \"Project\" Status—Update ✓", // International content "国际业务 🌍 Négociation €500K 💼", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input) { $result = Encode::convert($input); // Should be valid UTF-8 (Gmail requirement) $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Gmail compatibility failed for: {$input}"); // Should not contain replacement characters $this->assertStringNotContainsString("\xEF\xBF\xBD", $result, "Contains replacement characters: {$input}"); $this->assertStringNotContainsString('�', $result, "Contains double-encoded replacement: {$input}"); // Should preserve original content for valid UTF-8 $this->assertEquals($input, $result, "Content changed unnecessarily: {$input}"); } } /** * Test edge cases that might break email clients */ public function testEmailClientEdgeCases() { $testCases = [ // Empty string "" => "", // Only spaces " " => " ", // Only special characters "€£¥" => "€£¥", // Only emojis "🚀🎉📧" => "🚀🎉📧", // Mixed spaces and emojis " 🚀 📧 🎉 " => " 🚀 📧 🎉 ", // Newlines and tabs (should be preserved) "Line 1\nLine 2\tTabbed" => "Line 1\nLine 2\tTabbed", "Nouvelle facture de Réact" => "Nouvelle facture de Réact" ]; foreach ($testCases as $input => $expected) { $result = Encode::convert($input); $this->assertEquals($expected, $result, "Edge case failed: " . var_export($input, true)); $this->assertTrue(mb_check_encoding($result, 'UTF-8'), "Not valid UTF-8: " . var_export($input, true)); } } /** * Test performance with typical email subject lengths */ public function testPerformanceWithTypicalSubjects2() { $baseSubject = "Nouvelle facture de Réact"; // Test with different subject lengths $subjects = [ $baseSubject, // ~40 chars str_repeat($baseSubject . " ", 2), // ~80 chars str_repeat($baseSubject . " ", 5), // ~200 chars ]; foreach ($subjects as $subject) { $startTime = microtime(true); $result = Encode::convert($subject); $endTime = microtime(true); $executionTime = ($endTime - $startTime) * 1000; // Convert to milliseconds // Should complete quickly (under 10ms for email subjects) $this->assertLessThan(10, $executionTime, "Too slow for subject: " . strlen($subject) . " chars"); $this->assertTrue(mb_check_encoding($result, 'UTF-8')); } } /** * Test performance with typical email subject lengths */ public function testPerformanceWithTypicalSubjects() { $baseSubject = "Rappel facture impayée (\$invoice) 🚀"; // Test with different subject lengths $subjects = [ $baseSubject, // ~40 chars str_repeat($baseSubject . " ", 2), // ~80 chars str_repeat($baseSubject . " ", 5), // ~200 chars ]; foreach ($subjects as $subject) { $startTime = microtime(true); $result = Encode::convert($subject); $endTime = microtime(true); $executionTime = ($endTime - $startTime) * 1000; // Convert to milliseconds // Should complete quickly (under 10ms for email subjects) $this->assertLessThan(10, $executionTime, "Too slow for subject: " . strlen($subject) . " chars"); $this->assertTrue(mb_check_encoding($result, 'UTF-8')); } } /** * Test that the method is safe to call multiple times */ public function testIdempotency() { $original = "Rappel facture impayée (\$invoice) 🚀"; $first = Encode::convert($original); $second = Encode::convert($first); $third = Encode::convert($second); // Should be identical after multiple conversions $this->assertEquals($original, $first); $this->assertEquals($first, $second); $this->assertEquals($second, $third); $original = "Nouvelle facture de Réact"; $first = Encode::convert($original); $second = Encode::convert($first); $third = Encode::convert($second); // Should be identical after multiple conversions $this->assertEquals($original, $first); $this->assertEquals($first, $second); $this->assertEquals($second, $third); } }