diff --git a/form-builder/src/main/java/com/dsc/form_builder/TextFieldState.kt b/form-builder/src/main/java/com/dsc/form_builder/TextFieldState.kt index 4f52cbc..f7965d6 100644 --- a/form-builder/src/main/java/com/dsc/form_builder/TextFieldState.kt +++ b/form-builder/src/main/java/com/dsc/form_builder/TextFieldState.kt @@ -53,6 +53,9 @@ open class TextFieldState( val validations = validators.map { when (it) { is Validators.Email -> validateEmail(it.message) + is Validators.Phone -> validatePhone(it.message) + is Validators.WebUrl -> validateWebUrl(it.message) + is Validators.CardNumber -> validateCardNumber(it.message) is Validators.Required -> validateRequired(it.message) is Validators.Min -> validateMinChars(it.limit, it.message) is Validators.Max -> validateMaxChars(it.limit, it.message) @@ -89,6 +92,60 @@ open class TextFieldState( return valid } + + /** + *This function validates a Phone in [value] + *It will return true if the string value is a valid phone number. + *The implementation makes use of the [android.util.Patterns] class to match the phone number. + *@param message the error message passed to [showError] to display if the value is not a valid phone number. By default we use the [PHONE_MESSAGE] constant. + */ + internal fun validatePhone(message: String): Boolean { + val phoneRegex = "(\\+[0-9]+)?(\\([0-9]+\\)[\\- ]*)?([0-9][0-9\\- ]+[0-9])" + val valid = phoneRegex.toRegex().matches(value) + if (!valid) showError(message) + return valid + } + + /** + *This function validates a Web Url in [value] + *It will return true if the string value is a valid web url. + *@param message the error message passed to [showError] to display if the value is not a valid web url. By default we use the [WEB_URL_MESSAGE] constant. + */ + internal fun validateWebUrl(message: String): Boolean { + val webUrlRegex = + "(https?://)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_+~#?&/=]*)" + val valid = webUrlRegex.toRegex().matches(value) + if (!valid) showError(message) + return valid + } + + /** + *This function validates a Card Number in [value] + *It will return true if the string value is a valid card number. + *@param message the error message passed to [showError] to display if the value is not a valid card number. By default we use the [CARD_NUMBER_MESSAGE] constant. + */ + internal fun validateCardNumber(message: String): Boolean { + val cardNumberRegex = "(^3[47][0-9]{13}\$)|" + + "(^(6541|6556)[0-9]{12}\$)|" + + "(^389[0-9]{11}\$)|" + + "(^3(?:0[0-5]|[68][0-9])[0-9]{11}\$)|" + + "(^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})\$\n)|" + + "(^63[7-9][0-9]{13}\$)|" + + "(^(?:2131|1800|35\\d{3})\\d{11}\$)|" + + "(^9[0-9]{15}\$)|" + + "(^(6304|6706|6709|6771)[0-9]{12,15}\$)|" + + "(^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}\$)|" + + "(^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))\$\n)|" + + "(^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}\$)|" + + "(^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}\$\n)|" + + "(^(62[0-9]{14,17})\$)|" + + "(^4[0-9]{12}(?:[0-9]{3})?\$)|" + + "(^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\$)" + val valid = cardNumberRegex.toRegex().matches(value) + if (!valid) showError(message) + return valid + } + /** * This function validates a required field. * It will return true if the value is not empty. diff --git a/form-builder/src/main/java/com/dsc/form_builder/Validators.kt b/form-builder/src/main/java/com/dsc/form_builder/Validators.kt index d8c99f4..9b8e9d2 100644 --- a/form-builder/src/main/java/com/dsc/form_builder/Validators.kt +++ b/form-builder/src/main/java/com/dsc/form_builder/Validators.kt @@ -2,6 +2,9 @@ package com.dsc.form_builder private const val EMAIL_MESSAGE = "invalid email address" private const val REQUIRED_MESSAGE = "this field is required" +private const val PHONE_MESSAGE = "invalid phone number" +private const val WEB_URL_MESSAGE = "invalid web url" +private const val CARD_NUMBER_MESSAGE = "invalid card number" /** * @@ -33,6 +36,24 @@ sealed interface Validators { */ class Email(var message: String = EMAIL_MESSAGE) : Validators + /** + * This is a phone validator. It will return true if the string value is a valid phone number. The implementation makes use of the [android.util.Patterns] class to match the phone number. + * @param message the error message to display if the value is not a valid phone number. By default we use the [PHONE_MESSAGE] constant. + */ + class Phone(var message: String = PHONE_MESSAGE) : Validators + + /** + * This is a web url validator. It will return true if the string value is a valid web url. + * @param message the error message to display if the value is not a valid web url. By default we use the [WEB_URL_MESSAGE] constant. + */ + class WebUrl(var message: String = WEB_URL_MESSAGE) : Validators + + /** + * This is a card number validator. It will return true if the string value is a valid card number. + * @param message the error message to display if the value is not a valid card number. By default we use the [CARD_NUMBER_MESSAGE] constant. + */ + class CardNumber(var message: String = CARD_NUMBER_MESSAGE) : Validators + /** * This validator is used to check for numeric values. It will return true is the value is numeric and is greater than or equal to the specified limit. * @param limit the minimum value that the value can hold. diff --git a/form-builder/src/test/java/com/dsc/form_builder/TextFieldProviders.kt b/form-builder/src/test/java/com/dsc/form_builder/TextFieldProviders.kt index 8a9e964..d4bcea1 100644 --- a/form-builder/src/test/java/com/dsc/form_builder/TextFieldProviders.kt +++ b/form-builder/src/test/java/com/dsc/form_builder/TextFieldProviders.kt @@ -13,6 +13,36 @@ object EmailArgumentsProvider : ArgumentsProvider { ) } +object PhoneArgumentsProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( + Arguments.of("test", false), + Arguments.of("88005553535", true), + Arguments.of("+8(800) 555-35-35", true), + Arguments.of("8-800-555-35-35-", false), + ) +} + +object WebUrlArgumentsProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( + Arguments.of("test", false), + Arguments.of("htt://www.test.com", false), + Arguments.of("http://test.com", true), + Arguments.of("https://www.test.com", true), + Arguments.of("www.test.", false), + Arguments.of("www.test.com.mx", true), + Arguments.of("https://", false), + Arguments.of("www.test.com", true), + ) +} + +object CardNumberArgumentsProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( + Arguments.of("1111111111111111", false), + Arguments.of("1111", false), + Arguments.of("4548111111111111", true) + ) +} + object MinCharsArgumentsProvider: ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream = Stream.of( Arguments.of("test", 2, true), diff --git a/form-builder/src/test/java/com/dsc/form_builder/TextFieldStateTest.kt b/form-builder/src/test/java/com/dsc/form_builder/TextFieldStateTest.kt index 3b5f1b3..aba835d 100644 --- a/form-builder/src/test/java/com/dsc/form_builder/TextFieldStateTest.kt +++ b/form-builder/src/test/java/com/dsc/form_builder/TextFieldStateTest.kt @@ -62,6 +62,33 @@ internal class TextFieldStateTest { assert(actual == expected) } + @ParameterizedTest + @ArgumentsSource(PhoneArgumentsProvider::class) + fun `Validators_Phone works correctly`(phone: String, expected: Boolean) { + classToTest.change(phone) + + val actual = classToTest.validatePhone("expected validation: $expected") + assert(actual == expected) + } + + @ParameterizedTest + @ArgumentsSource(WebUrlArgumentsProvider::class) + fun `Validators_WebUrl works correctly`(webUrl: String, expected: Boolean) { + classToTest.change(webUrl) + + val actual = classToTest.validateWebUrl("expected validation: $expected") + assert(actual == expected) + } + + @ParameterizedTest + @ArgumentsSource(CardNumberArgumentsProvider::class) + fun `Validators_CardNumber works correctly`(cardNumber: String, expected: Boolean) { + classToTest.change(cardNumber) + + val actual = classToTest.validateCardNumber("expected validation: $expected") + assert(actual == expected) + } + @ParameterizedTest @ArgumentsSource(MinCharsArgumentsProvider::class) fun `Validators_MinChars works correctly`(value: String, limit: Int, expected: Boolean) {