diff --git a/src/core/validateField.ts b/src/core/validateField.ts index 7cf98d7..00ed4a1 100644 --- a/src/core/validateField.ts +++ b/src/core/validateField.ts @@ -1,3 +1,4 @@ +import { MappingError, handleError } from "../internal/error-management"; import { RulesObject, FieldError, @@ -14,46 +15,102 @@ export function validateField( rulesObject: RulesObject ): Array { const errors: Array = []; + try { + const configMap: ConfigMap = { + minLength: (value: string, criterion: number, message: string) => { + /*--- Error Guards ---*/ - const configMap: ConfigMap = { - minLength: (value: string, criterion: number, message: string) => { - if (!_utils.isString(value)) - throw new Error( - "File value must be a string to be validated with minLength" - ); + if (!_utils.isString(value)) + throw new TypeError("'value' must be a string"); - if (value && value.length > 0 && !(value.length >= criterion)) { - errors.push({ name, message }); - } - }, - maxLength: (value: string, criterion: number, message: string) => { - if (value && !(value.length <= criterion)) { - errors.push({ name, message }); - } - }, - pattern: (value: string, criterion: RegExp, message: string) => { - if (value.length > 0 && !value.match(criterion)) { - errors.push({ name, message }); - } - }, - custom: (value: string, criterion: CustomFunction, message: string) => { - // if the custom function criterion(value) returns true: push an error - if (criterion(value)) { - errors.push({ name, message }); - } - }, - required: (value: string, criterion, message: string) => { - if (value.replace(/\s/g, "") === "" && criterion) { - errors.push({ name, message }); - } - }, - }; + if (!_utils.isNumber(criterion)) + throw new TypeError("'criterion' must be a number"); - function isKeyOfConfigMap(key: string): key is keyof ConfigMap { - return key in configMap; - } + if (!_utils.isString(message)) + throw new TypeError("'message' must be a string"); + + /*--- Functionality ---*/ + + if (value && value.length > 0 && !(value.length >= criterion)) { + errors.push({ name, message }); + } + }, + maxLength: (value: string, criterion: number, message: string) => { + /*--- Error Guards ---*/ + + if (!_utils.isString(value)) + throw new TypeError("'value' must be a string"); + + if (!_utils.isNumber(criterion)) + throw new TypeError("'criterion' must be a number"); + + if (!_utils.isString(message)) + throw new TypeError("'message' must be a string"); + + /*--- Functionality ---*/ + + if (value && !(value.length <= criterion)) { + errors.push({ name, message }); + } + }, + pattern: (value: string, criterion: RegExp, message: string) => { + /*--- Error Guards ---*/ + + if (!_utils.isString(value)) + throw new TypeError("'value' must be a string"); + + // if (!_utils.isRegExp(criterion)) + // throw new TypeError("'criterion' must be a RegExp"); + + if (!_utils.isString(message)) + throw new TypeError("'message' must be a string"); + + if (value.length > 0 && !value.match(criterion)) { + errors.push({ name, message }); + } + }, + custom: (value: string, criterion: CustomFunction, message: string) => { + /*--- Error Guards ---*/ + + if (!_utils.isString(value)) + throw new TypeError("'value' must be a string"); + + // if (!_utils.isFunction(criterion)) + // throw new TypeError("'criterion' must be a function"); + + if (!_utils.isString(message)) + throw new TypeError("'message' must be a string"); + + /*--- Functionality ---*/ + + if (criterion(value)) { + errors.push({ name, message }); + } + }, + required: (value: string, criterion: boolean, message: string) => { + /*--- Error Guards ---*/ + + if (!_utils.isString(value)) + throw new TypeError("'value' must be a string"); + + // if (!_utils.isBoolean(criterion)) + // throw new TypeError("'criterion' must be a boolean"); + + if (!_utils.isString(message)) + throw new TypeError("'message' must be a string"); + + /*--- Functionality ---*/ + + if (value.replace(/\s/g, "") === "" && criterion) { + errors.push({ name, message }); + } + }, + } as const; + + function isKeyOfConfigMap(key: string): key is keyof ConfigMap { + return key in configMap; + } - try { Object.entries(rulesObject).forEach(([key, { criterion, message }]) => { if (isKeyOfConfigMap(key)) { const validationFunction = configMap[ @@ -63,14 +120,18 @@ export function validateField( if (validationFunction) { validationFunction(value, criterion, message); } else { - throw new Error( - `Validation function for ${key} does not exist in configMap` + throw new MappingError( + `No validation function found for ${key} in configMap` ); } } }); - } catch (e) { - console.error(e); + } catch (error) { + if (error instanceof Error) { + handleError(error); + } else { + console.log(error); + } } return errors; diff --git a/src/core/validateForm.ts b/src/core/validateForm.ts index e24335b..9914af1 100644 --- a/src/core/validateForm.ts +++ b/src/core/validateForm.ts @@ -1,11 +1,5 @@ import { doKeysMatch } from "../internal/do-keys-match"; -import { - FieldError, - NameRuleMap, - KeyValuePair, - RulesObject, - FormDataShape, -} from "../types"; +import { FieldError, NameRuleMap, RulesObject, FormDataShape } from "../types"; import _utils from "../utils"; import { validateField } from "./validateField"; diff --git a/src/internal/error-management.ts b/src/internal/error-management.ts index e2970b9..39a4011 100644 --- a/src/internal/error-management.ts +++ b/src/internal/error-management.ts @@ -1,22 +1,24 @@ -// Custom error types -class NetworkError extends Error { - constructor(message: string) { - super(message); - this.name = "NetworkError"; - } -} +type ERROR_NAMES = "TYPE_ERROR" | "MAPPING_ERROR"; -class ValidationError extends Error { - constructor(message: string) { - super(message); - this.name = "ValidationError"; +class MappingError extends Error { + constructor( + message: string = "Validation Error", + name: ERROR_NAMES = "MAPPING_ERROR" + ) { + super(); + this.name = name; + this.message = message; } } -class DatabaseError extends Error { - constructor(message: string) { - super(message); - this.name = "DatabaseError"; +class TypeError extends Error { + constructor( + message: string = "Type Error", + name: ERROR_NAMES = "TYPE_ERROR" + ) { + super(); + this.name = name; + this.message = message; } } @@ -25,31 +27,21 @@ class DatabaseError extends Error { // Error Handling Utility function handleError(error: Error): void { switch (error.constructor) { - case NetworkError: - console.log("Handle network error:", error.message); - // specific logic for NetworkError - break; - case ValidationError: - console.log("Handle validation error:", error.message); - // specific logic for ValidationError + case TypeError: + console.log("Type Error:", error.message); break; - case DatabaseError: - console.log("Handle database error:", error.message); - // specific logic for DatabaseError + case MappingError: + console.log("Mapping Error:", error.message); break; default: - console.log("Handle general error:", error.message); - // logic for unrecognized error types + console.log( + "Internal Error", + error.message, + "Please report this at" + + "https://github.com/AmmarHalees/onsubmit/issues" + ); break; } } -// Example usage -try { - // Code that may throw errors - throw new ValidationError("Invalid input data"); -} catch (error) { - if (error instanceof Error) { - handleError(error); - } -} +export { MappingError, handleError }; diff --git a/src/mocks/validateField.mocks.ts b/src/mocks/validateField.mocks.ts index 7dc3be3..72d6ff6 100644 --- a/src/mocks/validateField.mocks.ts +++ b/src/mocks/validateField.mocks.ts @@ -16,6 +16,13 @@ const minLength = { input: "abc", expectedOutput: [], }, + case: { + rules: { + minLength: { criterion: 3, message: "Minimum length is 3" }, + }, + input: "", + expectedOutput: [], + }, }; const maxLength = { diff --git a/src/tests/validateField.test.ts b/src/tests/validateField.test.ts index 3abc32d..307931f 100644 --- a/src/tests/validateField.test.ts +++ b/src/tests/validateField.test.ts @@ -19,6 +19,15 @@ describe("validateField", () => { mocks.minLength.fail.rules ) ).toEqual([]); + + expect( + validateField( + mocks.minLength.case.input, + "testField", + mocks.minLength.case.rules + ) + ).toEqual([]); + }); it("should validate maximum length", () => { diff --git a/tsconfig.json b/tsconfig.json index 34499b6..e5e01ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,17 @@ { "compilerOptions": { - /* Base Options: */ "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", "allowJs": true, "resolveJsonModule": true, "moduleDetection": "force", - /* Strictness */ "strict": true, + "noUnusedLocals": true, "noUncheckedIndexedAccess": true, "noUnusedParameters": true, "strictFunctionTypes": true, "strictNullChecks": true, - /* If NOT transpiling with TypeScript: */ "moduleResolution": "Node", "module": "ESNext", "noEmit": true,