From 94d5a50abac379b5009ef3aae97d8b803af35379 Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Sun, 16 Jun 2024 14:11:15 +0000 Subject: [PATCH 1/6] Add timeout option for MX DNS record lookup Add optional second parameter `opts`, an object that can contain the properties `timeout` and `checkMx`. The `checkMx` property of the `opts` object overrides the `checkMx` function parameter. The `timeout` property, in `ms` module format, specifies the timeout for the DNS MX record lookup, the default is 10 seconds. Add test, example and update the API documentation. Signed-off-by: Rafael Kitover --- README.md | 24 ++++++++++++++++++++++-- package.json | 3 ++- src/index.js | 42 +++++++++++++++++++++++++++++++++++++++--- test/index.test.js | 4 ++++ 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 934b6f2..940f2b5 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,20 @@ async function validateEmailWithMx(email) { } } +// Example with MX record checking with timeout +async function validateEmailWithMx(email) { + try { + const isValid = await emailValidator(email, { timeout: '500ms' }); + console.log(`Is "${email}" a valid email address with MX checking?`, isValid); + } catch (error) { + if (error.message.match(/timed out/) { + console.error('Timeout on DNS MX lookup.'); + } else { + console.error('Error validating email with MX checking:', error); + } + } +} + // Example without MX record checking async function validateEmailWithoutMx(email) { try { @@ -54,14 +68,20 @@ validateEmailWithoutMx('test@example.com').then(); ## API -### ```async emailValidator(email, checkMx = true)``` +### ```async emailValidator(email, [opts], checkMx = true)``` Validates the given email address, with an option to skip MX record verification. #### Parameters - ```email``` (string): The email address to validate. -- ```checkMx``` (boolean): Whether to check for MX records, this defaults to true. +- ```opts``` (object): Optional configuration options. + - ```timeout``` (number): The timeout in + [ms](https://www.npmjs.com/package/ms) module format, e.g. ```500ms``` for the + the DNS MX lookup, the default is 10 seconds. + - ```checkMx``` (boolean): Whether to check for MX records, overrides parameter. +- ```checkMx``` (boolean): Whether to check for MX records, this defaults to + true, overridden by opts. #### Returns diff --git a/package.json b/package.json index bad1fe2..25fa5a6 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "jest": "^29.7.0" }, "dependencies": { - "validator": "^13.11.0" + "validator": "^13.11.0", + "ms": "^2.1.3" } } diff --git a/src/index.js b/src/index.js index 7a02f4b..a11873f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,8 @@ import dns from 'dns'; import util from 'util'; import validator from 'validator'; +import ms from 'ms'; +import { setTimeout } from 'timers/promises'; // Convert the callback-based dns.resolveMx function into a promise-based one const resolveMx = util.promisify(dns.resolveMx); @@ -39,16 +41,50 @@ const checkMxRecords = async (email) => { * checkMx parameter. * * @param {string} email - The email address to validate. + * @param {object} opts - An object containing options for the validator, + * curently supported options are: + * - checkMx: boolean - Determines whether to check for MX records. Defaults to + * true. This option overrides the checkMx parameter. + * - timeout: number - The time in ms module format, such as '2000ms' or '10s', + * after which the MX validation will be aborted. The default timeout is 10 + * seconds. * @param {boolean} checkMx - Determines whether to check for MX records. * Defaults to true. * @return {Promise} - Promise that resolves to true if the email is * valid, false otherwise. */ -const emailValidator = async (email, checkMx = true) => { +async function emailValidator(email, opts, checkMx) { + if (arguments.length === 2 && typeof opts === 'boolean') { + checkMx = opts; + } + else if (arguments.length < 3) { + checkMx = true; + } + + opts ||= {}; + + if (!('checkMx' in opts)) { + opts.checkMx = checkMx; + } + + if (!('timeout' in opts)) { + opts.timeout = '10s'; + } + opts.timeout = ms(opts.timeout); + if (!validateRfc5322(email)) return false; - if (checkMx) { - const hasMxRecords = await checkMxRecords(email); + if (opts.checkMx) { + let timeoutController = new AbortController(); + let timeout = setTimeout(opts.timeout, undefined, { signal: timeoutController.signal }).then(() => { + throw new Error('Domain MX lookup timed out'); + }); + let hasMxRecords = false; + let lookupMx = checkMxRecords(email).then((res) => { + hasMxRecords = res; + timeoutController.abort(); + }); + await Promise.race([lookupMx, timeout]); if (!hasMxRecords) return false; } diff --git a/test/index.test.js b/test/index.test.js index d565224..f46c518 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,6 +10,10 @@ describe('Email Validator', () => { expect(await emailValidator('test@adafwefewsd.com')).toBe(false); }); + test('should timeout MX record check', async () => { + expect(async () => { await emailValidator('test@example.com', { timeout: '1ms' }) }).rejects.toThrow(/timed out/); + }); + test('should reject non-string inputs', async () => { expect(await emailValidator(undefined)).toBe(false); expect(await emailValidator(null)).toBe(false); From f91b88926217b1b8c3a754288deef191130b8b7c Mon Sep 17 00:00:00 2001 From: Jesse Palmer Date: Sat, 27 Jul 2024 15:22:30 -0700 Subject: [PATCH 2/6] test: add additional tests --- test/index.test.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/index.test.js b/test/index.test.js index f46c518..34c1b64 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -20,6 +20,14 @@ describe('Email Validator', () => { expect(await emailValidator(1234)).toBe(false); expect(await emailValidator({})).toBe(false); }); + + test('should reject email with invalid domain format', async () => { + expect(await emailValidator('test@invalid-domain')).toBe(false); + }); + + test('should reject email with special characters in domain', async () => { + expect(await emailValidator('test@exam$ple.com')).toBe(false); + }); }); describe('without MX record check', () => { @@ -41,5 +49,29 @@ describe('Email Validator', () => { expect(await emailValidator(1234, false)).toBe(false); expect(await emailValidator({}, false)).toBe(false); }); + + test('should reject email with spaces', async () => { + expect(await emailValidator('test @example.com', false)).toBe(false); + }); + + test('should reject email with double dots in domain', async () => { + expect(await emailValidator('test@exa..mple.com', false)).toBe(false); + }); + + test('should validate email with single character local part', async () => { + expect(await emailValidator('t@example.com', false)).toBe(true); + }); + + test('should validate email with numeric local part', async () => { + expect(await emailValidator('12345@example.com', false)).toBe(true); + }); + + test('should validate email with hyphen in domain', async () => { + expect(await emailValidator('test@exam-ple.com', false)).toBe(true); + }); + + test('should reject email with underscore in domain', async () => { + expect(await emailValidator('test@exam_ple.com', false)).toBe(false); + }); }); }); From 8a826afb05de70e972b97e570e7c98e209a56ace Mon Sep 17 00:00:00 2001 From: Jesse Palmer Date: Sat, 27 Jul 2024 15:42:30 -0700 Subject: [PATCH 3/6] add additional tests --- test/index.test.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/index.test.js b/test/index.test.js index 34c1b64..1ea83cd 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,10 +10,6 @@ describe('Email Validator', () => { expect(await emailValidator('test@adafwefewsd.com')).toBe(false); }); - test('should timeout MX record check', async () => { - expect(async () => { await emailValidator('test@example.com', { timeout: '1ms' }) }).rejects.toThrow(/timed out/); - }); - test('should reject non-string inputs', async () => { expect(await emailValidator(undefined)).toBe(false); expect(await emailValidator(null)).toBe(false); @@ -28,6 +24,26 @@ describe('Email Validator', () => { test('should reject email with special characters in domain', async () => { expect(await emailValidator('test@exam$ple.com')).toBe(false); }); + + test('should reject email with spaces', async () => { + expect(await emailValidator('test @example.com')).toBe(false); + }); + + test('should reject email with double dots in domain', async () => { + expect(await emailValidator('test@exa..mple.com')).toBe(false); + }); + + test('should validate email with numeric local part', async () => { + expect(await emailValidator('12345@example.com')).toBe(true); + }); + + test('should validate email with hyphen in domain', async () => { + expect(await emailValidator('test@exam-ple.com')).toBe(true); + }); + + test('should reject email with underscore in domain', async () => { + expect(await emailValidator('test@exam_ple.com')).toBe(false); + }); }); describe('without MX record check', () => { @@ -58,10 +74,6 @@ describe('Email Validator', () => { expect(await emailValidator('test@exa..mple.com', false)).toBe(false); }); - test('should validate email with single character local part', async () => { - expect(await emailValidator('t@example.com', false)).toBe(true); - }); - test('should validate email with numeric local part', async () => { expect(await emailValidator('12345@example.com', false)).toBe(true); }); From aec07a5de20120bf9e272f5301fc9acddfc22629 Mon Sep 17 00:00:00 2001 From: Jesse Palmer Date: Sat, 27 Jul 2024 16:29:17 -0700 Subject: [PATCH 4/6] feat: update params --- src/index.js | 63 +++++++++++++++++++-------------------------- test/index.test.js | 64 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/index.js b/src/index.js index a11873f..2ae4106 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,6 @@ import dns from 'dns'; import util from 'util'; import validator from 'validator'; -import ms from 'ms'; import { setTimeout } from 'timers/promises'; // Convert the callback-based dns.resolveMx function into a promise-based one @@ -38,53 +37,43 @@ const checkMxRecords = async (email) => { /** * A sophisticated email validator that checks both the format of the email * address and the existence of MX records for the domain, depending on the - * checkMx parameter. + * options provided. * * @param {string} email - The email address to validate. - * @param {object} opts - An object containing options for the validator, - * curently supported options are: - * - checkMx: boolean - Determines whether to check for MX records. Defaults to - * true. This option overrides the checkMx parameter. - * - timeout: number - The time in ms module format, such as '2000ms' or '10s', - * after which the MX validation will be aborted. The default timeout is 10 - * seconds. - * @param {boolean} checkMx - Determines whether to check for MX records. - * Defaults to true. - * @return {Promise} - Promise that resolves to true if the email is + * @param {object} [opts={}] - An object containing options for the validator. + * @param {boolean} [opts.checkMx=true] - Determines whether to check for MX + * records. + * @param {number} [opts.timeout=10000] - The time in milliseconds after which + * the MX validation will be aborted. The default timeout is 10 seconds. + * @return {Promise} - Promise that resolves to true if the email is * valid, false otherwise. */ -async function emailValidator(email, opts, checkMx) { - if (arguments.length === 2 && typeof opts === 'boolean') { - checkMx = opts; - } - else if (arguments.length < 3) { - checkMx = true; - } - - opts ||= {}; - - if (!('checkMx' in opts)) { - opts.checkMx = checkMx; +const emailValidator = async (email, opts = {}) => { + // Handle the case where opts is a boolean for backward compatibility + if (typeof opts === 'boolean') { + opts = { checkMx: opts }; } - if (!('timeout' in opts)) { - opts.timeout = '10s'; - } - opts.timeout = ms(opts.timeout); + // Set default values for opts if not provided + const { checkMx = true, timeout = 10000 } = opts; + // Validate the email format if (!validateRfc5322(email)) return false; - if (opts.checkMx) { - let timeoutController = new AbortController(); - let timeout = setTimeout(opts.timeout, undefined, { signal: timeoutController.signal }).then(() => { - throw new Error('Domain MX lookup timed out'); - }); - let hasMxRecords = false; - let lookupMx = checkMxRecords(email).then((res) => { - hasMxRecords = res; + // Check MX records if required + if (checkMx) { + const timeoutController = new AbortController(); + const timeoutPromise = setTimeout(timeout, undefined, { signal: timeoutController.signal }) + .then(() => { + throw new Error('Domain MX lookup timed out'); + }); + + const lookupMx = checkMxRecords(email).then((hasMxRecords) => { timeoutController.abort(); + return hasMxRecords; }); - await Promise.race([lookupMx, timeout]); + + const hasMxRecords = await Promise.race([lookupMx, timeoutPromise]); if (!hasMxRecords) return false; } diff --git a/test/index.test.js b/test/index.test.js index 1ea83cd..ae7f3f9 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,6 +10,10 @@ describe('Email Validator', () => { expect(await emailValidator('test@adafwefewsd.com')).toBe(false); }); + test('should timeout MX record check', async () => { + await expect(emailValidator('test@example.com', { timeout: 1 })).rejects.toThrow(/timed out/); + }); + test('should reject non-string inputs', async () => { expect(await emailValidator(undefined)).toBe(false); expect(await emailValidator(null)).toBe(false); @@ -48,42 +52,78 @@ describe('Email Validator', () => { describe('without MX record check', () => { test('should validate correct email format regardless of MX records', async () => { - expect(await emailValidator('test@example.com', false)).toBe(true); + expect(await emailValidator('test@example.com', { checkMx: false })).toBe(true); }); test('should reject incorrect email format regardless of MX records', async () => { - expect(await emailValidator('invalid-email', false)).toBe(false); + expect(await emailValidator('invalid-email', { checkMx: false })).toBe(false); }); test('should validate email from domain without MX records', async () => { - expect(await emailValidator('test@adafwefewsd.com', false)).toBe(true); + expect(await emailValidator('test@adafwefewsd.com', { checkMx: false })).toBe(true); }); test('should reject non-string inputs', async () => { - expect(await emailValidator(undefined, false)).toBe(false); - expect(await emailValidator(null, false)).toBe(false); - expect(await emailValidator(1234, false)).toBe(false); - expect(await emailValidator({}, false)).toBe(false); + expect(await emailValidator(undefined, { checkMx: false })).toBe(false); + expect(await emailValidator(null, { checkMx: false })).toBe(false); + expect(await emailValidator(1234, { checkMx: false })).toBe(false); + expect(await emailValidator({}, { checkMx: false })).toBe(false); }); test('should reject email with spaces', async () => { - expect(await emailValidator('test @example.com', false)).toBe(false); + expect(await emailValidator('test @example.com', { checkMx: false })).toBe(false); }); test('should reject email with double dots in domain', async () => { - expect(await emailValidator('test@exa..mple.com', false)).toBe(false); + expect(await emailValidator('test@exa..mple.com', { checkMx: false })).toBe(false); }); test('should validate email with numeric local part', async () => { - expect(await emailValidator('12345@example.com', false)).toBe(true); + expect(await emailValidator('12345@example.com', { checkMx: false })).toBe(true); }); test('should validate email with hyphen in domain', async () => { - expect(await emailValidator('test@exam-ple.com', false)).toBe(true); + expect(await emailValidator('test@exam-ple.com', { checkMx: false })).toBe(true); }); test('should reject email with underscore in domain', async () => { - expect(await emailValidator('test@exam_ple.com', false)).toBe(false); + expect(await emailValidator('test@exam_ple.com', { checkMx: false })).toBe(false); + }); + }); + + describe('backward compatibility', () => { + test('should validate correct email format and MX record exists with boolean opts', async () => { + expect(await emailValidator('test@example.com', true)).toBe(true); + }); + + test('should validate correct email format without MX record check with boolean opts', async () => { + expect(await emailValidator('test@example.com', false)).toBe(true); + }); + }); + + describe('options parameter', () => { + test('should validate correct email format with checkMx set to true', async () => { + expect(await emailValidator('test@example.com', { checkMx: true })).toBe(true); + }); + + test('should validate correct email format with checkMx set to false', async () => { + expect(await emailValidator('test@example.com', { checkMx: false })).toBe(true); + }); + + test('should timeout with custom timeout setting', async () => { + await expect(emailValidator('test@example.com', { timeout: 1 })).rejects.toThrow(/timed out/); + }); + + test('should validate correct email format with custom timeout setting', async () => { + expect(await emailValidator('test@example.com', { timeout: 5000 })).toBe(true); + }); + + test('should validate correct email format and MX record exists with both options set', async () => { + expect(await emailValidator('test@example.com', { checkMx: true, timeout: 5000 })).toBe(true); + }); + + test('should validate correct email format without MX record check and custom timeout', async () => { + expect(await emailValidator('test@example.com', { checkMx: false, timeout: 5000 })).toBe(true); }); }); }); From 9ed89b5653b9c2580e83ce6f24b3bfe317b01f32 Mon Sep 17 00:00:00 2001 From: Jesse Palmer Date: Sat, 27 Jul 2024 16:29:33 -0700 Subject: [PATCH 5/6] docs: update with new signature --- README.md | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 940f2b5..4b163a6 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,18 @@ # Node Email Verifier Node Email Verifier is an email validation library for Node.js that checks if an -email address has a valid format and optionally verifies the domain's MX (Mail Exchange) -records to ensure it can receive emails. +email address has a valid format and optionally verifies the domain's MX +(Mail Exchange) records to ensure it can receive emails. ## Features -- **RFC 5322 Format Validation**: Validates email addresses against the standard email formatting rules. -- **MX Record Checking**: Verifies that the domain of the email address has valid MX records indicating that it can receive emails. This check can be disabled using a parameter. - +- **RFC 5322 Format Validation**: Validates email addresses against the standard + email formatting rules. +- **MX Record Checking**: Verifies that the domain of the email address has + valid MX records indicating that it can receive emails. This check can be + disabled using a parameter. +- **Customizable Timeout**: Allows setting a custom timeout for MX record + checking. ## Installation @@ -31,20 +35,20 @@ import emailValidator from 'node-email-verifier'; // Example with MX record checking async function validateEmailWithMx(email) { try { - const isValid = await emailValidator(email); + const isValid = await emailValidator(email, { checkMx: true }); console.log(`Is "${email}" a valid email address with MX checking?`, isValid); } catch (error) { console.error('Error validating email with MX checking:', error); } } -// Example with MX record checking with timeout -async function validateEmailWithMx(email) { +// Example with MX record checking and custom timeout +async function validateEmailWithMxTimeout(email) { try { - const isValid = await emailValidator(email, { timeout: '500ms' }); - console.log(`Is "${email}" a valid email address with MX checking?`, isValid); + const isValid = await emailValidator(email, { checkMx: true, timeout: 500 }); + console.log(`Is "${email}" a valid email address with MX checking and custom timeout?`, isValid); } catch (error) { - if (error.message.match(/timed out/) { + if (error.message.match(/timed out/)) { console.error('Timeout on DNS MX lookup.'); } else { console.error('Error validating email with MX checking:', error); @@ -55,7 +59,7 @@ async function validateEmailWithMx(email) { // Example without MX record checking async function validateEmailWithoutMx(email) { try { - const isValid = await emailValidator(email, false); + const isValid = await emailValidator(email, { checkMx: false }); console.log(`Is "${email}" a valid email address without MX checking?`, isValid); } catch (error) { console.error('Error validating email without MX checking:', error); @@ -63,29 +67,29 @@ async function validateEmailWithoutMx(email) { } validateEmailWithMx('test@example.com').then(); +validateEmailWithMxTimeout('test@example.com').then(); validateEmailWithoutMx('test@example.com').then(); ``` ## API -### ```async emailValidator(email, [opts], checkMx = true)``` +### ```async emailValidator(email, [opts])``` -Validates the given email address, with an option to skip MX record verification. +Validates the given email address, with an option to skip MX record verification +and set a custom timeout. #### Parameters - ```email``` (string): The email address to validate. - ```opts``` (object): Optional configuration options. - - ```timeout``` (number): The timeout in - [ms](https://www.npmjs.com/package/ms) module format, e.g. ```500ms``` for the - the DNS MX lookup, the default is 10 seconds. - - ```checkMx``` (boolean): Whether to check for MX records, overrides parameter. -- ```checkMx``` (boolean): Whether to check for MX records, this defaults to - true, overridden by opts. +- ```timeout``` (number): The timeout in milliseconds, e.g., 500 for the DNS MX + lookup. The default is 10 seconds (10000 milliseconds). +- ```checkMx``` (boolean): Whether to check for MX records. This defaults to true. #### Returns -- ```Promise```: A promise that resolves to true if the email address is valid and, if checked, has MX records; false otherwise. +- ```Promise```: A promise that resolves to true if the email address +is valid and, if checked, has MX records; false otherwise. ## Contributing From 246f1648d66b78c70572a18a67f74642f7deecc9 Mon Sep 17 00:00:00 2001 From: Jesse Palmer Date: Sat, 27 Jul 2024 16:43:39 -0700 Subject: [PATCH 6/6] feat: add back ms library --- README.md | 23 ++++++++++++++++++++--- src/index.js | 13 +++++++++---- test/index.test.js | 46 +++++++++++++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 4b163a6..c0200f2 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,20 @@ async function validateEmailWithMx(email) { // Example with MX record checking and custom timeout async function validateEmailWithMxTimeout(email) { + try { + const isValid = await emailValidator(email, { checkMx: true, timeout: '500ms' }); + console.log(`Is "${email}" a valid email address with MX checking and custom timeout?`, isValid); + } catch (error) { + if (error.message.match(/timed out/)) { + console.error('Timeout on DNS MX lookup.'); + } else { + console.error('Error validating email with MX checking:', error); + } + } +} + +// Example with custom timeout as a number +async function validateEmailWithMxTimeoutNumber(email) { try { const isValid = await emailValidator(email, { checkMx: true, timeout: 500 }); console.log(`Is "${email}" a valid email address with MX checking and custom timeout?`, isValid); @@ -68,6 +82,7 @@ async function validateEmailWithoutMx(email) { validateEmailWithMx('test@example.com').then(); validateEmailWithMxTimeout('test@example.com').then(); +validateEmailWithMxTimeoutNumber('test@example.com').then(); validateEmailWithoutMx('test@example.com').then(); ``` @@ -82,9 +97,11 @@ and set a custom timeout. - ```email``` (string): The email address to validate. - ```opts``` (object): Optional configuration options. -- ```timeout``` (number): The timeout in milliseconds, e.g., 500 for the DNS MX - lookup. The default is 10 seconds (10000 milliseconds). -- ```checkMx``` (boolean): Whether to check for MX records. This defaults to true. +- ```timeout``` (string|number): The timeout for the DNS MX lookup, in + milliseconds or ms format (e.g., '2000ms' or '10s'). The default is 10 seconds + ('10s'). +- ```checkMx``` (boolean): Whether to check for MX records. This defaults to + true. #### Returns diff --git a/src/index.js b/src/index.js index 2ae4106..049e982 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import dns from 'dns'; import util from 'util'; import validator from 'validator'; +import ms from 'ms'; import { setTimeout } from 'timers/promises'; // Convert the callback-based dns.resolveMx function into a promise-based one @@ -43,8 +44,9 @@ const checkMxRecords = async (email) => { * @param {object} [opts={}] - An object containing options for the validator. * @param {boolean} [opts.checkMx=true] - Determines whether to check for MX * records. - * @param {number} [opts.timeout=10000] - The time in milliseconds after which - * the MX validation will be aborted. The default timeout is 10 seconds. + * @param {string|number} [opts.timeout='10s'] - The time in ms module format, + * such as '2000ms' or '10s', after which the MX validation will be aborted. + * The default timeout is 10 seconds. * @return {Promise} - Promise that resolves to true if the email is * valid, false otherwise. */ @@ -55,7 +57,10 @@ const emailValidator = async (email, opts = {}) => { } // Set default values for opts if not provided - const { checkMx = true, timeout = 10000 } = opts; + const { checkMx = true, timeout = '10s' } = opts; + + // Convert timeout to milliseconds + const timeoutMs = typeof timeout === 'string' ? ms(timeout) : timeout; // Validate the email format if (!validateRfc5322(email)) return false; @@ -63,7 +68,7 @@ const emailValidator = async (email, opts = {}) => { // Check MX records if required if (checkMx) { const timeoutController = new AbortController(); - const timeoutPromise = setTimeout(timeout, undefined, { signal: timeoutController.signal }) + const timeoutPromise = setTimeout(timeoutMs, undefined, { signal: timeoutController.signal }) .then(() => { throw new Error('Domain MX lookup timed out'); }); diff --git a/test/index.test.js b/test/index.test.js index ae7f3f9..3f744e4 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,7 +10,11 @@ describe('Email Validator', () => { expect(await emailValidator('test@adafwefewsd.com')).toBe(false); }); - test('should timeout MX record check', async () => { + test('should timeout MX record check with string timeout', async () => { + await expect(emailValidator('test@example.com', { timeout: '1ms' })).rejects.toThrow(/timed out/); + }); + + test('should timeout MX record check with number timeout', async () => { await expect(emailValidator('test@example.com', { timeout: 1 })).rejects.toThrow(/timed out/); }); @@ -52,42 +56,42 @@ describe('Email Validator', () => { describe('without MX record check', () => { test('should validate correct email format regardless of MX records', async () => { - expect(await emailValidator('test@example.com', { checkMx: false })).toBe(true); + expect(await emailValidator('test@example.com', false)).toBe(true); }); test('should reject incorrect email format regardless of MX records', async () => { - expect(await emailValidator('invalid-email', { checkMx: false })).toBe(false); + expect(await emailValidator('invalid-email', false)).toBe(false); }); test('should validate email from domain without MX records', async () => { - expect(await emailValidator('test@adafwefewsd.com', { checkMx: false })).toBe(true); + expect(await emailValidator('test@adafwefewsd.com', false)).toBe(true); }); test('should reject non-string inputs', async () => { - expect(await emailValidator(undefined, { checkMx: false })).toBe(false); - expect(await emailValidator(null, { checkMx: false })).toBe(false); - expect(await emailValidator(1234, { checkMx: false })).toBe(false); - expect(await emailValidator({}, { checkMx: false })).toBe(false); + expect(await emailValidator(undefined, false)).toBe(false); + expect(await emailValidator(null, false)).toBe(false); + expect(await emailValidator(1234, false)).toBe(false); + expect(await emailValidator({}, false)).toBe(false); }); test('should reject email with spaces', async () => { - expect(await emailValidator('test @example.com', { checkMx: false })).toBe(false); + expect(await emailValidator('test @example.com', false)).toBe(false); }); test('should reject email with double dots in domain', async () => { - expect(await emailValidator('test@exa..mple.com', { checkMx: false })).toBe(false); + expect(await emailValidator('test@exa..mple.com', false)).toBe(false); }); test('should validate email with numeric local part', async () => { - expect(await emailValidator('12345@example.com', { checkMx: false })).toBe(true); + expect(await emailValidator('12345@example.com', false)).toBe(true); }); test('should validate email with hyphen in domain', async () => { - expect(await emailValidator('test@exam-ple.com', { checkMx: false })).toBe(true); + expect(await emailValidator('test@exam-ple.com', false)).toBe(true); }); test('should reject email with underscore in domain', async () => { - expect(await emailValidator('test@exam_ple.com', { checkMx: false })).toBe(false); + expect(await emailValidator('test@exam_ple.com', false)).toBe(false); }); }); @@ -110,20 +114,28 @@ describe('Email Validator', () => { expect(await emailValidator('test@example.com', { checkMx: false })).toBe(true); }); - test('should timeout with custom timeout setting', async () => { + test('should timeout with custom timeout setting as string', async () => { + await expect(emailValidator('test@example.com', { timeout: '1ms' })).rejects.toThrow(/timed out/); + }); + + test('should timeout with custom timeout setting as number', async () => { await expect(emailValidator('test@example.com', { timeout: 1 })).rejects.toThrow(/timed out/); }); - test('should validate correct email format with custom timeout setting', async () => { + test('should validate correct email format with custom timeout setting as string', async () => { + expect(await emailValidator('test@example.com', { timeout: '5s' })).toBe(true); + }); + + test('should validate correct email format with custom timeout setting as number', async () => { expect(await emailValidator('test@example.com', { timeout: 5000 })).toBe(true); }); test('should validate correct email format and MX record exists with both options set', async () => { - expect(await emailValidator('test@example.com', { checkMx: true, timeout: 5000 })).toBe(true); + expect(await emailValidator('test@example.com', { checkMx: true, timeout: '5s' })).toBe(true); }); test('should validate correct email format without MX record check and custom timeout', async () => { - expect(await emailValidator('test@example.com', { checkMx: false, timeout: 5000 })).toBe(true); + expect(await emailValidator('test@example.com', { checkMx: false, timeout: '5s' })).toBe(true); }); }); });