-
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.
- Loading branch information
Showing
4 changed files
with
113 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,40 @@ | ||
// | ||
// Validator+Email.swift | ||
// ValidationKit | ||
// | ||
// Created by Mathijs Bernson on 25/05/2023. | ||
// | ||
|
||
import Foundation | ||
|
||
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: #"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"#, 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,71 @@ | ||
import XCTest | ||
import ValidationKit | ||
|
||
class EmailTests: XCTestCase { | ||
func testValidEmailAddresses() { | ||
let validator = Validator<String, String>.email | ||
|
||
// Test cases from: https://www.softwaretestingo.com/test-cases-for-email-field/ | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Valid email") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "The email contains a dot in the address field") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "The email contains a dot with a subdomain") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Plus sign is considered a valid character") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "The domain is a valid IP address") | ||
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") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Digits in the address are valid") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Dash in the domain name is valid") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Underscore in the address field is valid") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, ".name is a valid Top Level Domain name") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Dot in Top Level Domain name also considered valid (use co.jp as an example here)") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Dash in the address field is valid") | ||
|
||
// Test cases from ChatGPT | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Standard email format") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a subdomain") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with numbers in the domain name") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with special characters in the local part") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a two-letter top-level domain (TLD)") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a hyphen in the domain name") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a single-letter local part") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a long local part and domain name") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a dot at the beginning of the local part") | ||
XCTAssertTrue(validator.validate(input: "[email protected]").isValid, "Email with a dot at the end of the local part") | ||
} | ||
|
||
func testInvalidEmailAddresses() { | ||
let validator = Validator<String, String>.email | ||
|
||
XCTAssertFalse(validator.validate(input: "@#@@##@%^%#$@#$@#.com").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: "Joe Smith <[email protected]>").isValid, "Encoded HTML within an email is invalid") | ||
XCTAssertFalse(validator.validate(input: "email.domain.com").isValid, "Missing @") | ||
XCTAssertFalse(validator.validate(input: "email@[email protected]").isValid, "Two @ sign") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, "The leading dot in the address is not allowed") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, "Trailing dot in address is not allowed") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, "Multiple dots") | ||
XCTAssertFalse(validator.validate(input: "あいうえお@domain.com").isValid, "Unicode char as address") | ||
XCTAssertFalse(validator.validate(input: "[email protected] (Joe Smith)").isValid, "Text followed email is not allowed") | ||
XCTAssertFalse(validator.validate(input: "email@domain").isValid, "Missing top-level domain (.com/.net/.org/etc.)") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, "The leading dash in front of the domain is invalid") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, ".web is not a valid top-level domain") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").isValid, "Invalid IP format") | ||
XCTAssertFalse(validator.validate(input: "[email protected]").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: "[email protected]").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@[email protected]").isValid, "Email with multiple @ symbols") | ||
XCTAssertFalse(validator.validate(input: "john [email protected]").isValid, "Email with a space character") | ||
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: "[email protected]").isValid, "Email with consecutive dots in the 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") | ||
} | ||
} |