Skip to content

Instantly share code, notes, and snippets.

@dreamyguy
Last active September 19, 2019 04:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dreamyguy/098f82d3fc5bc33d6d00607734fa06bd to your computer and use it in GitHub Desktop.
Save dreamyguy/098f82d3fc5bc33d6d00607734fa06bd to your computer and use it in GitHub Desktop.
Email validation
/*
* Email validator
* By Wallace Sidhrée - @dreamyguy
*/
const validateEmail = value => {
if (value) {
// Emails longer than 256 chars aren't valid
if (value.length > 256) {
return false;
}
// For regex explanation: https://gist.github.com/dreamyguy/098f82d3fc5bc33d6d00607734fa06bd
const regex = /^[a-z0-9_-]((?!.*(\.\.))[a-z0-9._%*+&-]+)*@[a-z0-9_]((?!.*(\.\.))[a-z0-9._-]+)*[a-z0-9_](\.[a-z]{2,7})+$/i;
return regex.test(value);
}
return false;
}
import {validateEmail} from './validateEmail';
// Assumes use of 'Jest'
describe('Email', () => {
it('validates correct email address', () => {
const value = 'that.guy@gmail.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('invalidates incorrect email address', () => {
const value = 'that.guy';
const result = validateEmail(value);
expect(result).toEqual(false);
});
describe('reported customer service user-cases:', () => {
it('that.guy@foobar.mobi', () => {
const value = 'that.guy@foobar.mobi';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('that.guy@foobar.info', () => {
const value = 'that.guy@foobar.info';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('that.guy@foobar.name', () => {
const value = 'that.guy@foobar.name';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('that.guy-@foobar.mobi', () => {
const value = 'that.guy-@foobar.mobi';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edvard@munch.museum', () => {
const value = 'edvard@munch.museum';
const result = validateEmail(value);
expect(result).toEqual(true);
});
});
describe('edge user-cases (valid):', () => {
it('edge-case--@foobar.com', () => {
const value = 'edge-case--@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('-edge-case@foobar.com', () => {
const value = '-edge-case@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('_edge-case@foobar.com', () => {
const value = '_edge-case@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case_@foobar.com', () => {
const value = 'edge-case_@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('eDgE-cAsE@fOobAr.cOm', () => {
const value = 'eDgE-cAsE@fOobAr.cOm';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case@foo-bar.com', () => {
const value = 'edge-case@foo-bar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case@foo_bar.com', () => {
const value = 'edge-case@foo_bar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case.@foobar.com', () => {
const value = 'edge-case.@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case@_foobar.com', () => {
const value = 'edge-case@_foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case@foobar_.com', () => {
const value = 'edge-case@foobar_.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('e.d.g.e.c.a.s.e@foobar.com', () => {
const value = 'e.d.g.e.c.a.s.e@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('edge-case@foo.bar.com', () => {
const value = 'edge-case@foo.bar.com';
const result = validateEmail(value);
expect(result).toEqual(true);
});
it('e@foo.bar.com', () => {
const value = 'e@foo.bar.com';
const result = util.validEmail(value);
expect(result).toEqual(undefined);
});
it('edge-case@fo.com', () => {
const value = 'edge-case@fo.com';
const result = util.validEmail(value);
expect(result).toEqual(undefined);
});
it('longest valid email length (256)', () => {
const value = 'if-you-just-knew-how-very-long-this-email-address-is-you-would-not-finish-this-sentence-but-again-its-all-a-matter-of-patience-and-if-you-keep-it-up-you-might-as-well-finish-reading-this-without-me-thinking-youre-kind-of-crazy-no-no-just-kidding@hahahah.no';
const result = validateEmail(value);
expect(result).toEqual(true);
});
});
describe('edge user-cases (invalid):', () => {
it('invalid email length by one character too long (257)', () => {
const value = 'if-you-just-knew-how-very-long-this-email-address-is-you-would-not-finish-this-sentence-but-again-its-all-a-matter-of-patience-and-if-you-keep-it-up-you-might-as-well-finish-reading-this-without-me-thinking-youre-kind-of-crazy-no-no-just-kidding@hahahaha.no';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('.edge-case@foobar.com', () => {
const value = '.edge-case@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge..case@foobar.com', () => {
const value = 'edge..case@foobar.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge.case@foo..bar.com', () => {
const value = 'edge.case@foo..bar.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge-case@foobar..com', () => {
const value = 'edge-case@foobar..com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge-case@-foobar.com', () => {
const value = 'edge-case@-foobar.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge-case@foobar-.com', () => {
const value = 'edge-case@foobar-.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge-case@.foobar.com', () => {
const value = 'edge-case@.foobar.com';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('.@foobar.com', () => {
const value = '.@foobar.com';
const result = util.validEmail(value);
expect(result).toEqual(errorMsg);
});
it('edge-case@f.com', () => {
const value = 'edge-case@f.com';
const result = util.validEmail(value);
expect(result).toEqual(errorMsg);
});
it('edge-case@foobar.c2m', () => {
const value = 'edge-case@foobar.c2m';
const result = validateEmail(value);
expect(result).toEqual(false);
});
it('edge-case@foobar.101', () => {
const value = 'edge-case@foobar.101';
const result = validateEmail(value);
expect(result).toEqual(false);
});
});
});

The regex, explained

This regex is far from perfect, and does not attempt to cover all possible cases. But it offers basic validation that covers many real user cases. Not all possibilities were covered with tests, but the usual suspects are represented.

/^[a-z0-9_-]((?!.*(\.\.))[a-z0-9._%*+&-]+)*@[a-z0-9_]((?!.*(\.\.))[a-z0-9._-]+)*[a-z0-9_](\.[a-z]{2,7})+$/i;
  • / (first and last)
    • beginning and end of expression.
  • ^
    • Beginning of line.
  • [a-z0-9_-]
    • One character (in this case, the first character of the local).
    • All alphanumeric characters and underscore (equivalent to \w), but including -.
  • ((?!.*(\.\.))[a-z0-9._%*+&-]+)*
    • (?!.*(\.\.))
      • A "look ahead" that will look for all ocurrences of double dots in the next pattern, in this case [a-z0-9._%*+&-]+.
    • [a-z0-9._%*+&-]+
      • All alphanumeric characters and underscore (equivalent to \w), plus ., _, %, *, +, & and -. The + at the end repeats the pattern within the [].
    • ()*
      • The pattern within this group may or may not be met
  • @
    • One ocurrence, that separates the local from the domain.
  • [a-z0-9_]
    • One character (in this case, the first character of the domain).
    • All alphanumeric characters and underscore (equivalent to \w).
  • ((?!.*(\.\.))[a-z0-9._-]+)*
    • (?!.*(\.\.))
      • A "look ahead" that will look for all ocurrences of double dots in the next pattern, in this case [a-z0-9._-]+.
    • [a-z0-9._-]+
      • All alphanumeric characters and underscore (equivalent to \w), plus . and -. The + at the end repeats the pattern within the [].
    • ()*
      • The pattern within this group may or may not be met
  • [a-z0-9_]
    • One character (in this case, the last character of the domain).
    • All alphanumeric characters and underscore (equivalent to \w).
  • (\.[a-z]{2,7})+
    • The domain extension
    • \. The dot
    • [a-z] All alpha characters, following the dot.
    • {2,7} Minimum of 2 characters, maximum of 7.
    • () The pattern's group
    • + The pattern within the group may repeat
  • $
    • End of line.
  • i
    • A flag that sets the whole expression to be case insensitive.
    • Flags are placed at the end of the whole expression, after the last /.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment