From 537afdb7cf3b21eef04696b809c060de8050ccba Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Fri, 17 Jan 2025 16:15:36 +0100 Subject: [PATCH] [11.x] Solidify `Rule::email()` tests (#54226) * Update ValidationEmailRuleTest.php * Update ValidationEmailRuleTest.php * Styleci * styleci * Update ValidationEmailRuleTest.php * Update ValidationEmailRuleTest.php * Update to use TestWith instead of inline arrays * styleci --- tests/Validation/ValidationEmailRuleTest.php | 556 +++++++++++++++---- 1 file changed, 457 insertions(+), 99 deletions(-) diff --git a/tests/Validation/ValidationEmailRuleTest.php b/tests/Validation/ValidationEmailRuleTest.php index a6e207910ac3..b6382f508bc4 100644 --- a/tests/Validation/ValidationEmailRuleTest.php +++ b/tests/Validation/ValidationEmailRuleTest.php @@ -11,6 +11,7 @@ use Illuminate\Validation\Rules\Email; use Illuminate\Validation\ValidationServiceProvider; use Illuminate\Validation\Validator; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class ValidationEmailRuleTest extends TestCase @@ -23,22 +24,23 @@ public function testBasic() $this->fails( Email::default(), 'foo', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); + $this->fails( Rule::email(), 'foo', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( Email::default(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); $this->passes( Rule::email(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); $this->passes(Email::default(), null); @@ -80,11 +82,12 @@ protected function assertValidationRules($rule, $values, $expectToPass, $expecte $customValidationMessage ? [self::ATTRIBUTE.'.email' => $customValidationMessage] : [] ); - $this->assertSame($expectToPass, $v->passes()); + $this->assertSame($expectToPass, $v->passes(), 'Expected email input '.$value.' to '.($expectToPass ? 'pass' : 'fail').'.'); $this->assertSame( $expectToPass ? [] : [self::ATTRIBUTE => $expectedMessages], - $v->messages()->toArray() + $v->messages()->toArray(), + 'Expected different message for email input '.$value, ); } } @@ -99,291 +102,646 @@ protected function passes($rule, $values) $this->assertValidationRules($rule, $values, true); } - public function testStrict() + public function testRfcCompliantStrict() { + $emailThatFailsBothNonStrictButFailsInStrict = 'username@sub..example.com'; + $emailThatPassesNonStrictButFailsInStrict = '"has space"@example.com'; + $emailThatPassesBothNonStrictAndInStrict = 'plainaddress@example.com'; + $this->fails( - (new Email())->rfcCompliant(true), - 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + (new Email())->rfcCompliant(strict: true), + $emailThatPassesNonStrictButFailsInStrict, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - Rule::email()->rfcCompliant(true), - 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + Rule::email()->rfcCompliant(strict: true), + $emailThatPassesNonStrictButFailsInStrict, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - (new Email())->rfcCompliant(true), - 'username@sub..example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + (new Email())->rfcCompliant(strict: true), + $emailThatFailsBothNonStrictButFailsInStrict, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - Rule::email()->rfcCompliant(true), - 'username@sub..example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + Rule::email()->rfcCompliant(strict: true), + $emailThatFailsBothNonStrictButFailsInStrict, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( - (new Email())->rfcCompliant(true), - 'plainaddress@example.com', + (new Email())->rfcCompliant(strict: true), + $emailThatPassesBothNonStrictAndInStrict ); $this->passes( - Rule::email()->rfcCompliant(true), - 'plainaddress@example.com', + Rule::email()->rfcCompliant(strict: true), + $emailThatPassesBothNonStrictAndInStrict ); } - public function testDns() + public function testValidateMxRecord() { $this->fails( (new Email())->validateMxRecord(), 'plainaddress@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->validateMxRecord(), 'plainaddress@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( (new Email())->validateMxRecord(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); $this->passes( Rule::email()->validateMxRecord(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); } - public function testSpoof() + public function testPreventSpoofing() { $this->fails( (new Email())->preventSpoofing(), 'admin@examрle.com',// Contains a Cyrillic 'р' (U+0440), not a Latin 'p' - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->preventSpoofing(), 'admin@examрle.com',// Contains a Cyrillic 'р' (U+0440), not a Latin 'p' - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $spoofingEmail = 'admin@exam'."\u{0440}".'le.com'; $this->fails( (new Email())->preventSpoofing(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->preventSpoofing(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( (new Email())->preventSpoofing(), - 'admin@example.com', + 'admin@example.com' ); $this->passes( Rule::email()->preventSpoofing(), - 'admin@example.com', + 'admin@example.com' ); $this->passes( (new Email())->preventSpoofing(), - 'test👨‍💻@domain.com', + 'test👨‍💻@domain.com' ); $this->passes( Rule::email()->preventSpoofing(), - 'test👨‍💻@domain.com', + 'test👨‍💻@domain.com' ); } - public function testFilter() + public function testWithNativeValidation() { $this->fails( (new Email())->withNativeValidation(), 'tést@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->withNativeValidation(), 'tést@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( (new Email())->withNativeValidation(), - 'admin@example.com', + 'admin@example.com' ); $this->passes( Rule::email()->withNativeValidation(), - 'admin@example.com', + 'admin@example.com' ); } - public function testFilterUnicode() + public function testWithNativeValidationAllowUnicode() { $this->fails( - (new Email())->withNativeValidation(true), + (new Email())->withNativeValidation(allowUnicode: true), 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - Rule::email()->withNativeValidation(true), + Rule::email()->withNativeValidation(allowUnicode: true), 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( - (new Email())->withNativeValidation(true), - 'tést@domain.com', + (new Email())->withNativeValidation(allowUnicode: true), + 'tést@domain.com' ); $this->passes( - Rule::email()->withNativeValidation(true), - 'tést@domain.com', + Rule::email()->withNativeValidation(allowUnicode: true), + 'tést@domain.com' ); $this->passes( - (new Email())->withNativeValidation(true), - 'admin@example.com', + (new Email())->withNativeValidation(allowUnicode: true), + 'admin@example.com' ); $this->passes( - Rule::email()->withNativeValidation(true), - 'admin@example.com', + Rule::email()->withNativeValidation(allowUnicode: true), + 'admin@example.com' ); } - public function testRfc() + public function testRfcCompliantNonStrict() { $this->fails( (new Email())->rfcCompliant(), 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->rfcCompliant(), 'invalid.@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( (new Email())->rfcCompliant(), 'test👨‍💻@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->rfcCompliant(), 'test👨‍💻@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( (new Email())->rfcCompliant(), - 'admin@example.com', + 'admin@example.com' ); $this->passes( Rule::email()->rfcCompliant(), - 'admin@example.com', + 'admin@example.com' ); $this->passes( (new Email())->rfcCompliant(), - 'tést@domain.com', + 'tést@domain.com' ); $this->passes( Rule::email()->rfcCompliant(), - 'tést@domain.com', + 'tést@domain.com' + ); + } + + #[TestWith(['"has space"@example.com'])] // Quoted local part with space + #[TestWith(['some(comment)@example.com'])] // Comment in local part + #[TestWith(['abc."test"@example.com'])] // Mixed quoted/unquoted local part + #[TestWith(['"escaped\\\"quote"@example.com'])] // Escaped quote inside quoted local part + #[TestWith(['test@example'])] // Domain without TLD + #[TestWith(['test@localhost'])] // Domain without TLD + #[TestWith(['name@[127.0.0.1]'])] // Local-part with domain-literal IPv4 address + #[TestWith(['user@[IPv6:::1]'])] // Domain-literal with unusual IPv6 short form + #[TestWith(['a@[IPv6:2001:db8::1]'])] // Domain-literal with normal IPv6 + #[TestWith(['user@[IPv6:::]'])] // invalid shorthand IPv6 + #[TestWith(['"ab\\(c"@example.com'])] + public function testEmailsThatPassOnRfcCompliantButFailOnStrict($email) + { + $this->passes( + Rule::email()->rfcCompliant(), + $email + ); + + $this->fails( + Rule::email()->rfcCompliant(strict: true), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + #[TestWith(['plainaddress@example.com'])] + #[TestWith(['joe.smith@example.io'])] + #[TestWith(['custom-tag+dev@example.org'])] + #[TestWith(['hyphens--@example.org'])] + #[TestWith(['underscore_name@example.co.uk'])] + #[TestWith(['underscores__@example.org'])] + #[TestWith(['user@subdomain.example.com'])] + #[TestWith(['numbers123@domain.com'])] + #[TestWith(['john-doe@some-domain.com'])] + #[TestWith(['UPPERlower@example.org'])] + #[TestWith(['dots.ok@sub.domain.io'])] + #[TestWith(['some_email+tag@domain.dev'])] + #[TestWith(['a@b.c'])] + #[TestWith(['user@xn--bcher-kva.example'])] + #[TestWith(['user@bücher.example'])] + public function testEmailsThatPassOnBothRfcCompliantAndStrict($email) + { + $this->passes( + Rule::email()->rfcCompliant(), + $email + ); + + $this->passes( + Rule::email()->rfcCompliant(strict: true), + $email + ); + } + + #[TestWith(['invalid.@example.com'])] + #[TestWith(['invalid@.example.com'])] + #[TestWith(['.invalid@example.com'])] + #[TestWith(['invalid@example.com.'])] + #[TestWith(['some..dots@example.com'])] + #[TestWith(['username@sub..example.com'])] + #[TestWith(['test@example..com'])] + #[TestWith(['test@@example.com'])] + #[TestWith(['test👨‍💻@domain.com'])] + #[TestWith(['username@domain-with-hyphen-.com'])] + #[TestWith(['()<>[]:,;@example.com'])] + #[TestWith(['@example.com'])] + #[TestWith(['[test]@example.com'])] + #[TestWith(['user@example.com:3000'])] + #[TestWith(['"unescaped"quote@example.com'])] + #[TestWith(['https://example.com'])] + #[TestWith(['with\\escape@example.com'])] + public function testEmailsThatFailOnBothRfcCompliantAndStrict($email) + { + $this->fails( + Rule::email()->rfcCompliant(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->fails( + Rule::email()->rfcCompliant(strict: true), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + #[TestWith(['plainaddress@example.com'])] // Simple valid address + #[TestWith(['joe.smith@example.io'])] // Dotted local part with TLD + #[TestWith(['custom-tag+dev@example.org'])] // Plus tag in local part + #[TestWith(['hyphens--@example.org'])] // Hyphens in local part + #[TestWith(['underscore_name@example.co.uk'])] // Underscore in local part + #[TestWith(['underscores__@example.org'])] // Double underscores in local part + #[TestWith(['user@subdomain.example.com'])] // Subdomain in domain part + #[TestWith(['numbers123@domain.com'])] // Numbers in local part + #[TestWith(['john-doe@some-domain.com'])] // Hyphenated domain + #[TestWith(['UPPERlower@example.org'])] // Mixed case local part + #[TestWith(['dots.ok@sub.domain.io'])] // Dots in local and subdomain + #[TestWith(['some_email+tag@domain.dev'])] // Email with plus tag and underscore + #[TestWith(['a@b.c'])] // Minimal email + #[TestWith(['user@xn--bcher-kva.example'])] // Punycode domain (bücher) + #[TestWith(['user@bücher.example'])] // Unicode domain + public function testEmailsThatPassOnBothRfcCompliantAndRfcCompliantStrict($email) + { + $this->passes( + Rule::email()->rfcCompliant(), + $email + ); + + $this->passes( + Rule::email()->rfcCompliant(strict: true), + $email + ); + } + + #[TestWith(['déjà@example.com'])] + #[TestWith(['测试@example.com'])] + public function testEmailsThatFailWithNativeValidationAsciiPassUnicode($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->passes( + Rule::email()->withNativeValidation(allowUnicode: true), + $email + ); + } + + #[TestWith(['test@üñîçødé.com'])] // Unicode domain + #[TestWith(['user@domain..com'])] // Double dots in domain + #[TestWith(['test@.example.com'])] // Domain starts with a dot + #[TestWith(['username@domain-with-hyphen-.com'])] + #[TestWith(['пример@пример.рф'])] // Cyrillic domain + #[TestWith(['例子@例子.公司'])] // Chinese domain + #[TestWith(['name@123.123.123.123'])] // Numeric domain + public function testEmailsThatFailOnBothWithNativeValidationAsciiAndUnicode($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->fails( + Rule::email()->withNativeValidation(allowUnicode: true), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + #[TestWith(['user@example.com'])] + #[TestWith(['user.name+tag@example.co.uk'])] + #[TestWith(['joe_smith@example.org'])] + #[TestWith(['user@[IPv6:2001:db8:1ff::a0b:dbd0]'])] + #[TestWith(['test@xn--bcher-kva.com'])] // Punycode for bücher.com + public function testEmailsThatPassBothWithNativeValidationAsciiAndUnicode($email) + { + $this->passes( + Rule::email()->withNativeValidation(), + $email + ); + + $this->passes( + Rule::email()->withNativeValidation(allowUnicode: true), + $email + ); + } + + #[TestWith(['some(comment)@example.com'])] // Comment in local part + #[TestWith(['tést@example.com'])] // Accented local part + #[TestWith(['user@üñîçødé.com'])] // Unicode domain + #[TestWith(['user@bücher.example'])] // Unicode domain + #[TestWith(['"has space"@example.com'])] // Quoted local part with space + #[TestWith(['"escaped\\\"quote"@example.com'])] // Escaped quote inside quoted local part + #[TestWith(['test@localhost'])] // Domain without TLD + #[TestWith(['test@example'])] // Domain without TLD + #[TestWith(['пример@пример.рф'])] // Cyrillic local and domain + #[TestWith(['例子@例子.公司'])] // Chinese local and domain + #[TestWith(['name@123.123.123.123'])] // Numeric domain + public function testEmailsThatFailWithNativeValidationAsciiPassRfcCompliant($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->passes( + Rule::email()->rfcCompliant(), + $email + ); + } + + #[TestWith(['plainaddress@example.com'])] // Simple valid address + #[TestWith(['joe.smith@example.io'])] // Dot in local part + #[TestWith(['custom-tag+dev@example.org'])] // Plus tag in local part + #[TestWith(['hyphens--@example.org'])] // Double hyphen in local part + #[TestWith(['underscore_name@example.co.uk'])] // Underscore in local part + #[TestWith(['underscores__@example.org'])] // Double underscores in local part + #[TestWith(['user@subdomain.example.com'])] // Subdomain in domain + #[TestWith(['numbers123@domain.com'])] // Numbers in local part + #[TestWith(['john-doe@some-domain.com'])] // Hyphen in domain + #[TestWith(['UPPERlower@example.org'])] // Mixed-case local part + #[TestWith(['dots.ok@sub.domain.io'])] // Subdomain with dot in local part + #[TestWith(['some_email+tag@domain.dev'])] // Underscore and tag in local part + #[TestWith(['a@b.c'])] // Minimal valid address + #[TestWith(['user@xn--bcher-kva.example'])] // Punycode domain (bücher.example) + #[TestWith(['user_name+tag@example.io'])] // Underscore with tag + #[TestWith(['UPPERCASE@EXAMPLE.IO'])] // All uppercase local and domain + #[TestWith(['abc."test"@example.com'])] // Mixed quoted/unquoted local part + #[TestWith(['name@[127.0.0.1]'])] // IPv4 domain literal + #[TestWith(['user@[IPv6:::1]'])] // IPv6 domain with unusual short form + #[TestWith(['a@[IPv6:2001:db8::1]'])] // IPv6 domain normal form + #[TestWith(['user@[IPv6:2001:db8:1ff::a0b:dbd0]'])] // Fully expanded IPv6 + public function testEmailsThatPassWithNativeValidationAndRfcCompliant($email) + { + $this->passes( + Rule::email()->withNativeValidation(), + $email + ); + + $this->passes( + Rule::email()->rfcCompliant(), + $email + ); + } + + #[TestWith(['test@@example.com'])] // Multiple @ symbols + #[TestWith(['user@domain..com'])] // Double dots in domain + #[TestWith(['.leadingdot@example.com'])] // Leading dot in local part + #[TestWith(['with\\escape@example.com'])] // Backslash in local part + #[TestWith(['@example.com'])] // Missing local part + #[TestWith(['some)@example.com'])] // Unmatched parenthesis in local part + #[TestWith([' space@domain.com'])] // Leading space in local part + #[TestWith(['user@domain:port.com'])] // Colon in domain (mimics a port) + #[TestWith(['username@domain-with-hyphen-.com'])] // Trailing hyphen in domain + public function testEmailsThatFailWithNativeValidationAndRfcCompliant($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->fails( + Rule::email()->rfcCompliant(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + public function testNativeValidationVsRfcCompliant() + { + $emailsThatPassNativeFailRfc = [ + // none I could find + ]; + + foreach ($emailsThatPassNativeFailRfc as $email) { + $this->passes( + Rule::email()->withNativeValidation(), + $email + ); + + $this->fails( + Rule::email()->rfcCompliant(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + } + + #[TestWith(['abc."test"@example.com'])] // Mixed quotes in local part + #[TestWith(['name@[127.0.0.1]'])] // Local-part with domain-literal IPv4 address + #[TestWith(['user@[IPv6:2001:db8::1]'])] // Domain-literal with normal IPv6 + #[TestWith(['user@[IPv6:2001:db8:1ff::a0b:dbd0]'])] // Domain-literal with full IPv6 address + #[TestWith(['"ab\\(c"@example.com'])] // Quoted local part with escaped character + public function testEmailsThatPassNativeValidationFailRfcCompliantStrict($email) + { + $this->passes( + Rule::email()->withNativeValidation(), + $email + ); + + $this->fails( + Rule::email()->rfcCompliant(true), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + #[TestWith(['пример@пример.рф'])] // Unicode domain in Cyrillic script + #[TestWith(['例子@例子.公司'])] // Unicode domain in Chinese script + #[TestWith(['name@123.123.123.123'])] // IP address in domain part + public function testEmailsThatFailNativeValidationPassRfcCompliantStrict($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->passes( + Rule::email()->rfcCompliant(true), + $email + ); + } + + #[TestWith(['user@example.com'])] // Simple, valid email + #[TestWith(['joe.smith+dev@example.co.uk'])] // Plus-tagged email with subdomain TLD + #[TestWith(['user!#$%&\'*+/=?^_`{|}~@example.com'])] // Unusual valid characters in local part + public function testEmailsThatPassBothNativeValidationAndRfcCompliantStrict($email) + { + $this->passes( + Rule::email()->withNativeValidation(), + $email + ); + + $this->passes( + Rule::email()->rfcCompliant(true), + $email + ); + } + + #[TestWith(['test@@example.com'])] // Multiple @ + #[TestWith(['.leadingdot@example.com'])] // Leading dot in local part + #[TestWith(['user@domain..com'])] // Double dots in domain + #[TestWith(['test@'])] // Missing domain + #[TestWith(['abc"quote@example.com'])] // Unescaped quote in local part + #[TestWith(['some(comment)@example.com'])] // Local part comment + #[TestWith(['"has space"@example.com'])] // Quoted local part with space + #[TestWith(['user@domain(comment)'])] // Comment in domain + #[TestWith(['user@[127.0.0.1(comment)]'])] // Comment in domain-literal IPv4 address + #[TestWith(['some((double))comment@example.com'])] // Nested comment in local part + #[TestWith(['"test\\\"quote"@example.com'])] // Escaped quote in quoted local part + #[TestWith(['" leading.space"@example.com'])] // Leading space in quoted local part + public function testEmailsThatFailBothNativeValidationAndRfcCompliantStrict($email) + { + $this->fails( + Rule::email()->withNativeValidation(), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->fails( + Rule::email()->rfcCompliant(true), + $email, + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); } public function testCombiningRules() { $this->passes( - (new Email())->rfcCompliant(true)->preventSpoofing(), - 'test@example.com', + (new Email())->rfcCompliant(strict: true)->preventSpoofing(), + 'test@example.com' ); $this->passes( - Rule::email()->rfcCompliant(true)->preventSpoofing(), - 'test@example.com', + Rule::email()->rfcCompliant(strict: true)->preventSpoofing(), + 'test@example.com' ); $this->fails( - (new Email())->rfcCompliant(true)->preventSpoofing()->validateMxRecord(), + (new Email())->rfcCompliant(strict: true)->preventSpoofing()->validateMxRecord(), 'test@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - Rule::email()->rfcCompliant(true)->preventSpoofing()->validateMxRecord(), + Rule::email()->rfcCompliant(strict: true)->preventSpoofing()->validateMxRecord(), 'test@example.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->passes( (new Email())->preventSpoofing(), - 'test👨‍💻@domain.com', + 'test👨‍💻@domain.com' ); $this->passes( Rule::email()->preventSpoofing(), - 'test👨‍💻@domain.com', + 'test👨‍💻@domain.com' ); $this->fails( (new Email())->preventSpoofing()->rfcCompliant(), 'test👨‍💻@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->preventSpoofing()->rfcCompliant(), 'test👨‍💻@domain.com', - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $spoofingEmail = 'admin@exam'."\u{0440}".'le.com'; $this->passes( (new Email())->rfcCompliant(), - $spoofingEmail, + $spoofingEmail ); $this->passes( Rule::email()->rfcCompliant(), - $spoofingEmail, + $spoofingEmail ); $this->fails( (new Email())->rfcCompliant()->preventSpoofing(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( Rule::email()->rfcCompliant()->preventSpoofing(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); } @@ -407,12 +765,12 @@ public function testMacro() $this->passes( Email::laravelEmployee(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); $this->passes( Rule::email()->laravelEmployee(), - 'taylor@laravel.com', + 'taylor@laravel.com' ); } @@ -424,7 +782,7 @@ public function testItCanSetDefaultUsing() $this->passes( Email::default(), - $spoofingEmail, + $spoofingEmail ); Email::defaults(function () { @@ -434,7 +792,7 @@ public function testItCanSetDefaultUsing() $this->fails( Email::default(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); Email::defaults(function () { @@ -443,7 +801,7 @@ public function testItCanSetDefaultUsing() $this->passes( Email::default(), - $spoofingEmail, + $spoofingEmail ); Email::defaults(function () { @@ -453,7 +811,7 @@ public function testItCanSetDefaultUsing() $this->fails( Email::default(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); } @@ -468,28 +826,28 @@ public function testValidationMessages() $this->fails( Email::default(), $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] ); $this->fails( - Email::default(), - $spoofingEmail, - ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], - 'The :attribute must be a valid email address.', + rule: Email::default(), + values: $spoofingEmail, + expectedMessages: ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'], + customValidationMessage: 'The :attribute must be a valid email address.' ); $this->fails( - Email::default(), - $spoofingEmail, - ['Please check the entered '.self::ATTRIBUTE_REPLACED.", it must be a valid email address, {$spoofingEmail} given."], - 'Please check the entered :attribute, it must be a valid email address, :input given.' + rule: Email::default(), + values: $spoofingEmail, + expectedMessages: ['Please check the entered '.self::ATTRIBUTE_REPLACED.", it must be a valid email address, {$spoofingEmail} given."], + customValidationMessage: 'Please check the entered :attribute, it must be a valid email address, :input given.' ); $this->fails( - Email::default(), - $spoofingEmail, - ['Plain text value'], - 'Plain text value' + rule: Email::default(), + values: $spoofingEmail, + expectedMessages: ['Plain text value'], + customValidationMessage: 'Plain text value' ); }