chromium/third_party/google-closure-library/closure/goog/format/emailaddress_test.js

/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.module('goog.format.EmailAddressTest');
goog.setTestOnly();

const EmailAddress = goog.require('goog.format.EmailAddress');
const Format = goog.require('goog.i18n.bidi.Format');
const googArray = goog.require('goog.array');
const testSuite = goog.require('goog.testing.testSuite');
const LRM = Format.LRM;
const RLM = Format.RLM;
const LRE = Format.LRE;
const RLE = Format.RLE;
const PDF = Format.PDF;

function doIsValidTest(testFunc, valid, invalid) {
  googArray.forEach(valid, (str) => {
    assertTrue(`"${str}" should be valid.`, testFunc(str));
  });
  googArray.forEach(invalid, (str) => {
    assertFalse(`"${str}" should be invalid.`, testFunc(str));
  });
}

/**
 * Asserts that parsing the inputString produces a list of email addresses
 * containing the specified address strings, irrespective of their order.
 * @param {string} inputString A raw address list.
 * @param {Array<string>} expectedList The expected results.
 * @param {string=} opt_message An assertion message.
 * @return {string} the resulting email address objects.
 * @suppress {checkTypes} suppression added to enable type checking
 */
function assertParsedList(inputString, expectedList, opt_message) {
  const message = opt_message || 'Should parse address correctly';
  const result = EmailAddress.parseList(inputString);
  assertEquals(
      'Should have correct # of addresses', expectedList.length, result.length);
  for (let i = 0; i < expectedList.length; ++i) {
    assertEquals(message, expectedList[i], result[i].getAddress());
  }
  return result;
}
testSuite({
  testparseList() {
    assertParsedList('', [], 'Failed to parse empty stringy');
    assertParsedList(',,', [], 'Failed to parse string with commas only');

    assertParsedList('<[email protected]>', ['[email protected]']);

    assertParsedList(
        '<[email protected]>,', ['[email protected]'],
        'Failed to parse 1 address with trailing comma');

    assertParsedList(
        '<[email protected]>, ', ['[email protected]'],
        'Failed to parse 1 address with trailing whitespace and comma');

    assertParsedList(
        ',<[email protected]>', ['[email protected]'],
        'Failed to parse 1 address with leading comma');

    assertParsedList(
        ' ,<[email protected]>', ['[email protected]'],
        'Failed to parse 1 address with leading whitespace and comma');

    assertParsedList(
        '<[email protected]>, <[email protected]>', ['[email protected]', '[email protected]'],
        'Failed to parse 2 email addresses');

    assertParsedList(
        '<[email protected]>, <[email protected]>,', ['[email protected]', '[email protected]'],
        'Failed to parse 2 email addresses and trailing comma');

    assertParsedList(
        '<[email protected]>, <[email protected]>, ',
        ['[email protected]', '[email protected]'],
        'Failed to parse 2 email addresses, trailing comma and whitespace');

    assertParsedList(
        'John Doe <[email protected]>; Jane Doe <[email protected]>, ' +
            '<[email protected]>',
        ['[email protected]', '[email protected]', '[email protected]'],
        'Failed to parse addresses with semicolon separator');
  },

  testparseListOpenersAndClosers() {
    assertParsedList(
        '[email protected], "[email protected]", <[email protected]>, ' +
            '([email protected]), [[email protected]]',
        [
          '[email protected]',
          '"[email protected]"',
          '[email protected]',
          '([email protected])',
          '[[email protected]]',
        ],
        'Failed to handle all 5 opener/closer characters');
  },

  testparseListIdn() {
    const idnaddr = 'mailtest@\u4F8B\u3048.\u30C6\u30B9\u30C8';
    assertParsedList(idnaddr, [idnaddr]);
  },

  testparseListWithQuotedSpecialChars() {
    const res = assertParsedList(
        'a\\"b\\"c <[email protected]>,"g\\"h\\"i\\\\" <[email protected]>', ['[email protected]', '[email protected]']);
    assertEquals('Wrong name 0', 'a"b"c', res[0].getName());
    assertEquals('Wrong name 1', 'g"h"i\\', res[1].getName());
  },

  testparseListWithCommaInLocalPart() {
    const res = assertParsedList(
        '"Doe, John" <[email protected]>, <[email protected]>',
        ['[email protected]', '[email protected]']);

    assertEquals('Doe, John', res[0].getName());
    assertEquals('', res[1].getName());
  },

  testparseListWithWhitespaceSeparatedEmails() {
    const res = assertParsedList(
        '[email protected] <[email protected]> [email protected] "G H" <[email protected]> [email protected]',
        ['[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]']);
    assertEquals('G H', res[3].getName());
  },

  testparseListSystemNewlines() {
    // These Windows newlines can be inserted in IE8, or copied-and-pasted from
    // bad data on a Mac, as seen in bug 11081852.
    assertParsedList(
        '[email protected]\r\[email protected]', ['[email protected]', '[email protected]'],
        'Failed to parse Windows newlines');
    assertParsedList(
        '[email protected]\[email protected]', ['[email protected]', '[email protected]'],
        'Failed to parse *nix newlines');
    assertParsedList(
        '[email protected]\n\[email protected]', ['[email protected]', '[email protected]'],
        'Failed to parse obsolete newlines');
    assertParsedList(
        '[email protected]\[email protected]', ['[email protected]', '[email protected]'],
        'Failed to parse pre-OS X Mac newlines');
  },

  testparseListBidiMarks() {
    // These bidi marks can be copy pasted from an RTL formatted email
    assertParsedList(
        `ab ${LRE}${PDF}${RLM}<${LRE}[email protected]${PDF}${RLM}>` +
            `${PDF}${RLM},[email protected]${PDF}${RLM} `,
        ['[email protected]', '[email protected]']);
    assertParsedList(
        `${PDF}ab ${RLE}${PDF}${LRM}<${RLE}[email protected]${PDF}${LRM}>` +
            `${PDF}${LRM},[email protected]${PDF}${LRM} `,
        ['[email protected]', '[email protected]']);
  },

  testToString() {
    const f = (str) => EmailAddress.parse(str).toString();

    // No modification.
    assertEquals('JOHN Doe <[email protected]>', f('JOHN Doe <[email protected]>'));

    // Extra spaces.
    assertEquals(
        'JOHN Doe <[email protected]>', f(' JOHN  Doe  <[email protected]> '));

    // No name.
    assertEquals('[email protected]', f('<[email protected]>'));
    assertEquals('[email protected]', f('[email protected]'));

    // No address.
    assertEquals('JOHN Doe', f('JOHN Doe <>'));

    // Special chars in the name.
    assertEquals(
        '"JOHN, Doe" <[email protected]>', f('JOHN, Doe <[email protected]>'));
    assertEquals(
        '"JOHN(Johnny) Doe" <[email protected]>',
        f('JOHN(Johnny) Doe <[email protected]>'));
    assertEquals(
        '"JOHN[Johnny] Doe" <[email protected]>',
        f('JOHN[Johnny] Doe <[email protected]>'));
    assertEquals(
        '"JOHN@work Doe" <[email protected]>',
        f('JOHN@work Doe <[email protected]>'));
    assertEquals(
        '"JOHN:theking Doe" <[email protected]>',
        f('JOHN:theking Doe <[email protected]>'));
    assertEquals(
        '"JOHN\\\\ Doe" <[email protected]>', f('JOHN\\ Doe <[email protected]>'));
    assertEquals(
        '"JOHN.com Doe" <[email protected]>', f('JOHN.com Doe <[email protected]>'));

    // Already quoted.
    assertEquals(
        '"JOHN, Doe" <[email protected]>', f('"JOHN, Doe" <[email protected]>'));

    // Needless quotes.
    assertEquals('JOHN Doe <[email protected]>', f('"JOHN Doe" <[email protected]>'));
    // Not quoted-string, but has double quotes.
    assertEquals(
        '"JOHN, Doe" <[email protected]>', f('JOHN, "Doe" <[email protected]>'));

    // No special characters other than quotes.
    assertEquals('JOHN Doe <[email protected]>', f('JOHN "Doe" <[email protected]>'));

    // Escaped quotes are also removed.
    assertEquals(
        '"JOHN, Doe" <[email protected]>', f('JOHN, \\"Doe\\" <[email protected]>'));
  },

  testIsValid() {
    const valid = [
      '[email protected]', '<[email protected]>', 'eric <[email protected]>', '"e" <[email protected]>',
      '[email protected]', '[email protected]', '[email protected]', 'o\'[email protected]',
      '[email protected]', '!/#$%&\'*+-=~|`{}?^[email protected]',
      '[email protected]',
      `blah blahson ${LRE}${PDF}${RLM}<${LRE}[email protected]${PDF}${RLM}>`
    ];
    const invalid = [
      'e',
      '',
      'e @c.com',
      'a@b',
      'foo.com',
      '[email protected]',
      'test@gma=il.com',
      'aaa@gmail',
      'has some [email protected]',
      'has@three@[email protected]',
      '@no-local-part.com',
      'み.ん-あ@みんあ.みんあ',
      'みんあ@test.com',
      'test@test.みんあ',
      'test@みんあ.com',
      'fullwidthfullstop@sld' +
          '\uff0e' +
          'tld',
      'ideographicfullstop@sld' +
          '\u3002' +
          'tld',
      'halfwidthideographicfullstop@sld' +
          '\uff61' +
          'tld',
    ];
    doIsValidTest(EmailAddress.isValidAddress, valid, invalid);
  },

  testIsValidLocalPart() {
    const valid = [
      'e',
      'a.b+foo',
      'o\'hara',
      'user+someone',
      '!/#$%&\'*+-=~|`{}?^_',
      'confirm-bhk=modulo.org',
    ];
    const invalid = [
      'A@b@c',
      'a"b(c)d,e:f;g<h>i[j\\k]l',
      'just"not"right',
      'this is"not\\allowed',
      'this\\ still\"not\\\\allowed',
      'has some spaces',
    ];
    doIsValidTest(EmailAddress.isValidLocalPartSpec, valid, invalid);
  },

  testIsValidDomainPart() {
    const valid =
        ['example.com', 'dept.example.org', 'long.domain.with.lots.of.dots'];
    const invalid = [
      '',
      '@has.an.at.sign',
      '..has.leading.dots',
      'gma=il.com',
      'DoesNotHaveADot',
      'sld' +
          '\uff0e' +
          'tld',
      'sld' +
          '\u3002' +
          'tld',
      'sld' +
          '\uff61' +
          'tld',
    ];
    doIsValidTest(EmailAddress.isValidDomainPartSpec, valid, invalid);
  },
});