-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from Q42/feature/email-validation
E-mail validation
- Loading branch information
Showing
4 changed files
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// | ||
// Validator+Email.swift | ||
// ValidationKit | ||
// | ||
// Created by Mathijs Bernson on 25/05/2023. | ||
// | ||
|
||
import Foundation | ||
|
||
private let emailPattern = "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + | ||
"\\@" + | ||
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + | ||
"(" + | ||
"\\." + | ||
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + | ||
")+" | ||
|
||
public extension Validator { | ||
/// Validates whether the value is a valid e-mail address, according to the WHATWG HTML living standard. | ||
/// | ||
/// A valid email address is a string that matches the email production of the following ABNF, the character set for which is Unicode. This ABNF implements the extensions described in RFC 1123. | ||
/// | ||
/// ``` | ||
/// email = 1*( atext / "." ) "@" label *( "." label ) | ||
/// label = let-dig [ [ ldh-str ] let-dig ] ; limited to a length of 63 characters by RFC 1034 section 3.5 | ||
/// atext = < as defined in RFC 5322 section 3.2.3 > | ||
/// let-dig = < as defined in RFC 1034 section 3.5 > | ||
/// ldh-str = < as defined in RFC 1034 section 3.5 > | ||
/// ``` | ||
/// | ||
/// Note: This requirement is a willful violation of RFC 5322, which defines a syntax for email addresses that is simultaneously too strict (before the "@" character), too vague (after the "@" character), and too lax (allowing comments, whitespace characters, and quoted strings in manners unfamiliar to most users) to be of practical use here. | ||
/// | ||
/// Reference: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address | ||
static var email: Validator<String, String> { | ||
Validator<String, String> { input in | ||
if let range = input.range(of: emailPattern, options: .regularExpression) { | ||
let output = String(input[range]) | ||
return .valid(output) | ||
} else { | ||
return .invalid(.invalidEmail) | ||
} | ||
} | ||
} | ||
} | ||
public extension ValidationError { | ||
static let invalidEmail = ValidationError(localizedDescription: NSLocalizedString("invalid_email", comment: "Invalid e-mail address error")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import XCTest | ||
import ValidationKit | ||
|
||
class EmailTests: XCTestCase { | ||
var validator: Validator<String, String>! | ||
|
||
override func setUpWithError() throws { | ||
validator = .email | ||
} | ||
|
||
func testValidEmailAddresses() throws { | ||
XCTAssertTrue(validator.validate(input: "mathijsb+test@q42.nl").isValid) | ||
XCTAssertTrue(validator.validate(input: "mathijsb@subdomain.q42.nl").isValid) | ||
|
||
// Test cases from: https://www.softwaretestingo.com/test-cases-for-email-field/ | ||
XCTAssertTrue(validator.validate(input: "email@domain.com").isValid, "Valid email") | ||
XCTAssertTrue(validator.validate(input: "firstname.lastname@domain.com").isValid, "The email contains a dot in the address field") | ||
XCTAssertTrue(validator.validate(input: "email@subdomain.domain.com").isValid, "The email contains a dot with a subdomain") | ||
XCTAssertTrue(validator.validate(input: "firstname+lastname@domain.com").isValid, "Plus sign is considered a valid character") | ||
XCTAssertTrue(validator.validate(input: "email@123.123.123.123").isValid, "The domain is a valid IP address") | ||
XCTAssertTrue(validator.validate(input: "1234567890@domain.com").isValid, "Digits in the address are valid") | ||
XCTAssertTrue(validator.validate(input: "email@domain-one.com").isValid, "Dash in the domain name is valid") | ||
XCTAssertTrue(validator.validate(input: "_______@domain.com").isValid, "Underscore in the address field is valid") | ||
XCTAssertTrue(validator.validate(input: "email@domain.name").isValid, ".name is a valid Top Level Domain name") | ||
XCTAssertTrue(validator.validate(input: "email@domain.co.jp").isValid, "Dot in Top Level Domain name also considered valid (use co.jp as an example here)") | ||
XCTAssertTrue(validator.validate(input: "firstname-lastname@domain.com").isValid, "Dash in the address field is valid") | ||
|
||
// Test cases from ChatGPT | ||
XCTAssertTrue(validator.validate(input: "example@example.com").isValid, "Standard email format") | ||
XCTAssertTrue(validator.validate(input: "john.doe@example.co.uk").isValid, "Email with a subdomain") | ||
XCTAssertTrue(validator.validate(input: "user123@example123.com").isValid, "Email with numbers in the domain name") | ||
XCTAssertTrue(validator.validate(input: "john_doe+test@example.com").isValid, "Email with special characters in the local part") | ||
XCTAssertTrue(validator.validate(input: "user@example.io").isValid, "Email with a two-letter top-level domain (TLD)") | ||
XCTAssertTrue(validator.validate(input: "test-email@example-domain.com").isValid, "Email with a hyphen in the domain name") | ||
XCTAssertTrue(validator.validate(input: "a@example.com").isValid, "Email with a single-letter local part") | ||
XCTAssertTrue(validator.validate(input: "thisisaverylongemailaddresswithlotsofcharacters@example.com").isValid, "Email with a long local part and domain name") | ||
XCTAssertTrue(validator.validate(input: ".test@example.com").isValid, "Email with a dot at the beginning of the local part") | ||
XCTAssertTrue(validator.validate(input: "test.@example.com").isValid, "Email with a dot at the end of the local part") | ||
|
||
// These cases are currently not considered valid, but they should be | ||
// XCTAssertTrue(validator.validate(input: "email@[123.123.123.123]").isValid, "A square bracket around the IP address is considered valid") | ||
// XCTAssertTrue(validator.validate(input: "“email”@domain.com").isValid, "Quotes around email are considered valid") | ||
} | ||
|
||
func testInvalidEmailAddresses() throws { | ||
XCTAssertFalse(validator.validate(input: "").isValid) | ||
XCTAssertFalse(validator.validate(input: "foo").isValid) | ||
XCTAssertFalse(validator.validate(input: "foobarbazquuxwhopper").isValid) | ||
|
||
// Test cases from: https://www.softwaretestingo.com/test-cases-for-email-field/ | ||
XCTAssertFalse(validator.validate(input: "plain address").isValid, "Missing @ sign and domain") | ||
XCTAssertFalse(validator.validate(input: "#@%^%#$@#$@#.com").isValid, "Garbage") | ||
XCTAssertFalse(validator.validate(input: "@domain.com").isValid, "Missing username") | ||
XCTAssertFalse(validator.validate(input: "email.domain.com").isValid, "Missing @") | ||
XCTAssertFalse(validator.validate(input: "email@domain").isValid, "Missing top-level domain (.com/.net/.org/etc.)") | ||
XCTAssertFalse(validator.validate(input: "email@-domain.com").isValid, "The leading dash in front of the domain is invalid") | ||
XCTAssertFalse(validator.validate(input: "email@domain..com").isValid, "Multiple dots in the domain portion is invalid") | ||
|
||
// Test cases from ChatGPT | ||
XCTAssertFalse(validator.validate(input: "example.com").isValid, "Missing @ symbol") | ||
XCTAssertFalse(validator.validate(input: "john@.com").isValid, "Email without a domain name") | ||
XCTAssertFalse(validator.validate(input: "user@example").isValid, "Email without a top-level domain (TLD)") | ||
XCTAssertFalse(validator.validate(input: "john@example#.com").isValid, "Email with invalid characters in the domain name") | ||
XCTAssertFalse(validator.validate(input: "@example.com").isValid, "Email without a local part") | ||
XCTAssertFalse(validator.validate(input: "john_doe@_example.com").isValid, "Email with an underscore at the beginning of the domain name") | ||
XCTAssertFalse(validator.validate(input: "user@example.").isValid, "Email with a missing domain extension") | ||
|
||
// These cases are currently considered valid, but they should not be | ||
// XCTAssertFalse(validator.validate(input: "email.@domain.com").isValid, "Trailing dot in address is not allowed") | ||
// XCTAssertFalse(validator.validate(input: "Joe Smith <email@domain.com>").isValid, "Encoded HTML within an email is invalid") | ||
// XCTAssertFalse(validator.validate(input: "email@domain@domain.com").isValid, "Two @ sign") | ||
// XCTAssertFalse(validator.validate(input: ".email@domain.com").isValid, "The leading dot in the address is not allowed") | ||
// XCTAssertFalse(validator.validate(input: "email..email@domain.com").isValid, "Multiple dots") | ||
// XCTAssertFalse(validator.validate(input: "あいうえお@domain.com").isValid, "Unicode char as address") | ||
// XCTAssertFalse(validator.validate(input: "email@domain.com (Joe Smith)").isValid, "Text followed email is not allowed") | ||
// XCTAssertFalse(validator.validate(input: "email@domain.web").isValid, ".web is not a valid top-level domain") | ||
// XCTAssertFalse(validator.validate(input: "email@111.222.333.44444").isValid, "Invalid IP format") | ||
|
||
// These cases are currently considered valid, but they should not be (test cases from ChatGPT) | ||
// XCTAssertFalse(validator.validate(input: "john@doe@example.com").isValid, "Email with multiple @ symbols") | ||
// XCTAssertFalse(validator.validate(input: "john doe@example.com").isValid, "Email with a space character") | ||
// XCTAssertFalse(validator.validate(input: "john..doe@example.com").isValid, "Email with consecutive dots in the local part") | ||
} | ||
} |