diff --git a/documentation/Arbitraries.md b/documentation/Arbitraries.md index abbda42fc39..af13a31886b 100644 --- a/documentation/Arbitraries.md +++ b/documentation/Arbitraries.md @@ -76,6 +76,7 @@ More specific strings: - `fc.webQueryParameters()` Query parameters to build an URI. Fragment is the optional part right after the ? in an URI - `fc.webSegment()` Web URL path segment - `fc.webUrl()` Web URL following the specs specified by RFC 3986 and WHATWG URL Standard +- `fc.emailAddress()` Email address following RFC 1123 and RFC 5322 ## Combinors of arbitraries (:T) diff --git a/src/check/arbitrary/EmailArbitrary.ts b/src/check/arbitrary/EmailArbitrary.ts new file mode 100644 index 00000000000..416c64f7d6d --- /dev/null +++ b/src/check/arbitrary/EmailArbitrary.ts @@ -0,0 +1,17 @@ +import { array } from './ArrayArbitrary'; +import { buildLowerAlphaNumericArb } from './helpers/SpecificCharacterRange'; +import { domain } from './HostArbitrary'; +import { stringOf } from './StringArbitrary'; +import { tuple } from './TupleArbitrary'; + +/** + * For email address + * + * According to RFC 5322 - https://www.ietf.org/rfc/rfc5322.txt + */ +export function emailAddress() { + const others = ['!', '#', '$', '%', '&', "'", '*', '+', '-', '/', '=', '?', '^', '_', '`', '{', '|', '}', '~']; + const atextArb = buildLowerAlphaNumericArb(others); + const dotAtomArb = array(stringOf(atextArb, 1, 10), 1, 5).map(a => a.join('.')); + return tuple(dotAtomArb, domain()).map(([lp, d]) => `${lp}@${d}`); +} diff --git a/src/fast-check-default.ts b/src/fast-check-default.ts index 7b516786e3f..6db274494c4 100644 --- a/src/fast-check-default.ts +++ b/src/fast-check-default.ts @@ -16,6 +16,7 @@ import { dedup } from './check/arbitrary/DedupArbitrary'; import { Arbitrary } from './check/arbitrary/definition/Arbitrary'; import { Shrinkable } from './check/arbitrary/definition/Shrinkable'; import { dictionary } from './check/arbitrary/DictionaryArbitrary'; +import { emailAddress } from './check/arbitrary/EmailArbitrary'; import { double, float } from './check/arbitrary/FloatingPointArbitrary'; import { frequency } from './check/arbitrary/FrequencyArbitrary'; import { compareBooleanFunc, compareFunc, func } from './check/arbitrary/FunctionArbitrary'; @@ -157,6 +158,7 @@ export { webFragments, webQueryParameters, webUrl, + emailAddress, // model-based AsyncCommand, Command, diff --git a/test/unit/check/arbitrary/EmailArbitrary.spec.ts b/test/unit/check/arbitrary/EmailArbitrary.spec.ts new file mode 100644 index 00000000000..4459002d477 --- /dev/null +++ b/test/unit/check/arbitrary/EmailArbitrary.spec.ts @@ -0,0 +1,30 @@ +import { emailAddress } from '../../../../src/check/arbitrary/EmailArbitrary'; + +import * as genericHelper from './generic/GenericArbitraryHelper'; + +const isValidEmailRfc1123 = (t: string) => { + // Taken from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address + const rfc1123 = /^[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])?)*$/; + return rfc1123.test(t); +}; + +const isValidEmailRfc5322 = (t: string) => { + // Taken from https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression + const rfc5322 = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return rfc5322.test(t); +}; + +describe('EmailArbitrary', () => { + describe('emailAddress', () => { + describe('RFC 1123', () => { + genericHelper.isValidArbitrary(() => emailAddress(), { + isValidValue: (g: string) => isValidEmailRfc1123(g) + }); + }); + describe('RFC 5322', () => { + genericHelper.isValidArbitrary(() => emailAddress(), { + isValidValue: (g: string) => isValidEmailRfc5322(g) + }); + }); + }); +});