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" 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());