Repository: icebob/fastest-validator
Pull request #150 (2020-06-09 16:56:39)

Enhancement: empty property added to `url` rule
The `empty` property was added to the `url` rule (#116)

What was done:
- changes on `url.js`
- tests (including the Typescript ones)
- README.md was updated


Patch:
diff --git a/README.md b/README.md
index d76e190..3e3882d 100644
--- a/README.md
+++ b/README.md
@@ -95,8 +95,9 @@ $ npm run bench
   - [`tuple`](#tuple)
     - [Properties](#properties-11)
   - [`url`](#url)
-  - [`uuid`](#uuid)
     - [Properties](#properties-12)
+  - [`uuid`](#uuid)
+    - [Properties](#properties-13)
 - [Custom validator](#custom-validator)
   - [Custom validation for built-in rules](#custom-validation-for-built-in-rules)
 - [Custom error messages (l10n)](#custom-error-messages-l10n)
@@ -984,6 +985,11 @@ v.validate({ url: "https://github.com/icebob" }, schema); // Valid
 v.validate({ url: "www.facebook.com" }, schema); // Fail
 ```
 
+### Properties
+Property | Default  | Description
+-------- | -------- | -----------
+`empty`  | `false`   | If `true`, the validator accepts an empty string `""`.
+
 ## `uuid`
 This is an UUID validator. 
 
diff --git a/index.d.ts b/index.d.ts
index a9ba6fd..ff26c8e 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -425,6 +425,11 @@ declare module "fastest-validator" {
 		 * Name of built-in validator
 		 */
 		type: "url";
+		/**
+		 * If true, the validator accepts an empty string ""
+		 * @default true
+		 */
+		empty?: boolean;
 	}
 
 	/**
diff --git a/lib/messages.js b/lib/messages.js
index 5234463..3c7ed7f 100644
--- a/lib/messages.js
+++ b/lib/messages.js
@@ -66,6 +66,7 @@ module.exports = {
 	objectMaxProps: "The object '{field}' must contain {expected} properties at most.",
 
 	url: "The '{field}' field must be a valid URL.",
+	urlEmpty: "The '{field}' field must not be empty.",
 
 	uuid: "The '{field}' field must be a valid UUID.",
 	uuidVersion: "The '{field}' field must be a valid UUID version provided.",
diff --git a/lib/rules/url.js b/lib/rules/url.js
index 4329907..80d0f8c 100644
--- a/lib/rules/url.js
+++ b/lib/rules/url.js
@@ -6,22 +6,37 @@ const PATTERN = /^https?:\/\/\S+/;
 
 /**	Signature: function(value, field, parent, errors, context)
  */
-module.exports = function({ schema, messages }, path, context) {
+module.exports = function ({ schema, messages }, path, context) {
 	const src = [];
+
 	src.push(`
 		if (typeof value !== "string") {
-			${this.makeError({ type: "string",  actual: "value", messages })}
+			${this.makeError({ type: "string", actual: "value", messages })}
 			return value;
 		}
+	`);
 
+	if (!schema.empty) {
+		src.push(`
+			if (value.length === 0) {
+				return ${this.makeError({ type: "urlEmpty", actual: "value", messages })}
+			}
+		`);
+	} else {
+		src.push(`
+			if (value.length === 0) return value;
+		`);
+	}
+
+	src.push(`
 		if (!${PATTERN.toString()}.test(value)) {
-			${this.makeError({ type: "url",  actual: "value", messages })}
+			${this.makeError({ type: "url", actual: "value", messages })}
 		}
 
 		return value;
 	`);
 
 	return {
-		source: src.join("\n")
+		source: src.join("\n"),
 	};
 };


Test patch:
diff --git a/test/messages.spec.js b/test/messages.spec.js
index f25f3aa..4f1e96a 100644
--- a/test/messages.spec.js
+++ b/test/messages.spec.js
@@ -43,6 +43,7 @@ describe("Test Messages", () => {
 		expect(msg.forbidden).toBeDefined();
 		expect(msg.email).toBeDefined();
 		expect(msg.url).toBeDefined();
+		expect(msg.urlEmpty).toBeDefined();
 		expect(msg.enumValue).toBeDefined();
 		expect(msg.equalValue).toBeDefined();
 		expect(msg.equalField).toBeDefined();
diff --git a/test/rules/url.spec.js b/test/rules/url.spec.js
index e4305e6..51648e8 100644
--- a/test/rules/url.spec.js
+++ b/test/rules/url.spec.js
@@ -4,6 +4,12 @@ const Validator = require("../../lib/validator");
 const v = new Validator();
 
 describe("Test rule: url", () => {
+	it("should check empty values", () => {
+		const check = v.compile({ $$root: true, type: "url", empty: true });
+
+		expect(check("https://google.com")).toEqual(true);
+		expect(check("")).toEqual(true);
+	});
 
 	it("should check values", () => {
 		const check = v.compile({ $$root: true, type: "url" });
@@ -17,7 +23,7 @@ describe("Test rule: url", () => {
 		expect(check(true)).toEqual([{ type: "string", actual: true, message }]);
 
 		message = "The '' field must be a valid URL.";
-		expect(check("")).toEqual([{ type: "url", actual: "", message }]);
+		expect(check("")).toEqual([{ type: "urlEmpty", actual: "", message: "The '' field must not be empty." }]);
 		expect(check("true")).toEqual([{ type: "url", actual: "true", message }]);
 		expect(check("abcdefg")).toEqual([{ type: "url", actual: "abcdefg", message }]);
 		expect(check("1234.c")).toEqual([{ type: "url", actual: "1234.c", message }]);
diff --git a/test/typescript/rules/url.spec.ts b/test/typescript/rules/url.spec.ts
index 8382283..e23b41e 100644
--- a/test/typescript/rules/url.spec.ts
+++ b/test/typescript/rules/url.spec.ts
@@ -1,14 +1,20 @@
 /// <reference path="../../../index.d.ts" /> // here we make a reference to exists module definition
-import ValidatorType from 'fastest-validator'; // here we importing type definition of default export
+import ValidatorType, {RuleURL} from 'fastest-validator'; // here we importing type definition of default export
 
 const Validator: typeof ValidatorType = require('../../../index'); // here we importing real Validator Constructor
 const v: ValidatorType = new Validator();
 
 describe('TypeScript Definitions', () => {
     describe('Test rule: url', () => {
+    	it("should check empty values", () => {
+			const check = v.compile({ $$root: true, type: "url", empty: true } as RuleURL);
+
+			expect(check("https://google.com")).toEqual(true);
+			expect(check("")).toEqual(true);
+		});
 
         it('should check values', () => {
-            const check = v.compile({ $$root: true, type: 'url' });
+            const check = v.compile({ $$root: true, type: 'url' } as RuleURL);
             let message = 'The \'\' field must be a string.';
 
             expect(check(0)).toEqual([{ type: 'string', actual: 0, message }]);
@@ -19,7 +25,7 @@ describe('TypeScript Definitions', () => {
             expect(check(true)).toEqual([{ type: 'string', actual: true, message }]);
 
             message = 'The \'\' field must be a valid URL.';
-            expect(check('')).toEqual([{ type: 'url', actual: '', message }]);
+            expect(check('')).toEqual([{ type: "urlEmpty", actual: "", message: "The '' field must not be empty." }]);
             expect(check('true')).toEqual([{ type: 'url', actual: 'true', message }]);
             expect(check('abcdefg')).toEqual([{ type: 'url', actual: 'abcdefg', message }]);
             expect(check('1234.c')).toEqual([{ type: 'url', actual: '1234.c', message }]);