From 7925ab9aa1063b4ec075b7f627cd50f03191b7b0 Mon Sep 17 00:00:00 2001 From: daci Date: Wed, 1 Mar 2023 08:37:18 +0200 Subject: [PATCH] input validator updates, fixed failling tests --- src/source/core/Request.spec.bs | 1 - src/source/mx/BaseViewModel.spec.bs | 5 +- src/source/view/NodeClass.spec.bs | 4 +- src/source/view/ValidationUtils.spec.bs | 166 ++++++++++++++++-- .../view/controls/ControlsGroup.spec.bs | 37 +++- src/source/view/controls/ValidationUtils.bs | 127 +++++++++++--- 6 files changed, 290 insertions(+), 50 deletions(-) diff --git a/src/source/core/Request.spec.bs b/src/source/core/Request.spec.bs index 0fbe514d..89063732 100644 --- a/src/source/core/Request.spec.bs +++ b/src/source/core/Request.spec.bs @@ -2,7 +2,6 @@ import "pkg:/source/tests/BaseTestSuite.spec.bs" import "pkg:/source/core/Request.bs" namespace tests - @only @suite("Request tests") class RequestTests extends tests.BaseTestSuite diff --git a/src/source/mx/BaseViewModel.spec.bs b/src/source/mx/BaseViewModel.spec.bs index 4fd24cb0..c3653b08 100644 --- a/src/source/mx/BaseViewModel.spec.bs +++ b/src/source/mx/BaseViewModel.spec.bs @@ -3,7 +3,6 @@ import "pkg:/source/rooibos/Utils.bs" import "pkg:/source/mx/BaseViewModel.bs" namespace tests - @suite("BaseViewModel tests") class BaseViewModelTests extends mv.tests.BaseTestSuite @@ -38,8 +37,8 @@ namespace tests } task = m.vm.createTask("mc_RequestTask", args, m.vm.cancelWaitTimer, false, "none") m.assertSubType(task, "mc_RequestTask") - m.assertEqual(task.v1, "a") - m.assertEqual(task.v2, "b") + m.assertEqual(task.args.v1, "a") + m.assertEqual(task.args.v2, "b") end function end class diff --git a/src/source/view/NodeClass.spec.bs b/src/source/view/NodeClass.spec.bs index c35b6e15..b56d7e50 100644 --- a/src/source/view/NodeClass.spec.bs +++ b/src/source/view/NodeClass.spec.bs @@ -38,8 +38,8 @@ namespace tests } task = m.nc.createTask("mc_RequestTask", args, m.nc.cancelWaitTimer, false, "none") m.assertSubType(task, "mc_RequestTask") - m.assertEqual(task.v1, "a") - m.assertEqual(task.v2, "b") + m.assertEqual(task.args.v1, "a") + m.assertEqual(task.args.v2, "b") end function end class diff --git a/src/source/view/ValidationUtils.spec.bs b/src/source/view/ValidationUtils.spec.bs index 6c84fa86..aca9c77c 100644 --- a/src/source/view/ValidationUtils.spec.bs +++ b/src/source/view/ValidationUtils.spec.bs @@ -6,6 +6,10 @@ namespace tests class ValidationUtilsTests extends mv.tests.BaseTestSuite private validator + protected override function beforeEach() + super.beforeEach() + m.validator = new mv.input.validators.InputValidator() + end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @describe("tests constructor") @@ -13,25 +17,161 @@ namespace tests @it("initializes") function _() - m.validator = new mv.controls.InputValidator() - m.assertEqual(m.validator.validationData, { hasError: false, errorMessage: "" }) + m.assertEqual(m.validator.__className, "mv.input.validators.InputValidator") + + m.validator = new mv.input.validators.InputValidator() + + m.assertTrue(mc.isFunction(m.validator.validatorTypeMap[mv.input.validators.Type.alphaNumeric])) + m.assertTrue(mc.isFunction(m.validator.validatorTypeMap[mv.input.validators.Type.numeric])) + m.assertTrue(mc.isFunction(m.validator.validatorTypeMap[mv.input.validators.Type.zipcode])) + m.assertTrue(mc.isFunction(m.validator.validatorTypeMap[mv.input.validators.Type.email])) + m.assertTrue(mc.isFunction(m.validator.validatorTypeMap[mv.input.validators.Type.required])) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @describe("tests validate") + @describe("validate") '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @it("checks returned data") - @params("numeric", "12345", { hasError: false, errorMessage: "" }) - @params("numeric", "1234567", { hasError: true, errorMessage: "Invalid.Must contain 5 digits." }) - @params("numeric", "1111L", { hasError: true, errorMessage: "Invalid.Must contain 5 digits." }) - @params("email", "invalidemail.com", { hasError: true, errorMessage: "Invalid email address." }) - @params("email", "test@gmail.com", { hasError: false, errorMessage: "" }) - @params("", "12345", { hasError: false, errorMessage: "" }) - @params("", "abc", { hasError: false, errorMessage: "" }) - function _(validatorType, value, expected) + @it("returns default validation data when no validator is found, or validator type is not valid") + @params(invalid) + @params([]) + @params(123) + @params("other") + function _(validatorType) + m.validator.validate(validatorType, "abc") + m.assertEqual(m.validator.validate(validatorType, "abc"), { hasError: false, errorMessage: "" }) + end function + + @it("it uses the correct validator") + @params("alphaNumeric") + @params("numeric") + @params("required") + @params("email") + @params("zipcode") + function _(validatorType) + value = "abc" + message = "" + + m.expectCalled(m.validator.validateByType(validatorType, value, message)) + m.validator.validate(validatorType, value) - m.assertEqual(m.validator.validationData, expected) end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateByType") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("validates the value using the correct validator") + @params("email", "test@emailcom", "Invalid email address.") + @params("numeric", "abc", "Must contain only numbers.") + @params("required", "", "Required.") + function _(validatorType, value, expectedMessage) + result = m.validator.validateByType(validatorType, value) + + m.assertEqual(result.errorMessage, expectedMessage) + m.assertEqual(result.hasError, true) + end function + + @it("validates the value using the correct validator and replaces default message with custom message") + @params("numeric", "abc", "Only numbers.") + @params("email", "test@emailcom", "Invalid email") + @params("required", "", "Field is required.") + function _(validatorType, value, customMessage) + result = m.validator.validateByType(validatorType, value, customMessage) + + m.assertEqual(result.errorMessage, customMessage) + m.assertEqual(result.hasError, true) + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateAlphaNumeric") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("returns object with hasError set to false when value is alpha numeric, true otherwise") + @params("abc123", false, "") + @params("abc", false, "") + @params("123", false, "") + @params("abc123!", true, "Must contain only alpha numeric characters.") + @params("a&$%b", true, "Must contain only alpha numeric characters.") + function _(value, expected, message) + result = m.validator.validateAlphaNumeric(value) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateNumeric") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("returns object with hasError set to false when value is numeric, true otherwise") + @params("abc123", true, "Must contain only numbers.") + @params("abc", true, "Must contain only numbers.") + @params("123", false, "") + @params("a&$%b", true, "Must contain only numbers.") + function _(value, expected, message) + result = m.validator.validateNumeric(value) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + + @it("returns object with hasError set to false when value is numeric and value length is equal to exactLength, true otherwise") + @params("123", 3, false, "") + @params("123", 2, true, "Must be 2 digits long.") + function _(value, exactLength, expected, message) + result = m.validator.validateNumeric(value, exactLength) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateZipCode") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("returns object with hasError set to false if valid zipcode, true otherwise") + @params("12345", false, "") + @params("123", true, "Must be 5 digits long.") + @params("123456", true, "Must be 5 digits long.") + @params("a&$%b", true, "Must be 5 digits long.") + @params("abcde", true, "Must be 5 digits long.") + function _(value, expected, message) + result = m.validator.validateZipCode(value) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateRequired") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("returns object with hasError set to false if value is not empty, true otherwise") + @params("abc", false, "") + @params("", true, "Required.") + function _(value, expected, message) + result = m.validator.validateRequired(value) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("validateEmail") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("returns object with hasError set to false if value is valid email address, true otherwise") + @params("aaa", true, "Invalid email address.") + @params("aaa@bbb", true, "Invalid email address.") + @params("aaa@bbb.", true, "Invalid email address.") + @params("test@test.com", false, "") + function _(value, expected, message) + result = m.validator.validateEmail(value) + + m.assertEqual(result.hasError, expected) + m.assertEqual(result.errorMessage, message) + end function + end class end namespace diff --git a/src/source/view/controls/ControlsGroup.spec.bs b/src/source/view/controls/ControlsGroup.spec.bs index fb2152aa..47fabad6 100644 --- a/src/source/view/controls/ControlsGroup.spec.bs +++ b/src/source/view/controls/ControlsGroup.spec.bs @@ -163,7 +163,7 @@ namespace tests end function - @it("sets controls isSelected to true, if the control is not disabled") + @it("sets controls isSelected to true, if the control is not disabled and setIsSelectedOnChildren is true") @params(false, true) @params(true, invalid) @params(invalid, true) @@ -172,6 +172,7 @@ namespace tests "id": "c1" isDisabled: disabled } + m.control.setIsSelectedOnChildren = true m.control.selectedIndex = -1 m.control.focusedIndex = 0 @@ -182,7 +183,30 @@ namespace tests m.assertEqual(c1.selected, expectedIsSelected) m.assertEqual(selectedControl.isSelected, expectedIsSelected) + end function + + @it("doesn't set controls isSelected to true, if setIsSelectedOnChildren is false") + @params(false) + @params(true) + @params(invalid) + function _(disabled) + c1 = { + "id": "c1" + isDisabled: disabled + selected: false + isSelected: false + } + m.control.setIsSelectedOnChildren = false + m.control.selectedIndex = -1 + m.control.focusedIndex = 0 + m.control.visibleChildren = [c1] + + m.assertTrue(m.control.onKeyPressOK()) + selectedControl = m.control.visibleChildren[0] + + m.assertFalse(c1.selected) + m.assertFalse(selectedControl.isSelected) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -210,7 +234,7 @@ namespace tests m.control.setFocusedControl(control) m.assertEqual(m.control.focusedIndex, -1) - m.assertInvalid(m.control.focusedControl) + m.assertInvalid(m.control._focusedControl) end function @it("sets focused control if control is found in original children") @@ -225,7 +249,7 @@ namespace tests m.control.setFocusedControl(control) m.assertEqual(m.control.focusedIndex, 0) - m.assertTrue(m.control.focusedControl.isSameNode(control)) + m.assertEqual(m.control._focusedControl, control) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -235,11 +259,12 @@ namespace tests @it("returns true if control is focused or not") @params(true) @params(false) - function _(isControlFocused) + function _(isSameNode) control = { "id": "control" } + m.control._focusedControl = control - m.expectCalled(m.control.isControlFocused(control), isControlFocused) - m.assertEqual(m.control.getIsFocusedControl(control), isControlFocused) + m.expectCalled(m.control._focusedControl.isSameNode(control), isSameNode) + m.assertEqual(m.control.getIsFocusedControl(control), isSameNode) end function end class diff --git a/src/source/view/controls/ValidationUtils.bs b/src/source/view/controls/ValidationUtils.bs index 106a43dd..bb8f1af1 100644 --- a/src/source/view/controls/ValidationUtils.bs +++ b/src/source/view/controls/ValidationUtils.bs @@ -1,53 +1,130 @@ import "pkg:/source/core/BaseClass.bs" -namespace mv.controls - - class ValidationTypes +namespace mv.input.validators + enum Type + alphaNumeric = "alphaNumeric" numeric = "numeric" + zipcode = "zipcode" + required = "required" email = "email" - end class + end enum class InputValidator extends mc.BaseClass + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - '++ Private Fields + '++ Private fields '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - private validationTypes = new mv.controls.ValidationTypes() - private validationData = { hasError: false, errorMessage: "" } + private validatorTypeMap = {} + private message as string '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - '++ Initialize + '++ Initialization '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ function new() super("InputValidator") - m.log = new log.Logger("InputValidator") + m.configureValidators() end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - '++ Public Methods + '++ Public methods '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - function validate(validatorType as string, value as string) as mc.types.assocarray - return m.validateByType(validatorType, value) + function validate(validatorType as string, value as string, message = "" as string) + m.log.info("Validating value: ", value, " with validator type: ", validatorType) + validationData = { hasError: false, errorMessage: "" } + + if not mc.isString(validatorType) + return validationData + end if + + return m.validateByType(validatorType, value, message) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - '++ Private Methods + '++ Private methods '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - private function validateByType(validatorType as string, value as string) - isValid = true - if validatorType = m.validationTypes.numeric - regex = createObject("roRegex", "^[0-9]+$", "i") - isValid = regex.isMatch(value) and len(value) = 5: - m.validationData.errorMessage = isValid ? "" : "Invalid.Must contain 5 digits." - else if validatorType = m.validationTypes.email - regex = createObject("roRegex", "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", "i") - isValid = regex.isMatch(value) - m.validationData.errorMessage = isValid ? "" : "Invalid email address." + + private function configureValidators() + m.log.info("Setting available validators") + m.validatorTypeMap[mv.input.validators.Type.alphaNumeric] = m.validateAlphaNumeric + m.validatorTypeMap[mv.input.validators.Type.numeric] = m.validateNumeric + m.validatorTypeMap [mv.input.validators.Type.required] = m.validateRequired + m.validatorTypeMap[mv.input.validators.Type.email] = m.validateEmail + m.validatorTypeMap[mv.input.validators.Type.zipcode] = m.validateZipcode + end function + + private function validateByType(validatorType as string, value as string, message = "" as string) + m.log.info("Validating value: ", value, " with validator type: ", validatorType) + validationData = { hasError: false, errorMessage: "" } + + if m.validatorTypeMap.doesExist(validatorType) + validator = m.validatorTypeMap[validatorType] + validationData = validator(value) + if message <> "" + validationData.errorMessage = message + return validationData + end if end if - m.validationData.hasError = not isValid - return m.validationData + + return validationData + end function + + private function validateAlphaNumeric(value as string) + m.log.info("Validating alpha numeric: ", value) + + regex = createObject("roRegex", "^[A-Za-z0-9]+$", "i") + isValid = regex.isMatch(value) + + return { + hasError: not isValid + errorMessage: isValid ? "" : "Must contain only alpha numeric characters." + } + end function + + private function validateZipCode(value as string) + m.log.info("Validating zipcode", value) + return m.validateNumeric(value, 5) + end function + + private function validateNumeric(value as string, exactLength = 0 as integer) + m.log.info("Validating numeric", value) + regex = createObject("roRegex", "^[0-9]+$", "i") + isValid = regex.isMatch(value) + + message = "Must contain only numbers." + + if exactLength > 0 + isValid = (len(value) = exactLength) and isValid + message = `Must be ${exactLength} digits long.` + end if + + return { + hasError: not isValid + errorMessage: isValid ? "" : message + } + end function + + private function validateRequired(value as string) + m.log.info("Validating required", value) + isValid = value <> "" + + return { + hasError: not isValid + errorMessage: isValid ? "" : "Required." + } + end function + + private function validateEmail(value as string) + m.log.info("Validating email", value) + regex = createObject("roRegex", "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", "i") + isValid = regex.isMatch(value) + + return { + hasError: not isValid + errorMessage: isValid ? "" : "Invalid email address." + } end function end class end namespace