From 56298dc291619717ab02af48c33c3a68b8f29d94 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 20 Oct 2025 09:32:59 +0100 Subject: [PATCH 1/2] Enhance email normalization providers by adding custom behavior for hyphen-based subaddress removal and updating documentation. Refactor tests to reflect changes in handling plus addressing and dot preservation across various providers, ensuring comprehensive coverage for Fastmail, Generic, Gmail, Icloud, Outlook, Protonmail, and Yahoo. --- src/Emails/Canonicals/Provider.php | 12 + src/Emails/Canonicals/Providers/Fastmail.php | 16 +- src/Emails/Canonicals/Providers/Generic.php | 18 +- src/Emails/Canonicals/Providers/Gmail.php | 16 +- src/Emails/Canonicals/Providers/Icloud.php | 22 +- src/Emails/Canonicals/Providers/Outlook.php | 28 +- .../Canonicals/Providers/Protonmail.php | 16 +- src/Emails/Canonicals/Providers/Yahoo.php | 27 +- src/Emails/Canonicals/Providers/Yandex.php | 50 +++ tests/Canonicals/Providers/FastmailTest.php | 37 +- tests/Canonicals/Providers/GenericTest.php | 42 +-- tests/Canonicals/Providers/IcloudTest.php | 35 +- tests/Canonicals/Providers/OutlookTest.php | 70 ++-- tests/Canonicals/Providers/ProtonmailTest.php | 37 +- tests/Canonicals/Providers/YahooTest.php | 79 ++-- tests/Canonicals/Providers/YandexTest.php | 71 ++++ tests/EmailTest.php | 346 ++++++++---------- 17 files changed, 506 insertions(+), 416 deletions(-) create mode 100644 src/Emails/Canonicals/Providers/Yandex.php create mode 100644 tests/Canonicals/Providers/YandexTest.php diff --git a/src/Emails/Canonicals/Provider.php b/src/Emails/Canonicals/Provider.php index e220ac6..340d9c1 100644 --- a/src/Emails/Canonicals/Provider.php +++ b/src/Emails/Canonicals/Provider.php @@ -48,6 +48,7 @@ protected function removePlusAddressing(string $local): string /** * Remove all dots from local part + * Can be overridden by providers for custom behavior */ protected function removeDots(string $local): string { @@ -62,6 +63,17 @@ protected function removeHyphens(string $local): string return str_replace('-', '', $local); } + /** + * Remove hyphen-based subaddress (Yahoo style) + * Removes everything after the last hyphen + */ + protected function removeHyphenSubaddress(string $local): string + { + $components = explode('-', $local); + + return count($components) > 1 ? implode('-', array_slice($components, 0, -1)) : $components[0]; + } + /** * Convert local part to lowercase */ diff --git a/src/Emails/Canonicals/Providers/Fastmail.php b/src/Emails/Canonicals/Providers/Fastmail.php index b77ac76..9781a0d 100644 --- a/src/Emails/Canonicals/Providers/Fastmail.php +++ b/src/Emails/Canonicals/Providers/Fastmail.php @@ -8,8 +8,7 @@ * Fastmail * * Handles Fastmail email normalization - * - TODO: Plus addressing and dots removal commented out until manual confirmation - * - Preserves dots and hyphens in local part + * - Preserves all characters in local part (no subaddress or dot removal) * - Normalizes to fastmail.com domain */ class Fastmail extends Provider @@ -28,17 +27,8 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of Fastmail's plus addressing and dots support - // Check if there's plus addressing - // $hasPlus = strpos($normalizedLocal, '+') !== false && strpos($normalizedLocal, '+') > 0; - - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - - // Remove dots only if there was plus addressing (Fastmail treats dots as aliases only with plus) - // if ($hasPlus) { - // $normalizedLocal = $this->removeDots($normalizedLocal); - // } + // Fastmail doesn't remove subaddresses or dots + // Just normalize case and domain return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Generic.php b/src/Emails/Canonicals/Providers/Generic.php index 58cbb0a..58bf943 100644 --- a/src/Emails/Canonicals/Providers/Generic.php +++ b/src/Emails/Canonicals/Providers/Generic.php @@ -8,8 +8,8 @@ * Generic * * Handles generic email normalization for unsupported providers - * - TODO: Plus addressing, dots, and hyphens removal commented out until manual confirmation - * - Preserves all other characters + * - Preserves all characters in local part (no subaddress or dot removal) + * - Only converts to lowercase */ class Generic extends Provider { @@ -24,18 +24,8 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of generic providers' plus addressing, dots, and hyphens support - // Check if there's plus addressing - // $hasPlus = strpos($normalizedLocal, '+') !== false && strpos($normalizedLocal, '+') > 0; - - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - - // Remove dots and hyphens only if there was plus addressing (generic providers treat these as aliases only with plus) - // if ($hasPlus) { - // $normalizedLocal = $this->removeDots($normalizedLocal); - // $normalizedLocal = $this->removeHyphens($normalizedLocal); - // } + // Generic providers don't remove subaddresses or dots + // Just normalize case return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Gmail.php b/src/Emails/Canonicals/Providers/Gmail.php index bbd6d36..7e38e21 100644 --- a/src/Emails/Canonicals/Providers/Gmail.php +++ b/src/Emails/Canonicals/Providers/Gmail.php @@ -7,10 +7,11 @@ /** * Gmail * - * Handles Gmail and Googlemail email normalization + * Handles Gmail and Googlemail email normalization based on validator.js rules * - Removes all dots from local part - * - Removes plus addressing + * - Removes plus addressing (subaddress) * - Normalizes to gmail.com domain + * - Converts googlemail.com to gmail.com */ class Gmail extends Provider { @@ -28,11 +29,16 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // Remove all dots from local part + // Remove plus addressing (subaddress) - everything after + + $normalizedLocal = $this->removePlusAddressing($normalizedLocal); + + // Remove dots from local part $normalizedLocal = $this->removeDots($normalizedLocal); - // Remove plus addressing (everything after +) - $normalizedLocal = $this->removePlusAddressing($normalizedLocal); + // Ensure local part is not empty after normalization + if (empty($normalizedLocal)) { + throw new \InvalidArgumentException('Email local part cannot be empty after normalization'); + } return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Icloud.php b/src/Emails/Canonicals/Providers/Icloud.php index d2d9e1e..233a284 100644 --- a/src/Emails/Canonicals/Providers/Icloud.php +++ b/src/Emails/Canonicals/Providers/Icloud.php @@ -7,9 +7,9 @@ /** * iCloud * - * Handles Apple iCloud email normalization - * - TODO: Plus addressing and dots removal commented out until manual confirmation - * - Preserves dots and hyphens in local part + * Handles Apple iCloud email normalization based on validator.js rules + * - Removes plus addressing (subaddress) + * - Preserves dots in local part * - Normalizes to icloud.com domain */ class Icloud extends Provider @@ -28,17 +28,13 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of iCloud's plus addressing and dots support - // Check if there's plus addressing - // $hasPlus = strpos($normalizedLocal, '+') !== false && strpos($normalizedLocal, '+') > 0; + // Remove plus addressing (subaddress) - everything after + + $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - - // Remove dots only if there was plus addressing (iCloud treats dots as aliases only with plus) - // if ($hasPlus) { - // $normalizedLocal = $this->removeDots($normalizedLocal); - // } + // Ensure local part is not empty after normalization + if (empty($normalizedLocal)) { + throw new \InvalidArgumentException('Email local part cannot be empty after normalization'); + } return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Outlook.php b/src/Emails/Canonicals/Providers/Outlook.php index fe57903..a0906e8 100644 --- a/src/Emails/Canonicals/Providers/Outlook.php +++ b/src/Emails/Canonicals/Providers/Outlook.php @@ -7,16 +7,26 @@ /** * Outlook * - * Handles Outlook, Hotmail, and Live email normalization - * - TODO: Plus addressing removal commented out until manual confirmation + * Handles Outlook, Hotmail, and Live email normalization based on validator.js rules + * - Removes plus addressing (subaddress) * - Preserves dots in local part * - Normalizes to outlook.com domain */ class Outlook extends Provider { private const SUPPORTED_DOMAINS = [ - 'outlook.com', 'hotmail.com', 'live.com', - 'outlook.co.uk', 'hotmail.co.uk', 'live.co.uk', + 'outlook.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.co.uk', + 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', + 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', + 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', + 'outlook.sa', 'outlook.sg', 'outlook.sk', + 'hotmail.com', 'hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', + 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', + 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', + 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', + 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', + 'live.com', 'live.be', 'live.co.uk', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', + 'msn.com', 'passport.com', ]; private const CANONICAL_DOMAIN = 'outlook.com'; @@ -31,9 +41,13 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); + // Remove plus addressing (subaddress) - everything after + + $normalizedLocal = $this->removePlusAddressing($normalizedLocal); + + // Ensure local part is not empty after normalization + if (empty($normalizedLocal)) { + throw new \InvalidArgumentException('Email local part cannot be empty after normalization'); + } return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Protonmail.php b/src/Emails/Canonicals/Providers/Protonmail.php index 27bdaa6..b9707ae 100644 --- a/src/Emails/Canonicals/Providers/Protonmail.php +++ b/src/Emails/Canonicals/Providers/Protonmail.php @@ -8,8 +8,7 @@ * ProtonMail * * Handles ProtonMail email normalization - * - TODO: Plus addressing and dots removal commented out until manual confirmation - * - Preserves dots and hyphens in local part + * - Preserves all characters in local part (no subaddress or dot removal) * - Normalizes to protonmail.com domain */ class Protonmail extends Provider @@ -28,17 +27,8 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of ProtonMail's plus addressing and dots support - // Check if there's plus addressing - // $hasPlus = strpos($normalizedLocal, '+') !== false && strpos($normalizedLocal, '+') > 0; - - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - - // Remove dots only if there was plus addressing (ProtonMail treats dots as aliases only with plus) - // if ($hasPlus) { - // $normalizedLocal = $this->removeDots($normalizedLocal); - // } + // ProtonMail doesn't remove subaddresses or dots + // Just normalize case and domain return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Yahoo.php b/src/Emails/Canonicals/Providers/Yahoo.php index 3075f92..f156a22 100644 --- a/src/Emails/Canonicals/Providers/Yahoo.php +++ b/src/Emails/Canonicals/Providers/Yahoo.php @@ -7,15 +7,15 @@ /** * Yahoo * - * Handles Yahoo email normalization - * - TODO: Plus addressing, dots, and hyphens removal commented out until manual confirmation - * - Preserves dots and hyphens in local part + * Handles Yahoo email normalization based on validator.js rules + * - Removes hyphen-based subaddress (everything after last -) + * - Preserves dots in local part * - Normalizes to yahoo.com domain */ class Yahoo extends Provider { private const SUPPORTED_DOMAINS = [ - 'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', + 'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com', 'rocketmail.com', ]; @@ -31,20 +31,13 @@ public function getCanonical(string $local, string $domain): array // Convert to lowercase $normalizedLocal = $this->toLowerCase($local); - // TODO: Commented out until manual confirmation of Yahoo's plus addressing, dots, and hyphens support - // Check if there's plus addressing - // $hasPlus = strpos($normalizedLocal, '+') !== false && strpos($normalizedLocal, '+') > 0; + // Remove hyphen-based subaddress (everything after last -) + $normalizedLocal = $this->removeHyphenSubaddress($normalizedLocal); - // Remove plus addressing (everything after +) - // $normalizedLocal = $this->removePlusAddressing($normalizedLocal); - - // Remove dots only if there was plus addressing (Yahoo treats dots as aliases only with plus) - // if ($hasPlus) { - // $normalizedLocal = $this->removeDots($normalizedLocal); - // } - - // Remove hyphens (Yahoo treats hyphens as aliases) - // $normalizedLocal = $this->removeHyphens($normalizedLocal); + // Ensure local part is not empty after normalization + if (empty($normalizedLocal)) { + throw new \InvalidArgumentException('Email local part cannot be empty after normalization'); + } return [ 'local' => $normalizedLocal, diff --git a/src/Emails/Canonicals/Providers/Yandex.php b/src/Emails/Canonicals/Providers/Yandex.php new file mode 100644 index 0000000..f3275f9 --- /dev/null +++ b/src/Emails/Canonicals/Providers/Yandex.php @@ -0,0 +1,50 @@ +toLowerCase($local); + + // Yandex doesn't remove subaddresses or dots + // Just normalize case and domain + + return [ + 'local' => $normalizedLocal, + 'domain' => self::CANONICAL_DOMAIN, + ]; + } + + public function getCanonicalDomain(): string + { + return self::CANONICAL_DOMAIN; + } + + public function getSupportedDomains(): array + { + return self::SUPPORTED_DOMAINS; + } +} diff --git a/tests/Canonicals/Providers/FastmailTest.php b/tests/Canonicals/Providers/FastmailTest.php index 5f98348..e98f7bd 100644 --- a/tests/Canonicals/Providers/FastmailTest.php +++ b/tests/Canonicals/Providers/FastmailTest.php @@ -26,28 +26,29 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Fastmail's plus addressing and dots support - // ['user.name+tag', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+spam', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+newsletter', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+work', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+personal', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+test123', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+anything', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+verylongtag', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+tag.with.dots', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+tag-with-hyphens', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+tag_with_underscores', 'fastmail.com', 'username', 'fastmail.com'], - // ['user.name+tag123', 'fastmail.com', 'username', 'fastmail.com'], - // // Other Fastmail domain - // ['user.name+tag', 'fastmail.fm', 'username', 'fastmail.com'], - // Dots are preserved for Fastmail + // Fastmail preserves all characters (no subaddress or dot removal) ['user.name', 'fastmail.com', 'user.name', 'fastmail.com'], + ['user.name+tag', 'fastmail.com', 'user.name+tag', 'fastmail.com'], + ['user.name+spam', 'fastmail.com', 'user.name+spam', 'fastmail.com'], + ['user.name+newsletter', 'fastmail.com', 'user.name+newsletter', 'fastmail.com'], + ['user.name+work', 'fastmail.com', 'user.name+work', 'fastmail.com'], + ['user.name+personal', 'fastmail.com', 'user.name+personal', 'fastmail.com'], + ['user.name+test123', 'fastmail.com', 'user.name+test123', 'fastmail.com'], + ['user.name+anything', 'fastmail.com', 'user.name+anything', 'fastmail.com'], + ['user.name+verylongtag', 'fastmail.com', 'user.name+verylongtag', 'fastmail.com'], + ['user.name+tag.with.dots', 'fastmail.com', 'user.name+tag.with.dots', 'fastmail.com'], + ['user.name+tag-with-hyphens', 'fastmail.com', 'user.name+tag-with-hyphens', 'fastmail.com'], + ['user.name+tag_with_underscores', 'fastmail.com', 'user.name+tag_with_underscores', 'fastmail.com'], + ['user.name+tag123', 'fastmail.com', 'user.name+tag123', 'fastmail.com'], ['u.s.e.r.n.a.m.e', 'fastmail.com', 'u.s.e.r.n.a.m.e', 'fastmail.com'], - // Edge cases - // ['user+', 'fastmail.com', 'user', 'fastmail.com'], + ['u.s.e.r.n.a.m.e+tag', 'fastmail.com', 'u.s.e.r.n.a.m.e+tag', 'fastmail.com'], + ['user+', 'fastmail.com', 'user+', 'fastmail.com'], ['user.', 'fastmail.com', 'user.', 'fastmail.com'], ['.user', 'fastmail.com', '.user', 'fastmail.com'], + ['user..name', 'fastmail.com', 'user..name', 'fastmail.com'], + // Other Fastmail domain + ['user.name+tag', 'fastmail.fm', 'user.name+tag', 'fastmail.com'], + ['user.name', 'fastmail.fm', 'user.name', 'fastmail.com'], ]; foreach ($testCases as [$inputLocal, $inputDomain, $expectedLocal, $expectedDomain]) { diff --git a/tests/Canonicals/Providers/GenericTest.php b/tests/Canonicals/Providers/GenericTest.php index 2c82ea8..0c1a05e 100644 --- a/tests/Canonicals/Providers/GenericTest.php +++ b/tests/Canonicals/Providers/GenericTest.php @@ -29,34 +29,32 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of generic providers' plus addressing, dots, and hyphens support - // // Other domains with plus addressing - // ['user.name+tag', 'example.com', 'username', 'example.com'], - // ['user.name+spam', 'example.com', 'username', 'example.com'], - // ['user.name+newsletter', 'example.com', 'username', 'example.com'], - // ['user.name+work', 'example.com', 'username', 'example.com'], - // ['user.name+personal', 'example.com', 'username', 'example.com'], - // ['user.name+test123', 'example.com', 'username', 'example.com'], - // ['user.name+anything', 'example.com', 'username', 'example.com'], - // ['user.name+verylongtag', 'example.com', 'username', 'example.com'], - // ['user.name+tag.with.dots', 'example.com', 'username', 'example.com'], - // ['user.name+tag-with-hyphens', 'example.com', 'username', 'example.com'], - // ['user.name+tag_with_underscores', 'example.com', 'username', 'example.com'], - // ['user.name+tag123', 'example.com', 'username', 'example.com'], - // Dots are preserved for other domains + // Generic providers preserve all characters (no subaddress or dot removal) ['user.name', 'example.com', 'user.name', 'example.com'], + ['user.name+tag', 'example.com', 'user.name+tag', 'example.com'], + ['user.name+spam', 'example.com', 'user.name+spam', 'example.com'], + ['user.name+newsletter', 'example.com', 'user.name+newsletter', 'example.com'], + ['user.name+work', 'example.com', 'user.name+work', 'example.com'], + ['user.name+personal', 'example.com', 'user.name+personal', 'example.com'], + ['user.name+test123', 'example.com', 'user.name+test123', 'example.com'], + ['user.name+anything', 'example.com', 'user.name+anything', 'example.com'], + ['user.name+verylongtag', 'example.com', 'user.name+verylongtag', 'example.com'], + ['user.name+tag.with.dots', 'example.com', 'user.name+tag.with.dots', 'example.com'], + ['user.name+tag-with-hyphens', 'example.com', 'user.name+tag-with-hyphens', 'example.com'], + ['user.name+tag_with_underscores', 'example.com', 'user.name+tag_with_underscores', 'example.com'], + ['user.name+tag123', 'example.com', 'user.name+tag123', 'example.com'], ['u.s.e.r.n.a.m.e', 'example.com', 'u.s.e.r.n.a.m.e', 'example.com'], - // Hyphens are preserved for other domains + ['u.s.e.r.n.a.m.e+tag', 'example.com', 'u.s.e.r.n.a.m.e+tag', 'example.com'], ['user-name', 'example.com', 'user-name', 'example.com'], - // ['user-name+tag', 'example.com', 'username', 'example.com'], - // Edge cases - // ['user+', 'example.com', 'user', 'example.com'], + ['user-name+tag', 'example.com', 'user-name+tag', 'example.com'], + ['user+', 'example.com', 'user+', 'example.com'], ['user.', 'example.com', 'user.', 'example.com'], ['.user', 'example.com', '.user', 'example.com'], + ['user..name', 'example.com', 'user..name', 'example.com'], // Test with different domains - // ['user.name+tag', 'test.org', 'username', 'test.org'], - // ['user.name+tag', 'company.net', 'username', 'company.net'], - // ['user.name+tag', 'business.co.uk', 'username', 'business.co.uk'], + ['user.name+tag', 'test.org', 'user.name+tag', 'test.org'], + ['user.name+tag', 'company.net', 'user.name+tag', 'company.net'], + ['user.name+tag', 'business.co.uk', 'user.name+tag', 'business.co.uk'], ['user.name', 'test.org', 'user.name', 'test.org'], ['user.name', 'company.net', 'user.name', 'company.net'], ['user.name', 'business.co.uk', 'user.name', 'business.co.uk'], diff --git a/tests/Canonicals/Providers/IcloudTest.php b/tests/Canonicals/Providers/IcloudTest.php index 1133d5d..682b972 100644 --- a/tests/Canonicals/Providers/IcloudTest.php +++ b/tests/Canonicals/Providers/IcloudTest.php @@ -27,30 +27,29 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of iCloud's plus addressing and dots support - // ['user.name+tag', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+spam', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+newsletter', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+work', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+personal', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+test123', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+anything', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+verylongtag', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+tag.with.dots', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+tag-with-hyphens', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+tag_with_underscores', 'icloud.com', 'username', 'icloud.com'], - // ['user.name+tag123', 'icloud.com', 'username', 'icloud.com'], - // // Other Apple domains - // ['user.name+tag', 'me.com', 'username', 'icloud.com'], - // ['user.name+tag', 'mac.com', 'username', 'icloud.com'], + // Plus-based subaddress removal (iCloud style) + ['user.name+tag', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+spam', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+newsletter', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+work', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+personal', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+test123', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+anything', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+verylongtag', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+tag.with.dots', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+tag-with-hyphens', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+tag_with_underscores', 'icloud.com', 'user.name', 'icloud.com'], + ['user.name+tag123', 'icloud.com', 'user.name', 'icloud.com'], + ['u.s.e.r.n.a.m.e+tag', 'icloud.com', 'u.s.e.r.n.a.m.e', 'icloud.com'], + ['user+', 'icloud.com', 'user', 'icloud.com'], // Dots are preserved for iCloud ['user.name', 'icloud.com', 'user.name', 'icloud.com'], ['u.s.e.r.n.a.m.e', 'icloud.com', 'u.s.e.r.n.a.m.e', 'icloud.com'], - // Edge cases - // ['user+', 'icloud.com', 'user', 'icloud.com'], ['user.', 'icloud.com', 'user.', 'icloud.com'], ['.user', 'icloud.com', '.user', 'icloud.com'], // Other Apple domains + ['user.name+tag', 'me.com', 'user.name', 'icloud.com'], + ['user.name+tag', 'mac.com', 'user.name', 'icloud.com'], ['user.name', 'me.com', 'user.name', 'icloud.com'], ['user.name', 'mac.com', 'user.name', 'icloud.com'], ]; diff --git a/tests/Canonicals/Providers/OutlookTest.php b/tests/Canonicals/Providers/OutlookTest.php index 2c2e00a..5ff625c 100644 --- a/tests/Canonicals/Providers/OutlookTest.php +++ b/tests/Canonicals/Providers/OutlookTest.php @@ -22,6 +22,11 @@ public function test_supports(): void $this->assertTrue($this->provider->supports('outlook.co.uk')); $this->assertTrue($this->provider->supports('hotmail.co.uk')); $this->assertTrue($this->provider->supports('live.co.uk')); + $this->assertTrue($this->provider->supports('msn.com')); + $this->assertTrue($this->provider->supports('passport.com')); + $this->assertTrue($this->provider->supports('outlook.de')); + $this->assertTrue($this->provider->supports('hotmail.fr')); + $this->assertTrue($this->provider->supports('live.it')); $this->assertFalse($this->provider->supports('gmail.com')); $this->assertFalse($this->provider->supports('yahoo.com')); $this->assertFalse($this->provider->supports('example.com')); @@ -30,40 +35,46 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // ['user.name+tag', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+spam', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+newsletter', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+work', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+personal', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+test123', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+anything', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+verylongtag', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+tag.with.dots', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+tag-with-hyphens', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+tag_with_underscores', 'outlook.com', 'user.name', 'outlook.com'], - // ['user.name+tag123', 'outlook.com', 'user.name', 'outlook.com'], - // ['u.s.e.r.n.a.m.e+tag', 'outlook.com', 'u.s.e.r.n.a.m.e', 'outlook.com'], - // ['user+', 'outlook.com', 'user', 'outlook.com'], + // Plus-based subaddress removal (Outlook style) + ['user.name+tag', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+spam', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+newsletter', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+work', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+personal', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+test123', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+anything', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+verylongtag', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+tag.with.dots', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+tag-with-hyphens', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+tag_with_underscores', 'outlook.com', 'user.name', 'outlook.com'], + ['user.name+tag123', 'outlook.com', 'user.name', 'outlook.com'], + ['u.s.e.r.n.a.m.e+tag', 'outlook.com', 'u.s.e.r.n.a.m.e', 'outlook.com'], + ['user+', 'outlook.com', 'user', 'outlook.com'], // Dots are preserved for Outlook ['u.s.e.r.n.a.m.e', 'outlook.com', 'u.s.e.r.n.a.m.e', 'outlook.com'], ['user.', 'outlook.com', 'user.', 'outlook.com'], ['.user', 'outlook.com', '.user', 'outlook.com'], // Hotmail - // ['user.name+tag', 'hotmail.com', 'user.name', 'outlook.com'], - // ['user.name+spam', 'hotmail.com', 'user.name', 'outlook.com'], + ['user.name+tag', 'hotmail.com', 'user.name', 'outlook.com'], + ['user.name+spam', 'hotmail.com', 'user.name', 'outlook.com'], ['user.name', 'hotmail.com', 'user.name', 'outlook.com'], // Live - // ['user.name+tag', 'live.com', 'user.name', 'outlook.com'], - // ['user.name+spam', 'live.com', 'user.name', 'outlook.com'], + ['user.name+tag', 'live.com', 'user.name', 'outlook.com'], + ['user.name+spam', 'live.com', 'user.name', 'outlook.com'], ['user.name', 'live.com', 'user.name', 'outlook.com'], // UK variants - // ['user.name+tag', 'outlook.co.uk', 'user.name', 'outlook.com'], - // ['user.name+tag', 'hotmail.co.uk', 'user.name', 'outlook.com'], - // ['user.name+tag', 'live.co.uk', 'user.name', 'outlook.com'], + ['user.name+tag', 'outlook.co.uk', 'user.name', 'outlook.com'], + ['user.name+tag', 'hotmail.co.uk', 'user.name', 'outlook.com'], + ['user.name+tag', 'live.co.uk', 'user.name', 'outlook.com'], ['user.name', 'outlook.co.uk', 'user.name', 'outlook.com'], ['user.name', 'hotmail.co.uk', 'user.name', 'outlook.com'], ['user.name', 'live.co.uk', 'user.name', 'outlook.com'], + // Additional domains + ['user.name+tag', 'msn.com', 'user.name', 'outlook.com'], + ['user.name+tag', 'passport.com', 'user.name', 'outlook.com'], + ['user.name+tag', 'outlook.de', 'user.name', 'outlook.com'], + ['user.name+tag', 'hotmail.fr', 'user.name', 'outlook.com'], + ['user.name+tag', 'live.it', 'user.name', 'outlook.com'], ]; foreach ($testCases as [$inputLocal, $inputDomain, $expectedLocal, $expectedDomain]) { @@ -81,7 +92,20 @@ public function test_get_canonical_domain(): void public function test_get_supported_domains(): void { $domains = $this->provider->getSupportedDomains(); - $expected = ['outlook.com', 'hotmail.com', 'live.com', 'outlook.co.uk', 'hotmail.co.uk', 'live.co.uk']; + $expected = [ + 'outlook.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.co.uk', + 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', + 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', + 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', + 'outlook.sa', 'outlook.sg', 'outlook.sk', + 'hotmail.com', 'hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', + 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', + 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', + 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', + 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', + 'live.com', 'live.be', 'live.co.uk', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', + 'msn.com', 'passport.com', + ]; $this->assertEquals($expected, $domains); } } diff --git a/tests/Canonicals/Providers/ProtonmailTest.php b/tests/Canonicals/Providers/ProtonmailTest.php index 8fe0af3..6ef1b69 100644 --- a/tests/Canonicals/Providers/ProtonmailTest.php +++ b/tests/Canonicals/Providers/ProtonmailTest.php @@ -27,30 +27,29 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of ProtonMail's plus addressing and dots support - // ['user.name+tag', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+spam', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+newsletter', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+work', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+personal', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+test123', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+anything', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+verylongtag', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+tag.with.dots', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+tag-with-hyphens', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+tag_with_underscores', 'protonmail.com', 'username', 'protonmail.com'], - // ['user.name+tag123', 'protonmail.com', 'username', 'protonmail.com'], - // // Other ProtonMail domains - // ['user.name+tag', 'proton.me', 'username', 'protonmail.com'], - // ['user.name+tag', 'pm.me', 'username', 'protonmail.com'], - // Dots are preserved for ProtonMail + // ProtonMail preserves all characters (no subaddress or dot removal) ['user.name', 'protonmail.com', 'user.name', 'protonmail.com'], + ['user.name+tag', 'protonmail.com', 'user.name+tag', 'protonmail.com'], + ['user.name+spam', 'protonmail.com', 'user.name+spam', 'protonmail.com'], + ['user.name+newsletter', 'protonmail.com', 'user.name+newsletter', 'protonmail.com'], + ['user.name+work', 'protonmail.com', 'user.name+work', 'protonmail.com'], + ['user.name+personal', 'protonmail.com', 'user.name+personal', 'protonmail.com'], + ['user.name+test123', 'protonmail.com', 'user.name+test123', 'protonmail.com'], + ['user.name+anything', 'protonmail.com', 'user.name+anything', 'protonmail.com'], + ['user.name+verylongtag', 'protonmail.com', 'user.name+verylongtag', 'protonmail.com'], + ['user.name+tag.with.dots', 'protonmail.com', 'user.name+tag.with.dots', 'protonmail.com'], + ['user.name+tag-with-hyphens', 'protonmail.com', 'user.name+tag-with-hyphens', 'protonmail.com'], + ['user.name+tag_with_underscores', 'protonmail.com', 'user.name+tag_with_underscores', 'protonmail.com'], + ['user.name+tag123', 'protonmail.com', 'user.name+tag123', 'protonmail.com'], ['u.s.e.r.n.a.m.e', 'protonmail.com', 'u.s.e.r.n.a.m.e', 'protonmail.com'], - // Edge cases - // ['user+', 'protonmail.com', 'user', 'protonmail.com'], + ['u.s.e.r.n.a.m.e+tag', 'protonmail.com', 'u.s.e.r.n.a.m.e+tag', 'protonmail.com'], + ['user+', 'protonmail.com', 'user+', 'protonmail.com'], ['user.', 'protonmail.com', 'user.', 'protonmail.com'], ['.user', 'protonmail.com', '.user', 'protonmail.com'], + ['user..name', 'protonmail.com', 'user..name', 'protonmail.com'], // Other ProtonMail domains + ['user.name+tag', 'proton.me', 'user.name+tag', 'protonmail.com'], + ['user.name+tag', 'pm.me', 'user.name+tag', 'protonmail.com'], ['user.name', 'proton.me', 'user.name', 'protonmail.com'], ['user.name', 'pm.me', 'user.name', 'protonmail.com'], ]; diff --git a/tests/Canonicals/Providers/YahooTest.php b/tests/Canonicals/Providers/YahooTest.php index ee1ac5b..b5b1976 100644 --- a/tests/Canonicals/Providers/YahooTest.php +++ b/tests/Canonicals/Providers/YahooTest.php @@ -29,56 +29,43 @@ public function test_supports(): void public function test_get_canonical(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Yahoo's plus addressing, dots, and hyphens support - // ['user.name+tag', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+spam', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+newsletter', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+work', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+personal', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+test123', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+anything', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+verylongtag', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+tag.with.dots', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+tag-with-hyphens', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+tag_with_underscores', 'yahoo.com', 'username', 'yahoo.com'], - // ['user.name+tag123', 'yahoo.com', 'username', 'yahoo.com'], - // // Hyphen removal - // ['user-name', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+tag', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+spam', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+newsletter', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+work', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+personal', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+test123', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+anything', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+verylongtag', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+tag.with.dots', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+tag-with-hyphens', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+tag_with_underscores', 'yahoo.com', 'username', 'yahoo.com'], - // ['user-name+tag123', 'yahoo.com', 'username', 'yahoo.com'], - // // Multiple hyphens - // ['u-s-e-r-n-a-m-e', 'yahoo.com', 'username', 'yahoo.com'], - // ['u-s-e-r-n-a-m-e+tag', 'yahoo.com', 'username', 'yahoo.com'], - // // Other Yahoo domains - // ['user.name+tag', 'yahoo.co.uk', 'username', 'yahoo.com'], - // ['user.name+tag', 'yahoo.ca', 'username', 'yahoo.com'], - // ['user.name+tag', 'ymail.com', 'username', 'yahoo.com'], - // ['user.name+tag', 'rocketmail.com', 'username', 'yahoo.com'], - // // Edge cases - // ['user+', 'yahoo.com', 'user', 'yahoo.com'], - // ['user-', 'yahoo.com', 'user', 'yahoo.com'], - // Dots and hyphens are preserved for Yahoo + // Hyphen-based subaddress removal (Yahoo style) + ['user-name', 'yahoo.com', 'user', 'yahoo.com'], + ['user-name-tag', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-spam', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-newsletter', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-work', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-personal', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-test123', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-anything', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-verylongtag', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-tag.with.dots', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-tag-with-hyphens', 'yahoo.com', 'user-name-tag-with', 'yahoo.com'], + ['user-name-tag_with_underscores', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user-name-tag123', 'yahoo.com', 'user-name', 'yahoo.com'], + // Multiple hyphens + ['u-s-e-r-n-a-m-e', 'yahoo.com', 'u-s-e-r-n-a-m', 'yahoo.com'], + ['u-s-e-r-n-a-m-e-tag', 'yahoo.com', 'u-s-e-r-n-a-m-e', 'yahoo.com'], + // Dots are preserved for Yahoo ['user.name', 'yahoo.com', 'user.name', 'yahoo.com'], - ['user-name', 'yahoo.com', 'user-name', 'yahoo.com'], + ['user.name-tag', 'yahoo.com', 'user.name', 'yahoo.com'], ['u.s.e.r.n.a.m.e', 'yahoo.com', 'u.s.e.r.n.a.m.e', 'yahoo.com'], - ['u-s-e-r-n-a-m-e', 'yahoo.com', 'u-s-e-r-n-a-m-e', 'yahoo.com'], + ['u.s.e.r.n.a.m.e-tag', 'yahoo.com', 'u.s.e.r.n.a.m.e', 'yahoo.com'], ['user.', 'yahoo.com', 'user.', 'yahoo.com'], ['.user', 'yahoo.com', '.user', 'yahoo.com'], + // Edge cases + ['user-', 'yahoo.com', 'user', 'yahoo.com'], + ['user--tag', 'yahoo.com', 'user-', 'yahoo.com'], // Other Yahoo domains - ['user.name', 'yahoo.co.uk', 'user.name', 'yahoo.com'], - ['user.name', 'yahoo.ca', 'user.name', 'yahoo.com'], - ['user.name', 'ymail.com', 'user.name', 'yahoo.com'], - ['user.name', 'rocketmail.com', 'user.name', 'yahoo.com'], + ['user.name-tag', 'yahoo.co.uk', 'user.name', 'yahoo.com'], + ['user.name-tag', 'yahoo.ca', 'user.name', 'yahoo.com'], + ['user.name-tag', 'ymail.com', 'user.name', 'yahoo.com'], + ['user.name-tag', 'rocketmail.com', 'user.name', 'yahoo.com'], + // Additional domains from validator.js + ['user.name-tag', 'yahoo.de', 'user.name', 'yahoo.com'], + ['user.name-tag', 'yahoo.fr', 'user.name', 'yahoo.com'], + ['user.name-tag', 'yahoo.in', 'user.name', 'yahoo.com'], + ['user.name-tag', 'yahoo.it', 'user.name', 'yahoo.com'], ]; foreach ($testCases as [$inputLocal, $inputDomain, $expectedLocal, $expectedDomain]) { @@ -96,7 +83,7 @@ public function test_get_canonical_domain(): void public function test_get_supported_domains(): void { $domains = $this->provider->getSupportedDomains(); - $expected = ['yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'ymail.com', 'rocketmail.com']; + $expected = ['yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com', 'rocketmail.com']; $this->assertEquals($expected, $domains); } } diff --git a/tests/Canonicals/Providers/YandexTest.php b/tests/Canonicals/Providers/YandexTest.php new file mode 100644 index 0000000..15d6ba2 --- /dev/null +++ b/tests/Canonicals/Providers/YandexTest.php @@ -0,0 +1,71 @@ +provider = new Yandex; + } + + public function test_supports(): void + { + $this->assertTrue($this->provider->supports('yandex.ru')); + $this->assertTrue($this->provider->supports('yandex.ua')); + $this->assertTrue($this->provider->supports('yandex.kz')); + $this->assertTrue($this->provider->supports('yandex.com')); + $this->assertTrue($this->provider->supports('yandex.by')); + $this->assertTrue($this->provider->supports('ya.ru')); + $this->assertFalse($this->provider->supports('gmail.com')); + $this->assertFalse($this->provider->supports('outlook.com')); + $this->assertFalse($this->provider->supports('yahoo.com')); + $this->assertFalse($this->provider->supports('example.com')); + } + + public function test_get_canonical(): void + { + $testCases = [ + // Yandex preserves all characters (no subaddress or dot removal) + ['user.name', 'yandex.ru', 'user.name', 'yandex.ru'], + ['user.name+tag', 'yandex.ru', 'user.name+tag', 'yandex.ru'], + ['user.name-tag', 'yandex.ru', 'user.name-tag', 'yandex.ru'], + ['user.name_tag', 'yandex.ru', 'user.name_tag', 'yandex.ru'], + ['u.s.e.r.n.a.m.e', 'yandex.ru', 'u.s.e.r.n.a.m.e', 'yandex.ru'], + ['u-s-e-r-n-a-m-e', 'yandex.ru', 'u-s-e-r-n-a-m-e', 'yandex.ru'], + ['user.', 'yandex.ru', 'user.', 'yandex.ru'], + ['.user', 'yandex.ru', '.user', 'yandex.ru'], + ['user+', 'yandex.ru', 'user+', 'yandex.ru'], + ['user-', 'yandex.ru', 'user-', 'yandex.ru'], + // Other Yandex domains + ['user.name+tag', 'yandex.ua', 'user.name+tag', 'yandex.ru'], + ['user.name+tag', 'yandex.kz', 'user.name+tag', 'yandex.ru'], + ['user.name+tag', 'yandex.com', 'user.name+tag', 'yandex.ru'], + ['user.name+tag', 'yandex.by', 'user.name+tag', 'yandex.ru'], + ['user.name+tag', 'ya.ru', 'user.name+tag', 'yandex.ru'], + ]; + + foreach ($testCases as [$inputLocal, $inputDomain, $expectedLocal, $expectedDomain]) { + $result = $this->provider->getCanonical($inputLocal, $inputDomain); + $this->assertEquals($expectedLocal, $result['local'], "Failed for local: {$inputLocal}@{$inputDomain}"); + $this->assertEquals($expectedDomain, $result['domain'], "Failed for domain: {$inputLocal}@{$inputDomain}"); + } + } + + public function test_get_canonical_domain(): void + { + $this->assertEquals('yandex.ru', $this->provider->getCanonicalDomain()); + } + + public function test_get_supported_domains(): void + { + $domains = $this->provider->getSupportedDomains(); + $expected = ['yandex.ru', 'yandex.ua', 'yandex.kz', 'yandex.com', 'yandex.by', 'ya.ru']; + $this->assertEquals($expected, $domains); + } +} diff --git a/tests/EmailTest.php b/tests/EmailTest.php index 36092ee..360d37b 100644 --- a/tests/EmailTest.php +++ b/tests/EmailTest.php @@ -409,37 +409,36 @@ public function test_get_unique_gmail_aliases(): void public function test_get_unique_outlook_aliases(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // // Outlook/Hotmail/Live plus addressing - // ['user.name+tag@outlook.com', 'user.name@outlook.com'], - // ['user.name+spam@outlook.com', 'user.name@outlook.com'], - // ['user.name+newsletter@outlook.com', 'user.name@outlook.com'], - // ['user.name+work@outlook.com', 'user.name@outlook.com'], - // ['user.name+personal@outlook.com', 'user.name@outlook.com'], - // ['user.name+test123@outlook.com', 'user.name@outlook.com'], - // ['user.name+anything@outlook.com', 'user.name@outlook.com'], - // ['user.name+verylongtag@outlook.com', 'user.name@outlook.com'], - // ['user.name+tag.with.dots@outlook.com', 'user.name@outlook.com'], - // ['user.name+tag-with-hyphens@outlook.com', 'user.name@outlook.com'], - // ['user.name+tag_with_underscores@outlook.com', 'user.name@outlook.com'], - // ['user.name+tag123@outlook.com', 'user.name@outlook.com'], - // // Hotmail - // ['user.name+tag@hotmail.com', 'user.name@outlook.com'], - // ['user.name+spam@hotmail.com', 'user.name@outlook.com'], - // ['user.name@hotmail.com', 'user.name@outlook.com'], - // // Live - // ['user.name+tag@live.com', 'user.name@outlook.com'], - // ['user.name+spam@live.com', 'user.name@outlook.com'], - // ['user.name@live.com', 'user.name@outlook.com'], - // // UK variants - // ['user.name+tag@outlook.co.uk', 'user.name@outlook.com'], - // ['user.name+tag@hotmail.co.uk', 'user.name@outlook.com'], - // ['user.name+tag@live.co.uk', 'user.name@outlook.com'], + // Outlook/Hotmail/Live plus addressing + ['user.name+tag@outlook.com', 'user.name@outlook.com'], + ['user.name+spam@outlook.com', 'user.name@outlook.com'], + ['user.name+newsletter@outlook.com', 'user.name@outlook.com'], + ['user.name+work@outlook.com', 'user.name@outlook.com'], + ['user.name+personal@outlook.com', 'user.name@outlook.com'], + ['user.name+test123@outlook.com', 'user.name@outlook.com'], + ['user.name+anything@outlook.com', 'user.name@outlook.com'], + ['user.name+verylongtag@outlook.com', 'user.name@outlook.com'], + ['user.name+tag.with.dots@outlook.com', 'user.name@outlook.com'], + ['user.name+tag-with-hyphens@outlook.com', 'user.name@outlook.com'], + ['user.name+tag_with_underscores@outlook.com', 'user.name@outlook.com'], + ['user.name+tag123@outlook.com', 'user.name@outlook.com'], + // Hotmail + ['user.name+tag@hotmail.com', 'user.name@outlook.com'], + ['user.name+spam@hotmail.com', 'user.name@outlook.com'], + ['user.name@hotmail.com', 'user.name@outlook.com'], + // Live + ['user.name+tag@live.com', 'user.name@outlook.com'], + ['user.name+spam@live.com', 'user.name@outlook.com'], + ['user.name@live.com', 'user.name@outlook.com'], + // UK variants + ['user.name+tag@outlook.co.uk', 'user.name@outlook.com'], + ['user.name+tag@hotmail.co.uk', 'user.name@outlook.com'], + ['user.name+tag@live.co.uk', 'user.name@outlook.com'], // Dots are preserved for Outlook ['user.name@outlook.com', 'user.name@outlook.com'], ['u.s.e.r.n.a.m.e@outlook.com', 'u.s.e.r.n.a.m.e@outlook.com'], // Edge cases - // ['user+@outlook.com', 'user@outlook.com'], + ['user+@outlook.com', 'user@outlook.com'], ['user.@outlook.com', 'user.@outlook.com'], ['.user@outlook.com', '.user@outlook.com'], // Hotmail @@ -461,50 +460,35 @@ public function test_get_unique_outlook_aliases(): void public function test_get_unique_yahoo_aliases(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Yahoo's plus addressing, dots, and hyphens support - // // Yahoo plus addressing and hyphen removal - // ['user.name+tag@yahoo.com', 'username@yahoo.com'], - // ['user.name+spam@yahoo.com', 'username@yahoo.com'], - // ['user.name+newsletter@yahoo.com', 'username@yahoo.com'], - // ['user.name+work@yahoo.com', 'username@yahoo.com'], - // ['user.name+personal@yahoo.com', 'username@yahoo.com'], - // ['user.name+test123@yahoo.com', 'username@yahoo.com'], - // ['user.name+anything@yahoo.com', 'username@yahoo.com'], - // ['user.name+verylongtag@yahoo.com', 'username@yahoo.com'], - // ['user.name+tag.with.dots@yahoo.com', 'username@yahoo.com'], - // ['user.name+tag-with-hyphens@yahoo.com', 'username@yahoo.com'], - // ['user.name+tag_with_underscores@yahoo.com', 'username@yahoo.com'], - // ['user.name+tag123@yahoo.com', 'username@yahoo.com'], - // // Hyphen removal - // ['user-name@yahoo.com', 'username@yahoo.com'], - // ['user-name+tag@yahoo.com', 'username@yahoo.com'], - // ['user-name+spam@yahoo.com', 'username@yahoo.com'], - // ['user-name+newsletter@yahoo.com', 'username@yahoo.com'], - // ['user-name+work@yahoo.com', 'username@yahoo.com'], - // ['user-name+personal@yahoo.com', 'username@yahoo.com'], - // ['user-name+test123@yahoo.com', 'username@yahoo.com'], - // ['user-name+anything@yahoo.com', 'username@yahoo.com'], - // ['user-name+verylongtag@yahoo.com', 'username@yahoo.com'], - // ['user-name+tag.with.dots@yahoo.com', 'username@yahoo.com'], - // ['user-name+tag-with-hyphens@yahoo.com', 'username@yahoo.com'], - // ['user-name+tag_with_underscores@yahoo.com', 'username@yahoo.com'], - // ['user-name+tag123@yahoo.com', 'username@yahoo.com'], - // // Multiple hyphens - // ['u-s-e-r-n-a-m-e@yahoo.com', 'username@yahoo.com'], - // ['u-s-e-r-n-a-m-e+tag@yahoo.com', 'username@yahoo.com'], - // // Other Yahoo domains - // ['user.name+tag@yahoo.co.uk', 'username@yahoo.com'], - // ['user.name+tag@yahoo.ca', 'username@yahoo.com'], - // ['user.name+tag@ymail.com', 'username@yahoo.com'], - // ['user.name+tag@rocketmail.com', 'username@yahoo.com'], - // // Edge cases - // ['user+@yahoo.com', 'user@yahoo.com'], - // ['user-@yahoo.com', 'user@yahoo.com'], - // Dots and hyphens are preserved for Yahoo + // Yahoo hyphen-based subaddress removal + ['user-name@yahoo.com', 'user@yahoo.com'], + ['user-name-tag@yahoo.com', 'user-name@yahoo.com'], + ['user-name-spam@yahoo.com', 'user-name@yahoo.com'], + ['user-name-newsletter@yahoo.com', 'user-name@yahoo.com'], + ['user-name-work@yahoo.com', 'user-name@yahoo.com'], + ['user-name-personal@yahoo.com', 'user-name@yahoo.com'], + ['user-name-test123@yahoo.com', 'user-name@yahoo.com'], + ['user-name-anything@yahoo.com', 'user-name@yahoo.com'], + ['user-name-verylongtag@yahoo.com', 'user-name@yahoo.com'], + ['user-name-tag.with.dots@yahoo.com', 'user-name@yahoo.com'], + ['user-name-tag-with-hyphens@yahoo.com', 'user-name-tag-with@yahoo.com'], + ['user-name-tag_with_underscores@yahoo.com', 'user-name@yahoo.com'], + ['user-name-tag123@yahoo.com', 'user-name@yahoo.com'], + // Multiple hyphens + ['u-s-e-r-n-a-m-e@yahoo.com', 'u-s-e-r-n-a-m@yahoo.com'], + ['u-s-e-r-n-a-m-e-tag@yahoo.com', 'u-s-e-r-n-a-m-e@yahoo.com'], + // Other Yahoo domains + ['user-name-tag@yahoo.co.uk', 'user-name@yahoo.com'], + ['user-name-tag@yahoo.ca', 'user-name@yahoo.com'], + ['user-name-tag@ymail.com', 'user-name@yahoo.com'], + ['user-name-tag@rocketmail.com', 'user-name@yahoo.com'], + // Edge cases + ['user-@yahoo.com', 'user@yahoo.com'], + // Dots are preserved for Yahoo, hyphens are removed as subaddresses ['user.name@yahoo.com', 'user.name@yahoo.com'], - ['user-name@yahoo.com', 'user-name@yahoo.com'], + ['user-name@yahoo.com', 'user@yahoo.com'], ['u.s.e.r.n.a.m.e@yahoo.com', 'u.s.e.r.n.a.m.e@yahoo.com'], - ['u-s-e-r-n-a-m-e@yahoo.com', 'u-s-e-r-n-a-m-e@yahoo.com'], + ['u-s-e-r-n-a-m-e@yahoo.com', 'u-s-e-r-n-a-m@yahoo.com'], ['user.@yahoo.com', 'user.@yahoo.com'], ['.user@yahoo.com', '.user@yahoo.com'], // Other Yahoo domains @@ -523,28 +507,27 @@ public function test_get_unique_yahoo_aliases(): void public function test_get_unique_icloud_aliases(): void { $testCases = [ - // TODO: Commented out until manual confirmation of iCloud's plus addressing and dots support - // // iCloud plus addressing - // ['user.name+tag@icloud.com', 'username@icloud.com'], - // ['user.name+spam@icloud.com', 'username@icloud.com'], - // ['user.name+newsletter@icloud.com', 'username@icloud.com'], - // ['user.name+work@icloud.com', 'username@icloud.com'], - // ['user.name+personal@icloud.com', 'username@icloud.com'], - // ['user.name+test123@icloud.com', 'username@icloud.com'], - // ['user.name+anything@icloud.com', 'username@icloud.com'], - // ['user.name+verylongtag@icloud.com', 'username@icloud.com'], - // ['user.name+tag.with.dots@icloud.com', 'username@icloud.com'], - // ['user.name+tag-with-hyphens@icloud.com', 'username@icloud.com'], - // ['user.name+tag_with_underscores@icloud.com', 'username@icloud.com'], - // ['user.name+tag123@icloud.com', 'username@icloud.com'], - // // Other Apple domains - // ['user.name+tag@me.com', 'username@icloud.com'], - // ['user.name+tag@mac.com', 'username@icloud.com'], + // iCloud plus addressing + ['user.name+tag@icloud.com', 'user.name@icloud.com'], + ['user.name+spam@icloud.com', 'user.name@icloud.com'], + ['user.name+newsletter@icloud.com', 'user.name@icloud.com'], + ['user.name+work@icloud.com', 'user.name@icloud.com'], + ['user.name+personal@icloud.com', 'user.name@icloud.com'], + ['user.name+test123@icloud.com', 'user.name@icloud.com'], + ['user.name+anything@icloud.com', 'user.name@icloud.com'], + ['user.name+verylongtag@icloud.com', 'user.name@icloud.com'], + ['user.name+tag.with.dots@icloud.com', 'user.name@icloud.com'], + ['user.name+tag-with-hyphens@icloud.com', 'user.name@icloud.com'], + ['user.name+tag_with_underscores@icloud.com', 'user.name@icloud.com'], + ['user.name+tag123@icloud.com', 'user.name@icloud.com'], + // Other Apple domains + ['user.name+tag@me.com', 'user.name@icloud.com'], + ['user.name+tag@mac.com', 'user.name@icloud.com'], // Dots are preserved for iCloud ['user.name@icloud.com', 'user.name@icloud.com'], ['u.s.e.r.n.a.m.e@icloud.com', 'u.s.e.r.n.a.m.e@icloud.com'], // Edge cases - // ['user+@icloud.com', 'user@icloud.com'], + ['user+@icloud.com', 'user@icloud.com'], ['user.@icloud.com', 'user.@icloud.com'], ['.user@icloud.com', '.user@icloud.com'], // Other Apple domains @@ -561,28 +544,27 @@ public function test_get_unique_icloud_aliases(): void public function test_get_unique_protonmail_aliases(): void { $testCases = [ - // TODO: Commented out until manual confirmation of ProtonMail's plus addressing and dots support - // // ProtonMail plus addressing - // ['user.name+tag@protonmail.com', 'username@protonmail.com'], - // ['user.name+spam@protonmail.com', 'username@protonmail.com'], - // ['user.name+newsletter@protonmail.com', 'username@protonmail.com'], - // ['user.name+work@protonmail.com', 'username@protonmail.com'], - // ['user.name+personal@protonmail.com', 'username@protonmail.com'], - // ['user.name+test123@protonmail.com', 'username@protonmail.com'], - // ['user.name+anything@protonmail.com', 'username@protonmail.com'], - // ['user.name+verylongtag@protonmail.com', 'username@protonmail.com'], - // ['user.name+tag.with.dots@protonmail.com', 'username@protonmail.com'], - // ['user.name+tag-with-hyphens@protonmail.com', 'username@protonmail.com'], - // ['user.name+tag_with_underscores@protonmail.com', 'username@protonmail.com'], - // ['user.name+tag123@protonmail.com', 'username@protonmail.com'], - // // Other ProtonMail domains - // ['user.name+tag@proton.me', 'username@protonmail.com'], - // ['user.name+tag@pm.me', 'username@protonmail.com'], - // Dots are preserved for ProtonMail + // ProtonMail preserves all characters (no subaddress or dot removal) ['user.name@protonmail.com', 'user.name@protonmail.com'], + ['user.name+tag@protonmail.com', 'user.name+tag@protonmail.com'], + ['user.name+spam@protonmail.com', 'user.name+spam@protonmail.com'], + ['user.name+newsletter@protonmail.com', 'user.name+newsletter@protonmail.com'], + ['user.name+work@protonmail.com', 'user.name+work@protonmail.com'], + ['user.name+personal@protonmail.com', 'user.name+personal@protonmail.com'], + ['user.name+test123@protonmail.com', 'user.name+test123@protonmail.com'], + ['user.name+anything@protonmail.com', 'user.name+anything@protonmail.com'], + ['user.name+verylongtag@protonmail.com', 'user.name+verylongtag@protonmail.com'], + ['user.name+tag.with.dots@protonmail.com', 'user.name+tag.with.dots@protonmail.com'], + ['user.name+tag-with-hyphens@protonmail.com', 'user.name+tag-with-hyphens@protonmail.com'], + ['user.name+tag_with_underscores@protonmail.com', 'user.name+tag_with_underscores@protonmail.com'], + ['user.name+tag123@protonmail.com', 'user.name+tag123@protonmail.com'], + // Other ProtonMail domains + ['user.name+tag@proton.me', 'user.name+tag@protonmail.com'], + ['user.name+tag@pm.me', 'user.name+tag@protonmail.com'], ['u.s.e.r.n.a.m.e@protonmail.com', 'u.s.e.r.n.a.m.e@protonmail.com'], + ['u.s.e.r.n.a.m.e+tag@protonmail.com', 'u.s.e.r.n.a.m.e+tag@protonmail.com'], // Edge cases - // ['user+@protonmail.com', 'user@protonmail.com'], + ['user+@protonmail.com', 'user+@protonmail.com'], ['user.@protonmail.com', 'user.@protonmail.com'], ['.user@protonmail.com', '.user@protonmail.com'], // Other ProtonMail domains @@ -599,27 +581,26 @@ public function test_get_unique_protonmail_aliases(): void public function test_get_unique_fastmail_aliases(): void { $testCases = [ - // TODO: Commented out until manual confirmation of Fastmail's plus addressing and dots support - // // Fastmail plus addressing - // ['user.name+tag@fastmail.com', 'username@fastmail.com'], - // ['user.name+spam@fastmail.com', 'username@fastmail.com'], - // ['user.name+newsletter@fastmail.com', 'username@fastmail.com'], - // ['user.name+work@fastmail.com', 'username@fastmail.com'], - // ['user.name+personal@fastmail.com', 'username@fastmail.com'], - // ['user.name+test123@fastmail.com', 'username@fastmail.com'], - // ['user.name+anything@fastmail.com', 'username@fastmail.com'], - // ['user.name+verylongtag@fastmail.com', 'username@fastmail.com'], - // ['user.name+tag.with.dots@fastmail.com', 'username@fastmail.com'], - // ['user.name+tag-with-hyphens@fastmail.com', 'username@fastmail.com'], - // ['user.name+tag_with_underscores@fastmail.com', 'username@fastmail.com'], - // ['user.name+tag123@fastmail.com', 'username@fastmail.com'], - // // Other Fastmail domain - // ['user.name+tag@fastmail.fm', 'username@fastmail.com'], - // Dots are preserved for Fastmail + // Fastmail preserves all characters (no subaddress or dot removal) ['user.name@fastmail.com', 'user.name@fastmail.com'], + ['user.name+tag@fastmail.com', 'user.name+tag@fastmail.com'], + ['user.name+spam@fastmail.com', 'user.name+spam@fastmail.com'], + ['user.name+newsletter@fastmail.com', 'user.name+newsletter@fastmail.com'], + ['user.name+work@fastmail.com', 'user.name+work@fastmail.com'], + ['user.name+personal@fastmail.com', 'user.name+personal@fastmail.com'], + ['user.name+test123@fastmail.com', 'user.name+test123@fastmail.com'], + ['user.name+anything@fastmail.com', 'user.name+anything@fastmail.com'], + ['user.name+verylongtag@fastmail.com', 'user.name+verylongtag@fastmail.com'], + ['user.name+tag.with.dots@fastmail.com', 'user.name+tag.with.dots@fastmail.com'], + ['user.name+tag-with-hyphens@fastmail.com', 'user.name+tag-with-hyphens@fastmail.com'], + ['user.name+tag_with_underscores@fastmail.com', 'user.name+tag_with_underscores@fastmail.com'], + ['user.name+tag123@fastmail.com', 'user.name+tag123@fastmail.com'], + // Other Fastmail domain + ['user.name+tag@fastmail.fm', 'user.name+tag@fastmail.com'], ['u.s.e.r.n.a.m.e@fastmail.com', 'u.s.e.r.n.a.m.e@fastmail.com'], + ['u.s.e.r.n.a.m.e+tag@fastmail.com', 'u.s.e.r.n.a.m.e+tag@fastmail.com'], // Edge cases - // ['user+@fastmail.com', 'user@fastmail.com'], + ['user+@fastmail.com', 'user+@fastmail.com'], ['user.@fastmail.com', 'user.@fastmail.com'], ['.user@fastmail.com', '.user@fastmail.com'], // Other Fastmail domain @@ -635,28 +616,27 @@ public function test_get_unique_fastmail_aliases(): void public function test_get_unique_other_domains(): void { $testCases = [ - // TODO: Commented out until manual confirmation of generic providers' plus addressing, dots, and hyphens support - // // Other domains with plus addressing - // ['user.name+tag@example.com', 'username@example.com'], - // ['user.name+spam@example.com', 'username@example.com'], - // ['user.name+newsletter@example.com', 'username@example.com'], - // ['user.name+work@example.com', 'username@example.com'], - // ['user.name+personal@example.com', 'username@example.com'], - // ['user.name+test123@example.com', 'username@example.com'], - // ['user.name+anything@example.com', 'username@example.com'], - // ['user.name+verylongtag@example.com', 'username@example.com'], - // ['user.name+tag.with.dots@example.com', 'username@example.com'], - // ['user.name+tag-with-hyphens@example.com', 'username@example.com'], - // ['user.name+tag_with_underscores@example.com', 'username@example.com'], - // ['user.name+tag123@example.com', 'username@example.com'], - // Dots are preserved for other domains + // Generic providers preserve all characters (no subaddress or dot removal) ['user.name@example.com', 'user.name@example.com'], + ['user.name+tag@example.com', 'user.name+tag@example.com'], + ['user.name+spam@example.com', 'user.name+spam@example.com'], + ['user.name+newsletter@example.com', 'user.name+newsletter@example.com'], + ['user.name+work@example.com', 'user.name+work@example.com'], + ['user.name+personal@example.com', 'user.name+personal@example.com'], + ['user.name+test123@example.com', 'user.name+test123@example.com'], + ['user.name+anything@example.com', 'user.name+anything@example.com'], + ['user.name+verylongtag@example.com', 'user.name+verylongtag@example.com'], + ['user.name+tag.with.dots@example.com', 'user.name+tag.with.dots@example.com'], + ['user.name+tag-with-hyphens@example.com', 'user.name+tag-with-hyphens@example.com'], + ['user.name+tag_with_underscores@example.com', 'user.name+tag_with_underscores@example.com'], + ['user.name+tag123@example.com', 'user.name+tag123@example.com'], ['u.s.e.r.n.a.m.e@example.com', 'u.s.e.r.n.a.m.e@example.com'], + ['u.s.e.r.n.a.m.e+tag@example.com', 'u.s.e.r.n.a.m.e+tag@example.com'], // Hyphens are preserved for other domains ['user-name@example.com', 'user-name@example.com'], - // ['user-name+tag@example.com', 'username@example.com'], + ['user-name+tag@example.com', 'user-name+tag@example.com'], // Edge cases - // ['user+@example.com', 'user@example.com'], + ['user+@example.com', 'user+@example.com'], ['user.@example.com', 'user.@example.com'], ['.user@example.com', '.user@example.com'], ]; @@ -672,14 +652,12 @@ public function test_get_unique_edge_cases(): void $testCases = [ // Empty plus addressing ['user+@gmail.com', 'user@gmail.com'], - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // ['user+@outlook.com', 'user@outlook.com'], - // TODO: Commented out until manual confirmation of non-Gmail providers' plus addressing support - // ['user+@yahoo.com', 'user@yahoo.com'], - // ['user+@icloud.com', 'user@icloud.com'], - // ['user+@protonmail.com', 'user@protonmail.com'], - // ['user+@fastmail.com', 'user@fastmail.com'], - // ['user+@example.com', 'user@example.com'], + ['user+@outlook.com', 'user@outlook.com'], + ['user+@yahoo.com', 'user+@yahoo.com'], + ['user+@icloud.com', 'user@icloud.com'], + ['user+@protonmail.com', 'user+@protonmail.com'], + ['user+@fastmail.com', 'user+@fastmail.com'], + ['user+@example.com', 'user+@example.com'], // Plus at the beginning ['+user@gmail.com', '+user@gmail.com'], ['+user@outlook.com', '+user@outlook.com'], @@ -690,14 +668,12 @@ public function test_get_unique_edge_cases(): void ['+user@example.com', '+user@example.com'], // Multiple plus signs (only first one is considered) ['user+tag+more@gmail.com', 'user@gmail.com'], - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // ['user+tag+more@outlook.com', 'user@outlook.com'], - // TODO: Commented out until manual confirmation of non-Gmail providers' plus addressing support - // ['user+tag+more@yahoo.com', 'user@yahoo.com'], - // ['user+tag+more@icloud.com', 'user@icloud.com'], - // ['user+tag+more@protonmail.com', 'user@protonmail.com'], - // ['user+tag+more@fastmail.com', 'user@fastmail.com'], - // ['user+tag+more@example.com', 'user@example.com'], + ['user+tag+more@outlook.com', 'user@outlook.com'], + ['user+tag+more@yahoo.com', 'user+tag+more@yahoo.com'], + ['user+tag+more@icloud.com', 'user@icloud.com'], + ['user+tag+more@protonmail.com', 'user+tag+more@protonmail.com'], + ['user+tag+more@fastmail.com', 'user+tag+more@fastmail.com'], + ['user+tag+more@example.com', 'user+tag+more@example.com'], // Special characters in plus addressing ['user+tag!@gmail.com', 'user@gmail.com'], ['user+tag#@gmail.com', 'user@gmail.com'], @@ -740,30 +716,29 @@ public function test_get_unique_case_sensitivity(): void ['USER.NAME+TAG@GMAIL.COM', 'username@gmail.com'], ['User.Name+Tag@Gmail.Com', 'username@gmail.com'], ['user.name+tag@Gmail.com', 'username@gmail.com'], - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // ['USER.NAME+TAG@OUTLOOK.COM', 'user.name@outlook.com'], - // ['User.Name+Tag@Outlook.Com', 'user.name@outlook.com'], - // ['user.name+tag@Outlook.com', 'user.name@outlook.com'], + ['USER.NAME+TAG@OUTLOOK.COM', 'user.name@outlook.com'], + ['User.Name+Tag@Outlook.Com', 'user.name@outlook.com'], + ['user.name+tag@Outlook.com', 'user.name@outlook.com'], // Dots are preserved for Outlook ['USER.NAME@OUTLOOK.COM', 'user.name@outlook.com'], ['User.Name@Outlook.Com', 'user.name@outlook.com'], ['user.name@Outlook.com', 'user.name@outlook.com'], - // TODO: Commented out until manual confirmation of non-Gmail providers' plus addressing and dots support - // ['USER.NAME+TAG@YAHOO.COM', 'username@yahoo.com'], - // ['User.Name+Tag@Yahoo.Com', 'username@yahoo.com'], - // ['user.name+tag@Yahoo.com', 'username@yahoo.com'], - // ['USER.NAME+TAG@ICLOUD.COM', 'username@icloud.com'], - // ['User.Name+Tag@Icloud.Com', 'username@icloud.com'], - // ['user.name+tag@Icloud.com', 'username@icloud.com'], - // ['USER.NAME+TAG@PROTONMAIL.COM', 'username@protonmail.com'], - // ['User.Name+Tag@Protonmail.Com', 'username@protonmail.com'], - // ['user.name+tag@Protonmail.com', 'username@protonmail.com'], - // ['USER.NAME+TAG@FASTMAIL.COM', 'username@fastmail.com'], - // ['User.Name+Tag@Fastmail.Com', 'username@fastmail.com'], - // ['user.name+tag@Fastmail.com', 'username@fastmail.com'], - // ['USER.NAME+TAG@EXAMPLE.COM', 'username@example.com'], - // ['User.Name+Tag@Example.Com', 'username@example.com'], - // ['user.name+tag@Example.com', 'username@example.com'], + // Yahoo hyphen-based subaddress removal + ['USER-NAME+TAG@YAHOO.COM', 'user@yahoo.com'], + ['User-Name+Tag@Yahoo.Com', 'user@yahoo.com'], + ['user-name+tag@Yahoo.com', 'user@yahoo.com'], + ['USER.NAME+TAG@ICLOUD.COM', 'user.name@icloud.com'], + ['User.Name+Tag@Icloud.Com', 'user.name@icloud.com'], + ['user.name+tag@Icloud.com', 'user.name@icloud.com'], + ['USER.NAME+TAG@PROTONMAIL.COM', 'user.name+tag@protonmail.com'], + ['User.Name+Tag@Protonmail.Com', 'user.name+tag@protonmail.com'], + ['user.name+tag@Protonmail.com', 'user.name+tag@protonmail.com'], + ['USER.NAME+TAG@FASTMAIL.COM', 'user.name+tag@fastmail.com'], + ['User.Name+Tag@Fastmail.Com', 'user.name+tag@fastmail.com'], + ['user.name+tag@Fastmail.com', 'user.name+tag@fastmail.com'], + ['USER.NAME+TAG@EXAMPLE.COM', 'user.name+tag@example.com'], + ['User.Name+Tag@Example.Com', 'user.name+tag@example.com'], + ['user.name+tag@Example.com', 'user.name+tag@example.com'], // Dots and pluses are preserved for non-Gmail providers ['USER.NAME@YAHOO.COM', 'user.name@yahoo.com'], ['User.Name@Yahoo.Com', 'user.name@yahoo.com'], @@ -874,24 +849,19 @@ public function test_get_unique_with_different_providers(): void $gmailEmail = new Email('user.name+tag@gmail.com'); $this->assertEquals('username@gmail.com', $gmailEmail->getCanonical()); - // TODO: Commented out until manual confirmation of Outlook's plus addressing support - // $outlookEmail = new Email('user.name+tag@outlook.com'); - // $this->assertEquals('user.name@outlook.com', $outlookEmail->getCanonical()); + $outlookEmail = new Email('user.name+tag@outlook.com'); + $this->assertEquals('user.name@outlook.com', $outlookEmail->getCanonical()); // Dots are preserved for Outlook $outlookEmail = new Email('user.name@outlook.com'); $this->assertEquals('user.name@outlook.com', $outlookEmail->getCanonical()); - // TODO: Commented out until manual confirmation of non-Gmail providers' plus addressing and dots support - // $yahooEmail = new Email('user-name+tag@yahoo.com'); - // $this->assertEquals('username@yahoo.com', $yahooEmail->getCanonical()); - - // $genericEmail = new Email('user.name+tag@example.com'); - // $this->assertEquals('username@example.com', $genericEmail->getCanonical()); + $genericEmail = new Email('user.name+tag@example.com'); + $this->assertEquals('user.name+tag@example.com', $genericEmail->getCanonical()); - // Dots and pluses are preserved for non-Gmail providers + // Yahoo removes hyphen-based subaddresses, other providers preserve characters $yahooEmail = new Email('user-name@yahoo.com'); - $this->assertEquals('user-name@yahoo.com', $yahooEmail->getCanonical()); + $this->assertEquals('user@yahoo.com', $yahooEmail->getCanonical()); $genericEmail = new Email('user.name@example.com'); $this->assertEquals('user.name@example.com', $genericEmail->getCanonical()); From 00af9c718ce5fb04f08fc8d8e52b7077dc30c96c Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 20 Oct 2025 18:05:31 +0100 Subject: [PATCH 2/2] Update composer.json to replace utopia-php/framework dependency with utopia-php/validators for improved validation capabilities. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b523e67..79d1a2a 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.33.*", + "utopia-php/validators": "^0.0.1", "utopia-php/cli": "^0.15", "utopia-php/domains": "^0.8", "utopia-php/fetch": "^0.4"