From b6dd984cf444d244343707f1e13a71ebe1c74e7c Mon Sep 17 00:00:00 2001 From: Jacob Moura Date: Sat, 24 Aug 2024 16:38:28 -0300 Subject: [PATCH] Added must and mustWith --- CHANGELOG.md | 4 ++ README.md | 22 ++++---- .../lib/domain/dtos/register_param_dto.dart | 10 ++-- .../lib/domain/validations/extensions.dart | 10 ++-- .../register_param_validation.dart | 4 ++ .../presentation/login_page/login_page.dart | 4 +- .../register_page/register_page.dart | 26 +++++++-- lib/src/lucid_validation.dart | 14 ++--- lib/src/validator_builder.dart | 29 +++++++--- .../validators/equal_validator_validator.dart | 15 +++--- .../validators/greater_than_validator.dart | 6 +-- lib/src/validators/is_empty_validator.dart | 6 +-- lib/src/validators/is_not_null_validator.dart | 6 +-- lib/src/validators/is_null_validator.dart | 6 +-- lib/src/validators/less_then_validator.dart | 6 +-- .../validators/matches_pattern_validator.dart | 6 +-- lib/src/validators/max_length_validator.dart | 6 +-- lib/src/validators/max_validator.dart | 6 +-- lib/src/validators/min_length_validator.dart | 6 +-- lib/src/validators/min_validator.dart | 6 +-- .../must_have_lowercase_validator.dart | 6 +-- .../must_have_numbers_validator.dart | 6 +-- ...must_have_special_character_validator.dart | 6 +-- .../must_have_uppercase_validator.dart | 6 +-- lib/src/validators/not_empty_validator.dart | 6 +-- lib/src/validators/not_equal_validator.dart | 15 +++--- lib/src/validators/range_validator.dart | 6 +-- lib/src/validators/valid_cep_validator.dart | 6 +-- lib/src/validators/valid_cnpj_validator.dart | 6 +-- lib/src/validators/valid_cpf_validator.dart | 6 +-- .../valid_creditcard_validator.dart | 6 +-- lib/src/validators/valid_email_validator.dart | 6 +-- pubspec.yaml | 2 +- test/lucid_validation_test.dart | 18 +++++++ test/mocks/mocks.dart | 53 ++++++++++++++++--- 35 files changed, 231 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87d7903..63c514b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.4 + +* Added must and mustWith + ## 0.0.3 * Added Cascade Mode diff --git a/README.md b/README.md index d4de697..7ad1d04 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ Note, the validate method returns a list of errors with all validation exception Here’s a complete list of available validators you can use: +- **must**: custom validation. +- **mustWith**: custom validation with entity. - **equalTo**: checks if value is equal to another value. - **greaterThan**: Checks if number is greater than minimum value. - **lessThan**: Checks if the number is less than max value. @@ -114,14 +116,15 @@ Here’s a complete list of available validators you can use: If you’re using the `lucid_validation` package in a Flutter app, integrating with `TextFormField` is straightforward. -Use the `byField('key')` for this: +Use the `byField(entity, 'key')` for this: ```dart import 'package:flutter/material.dart'; import 'package:lucid_validation/lucid_validation.dart'; class LoginForm extends StatelessWidget { - final validator = UserValidation(); + final validator = CredentialsValidation(); + final credentials = CredentialsModel(); @override Widget build(BuildContext context) { @@ -130,11 +133,11 @@ class LoginForm extends StatelessWidget { children: [ TextFormField( decoration: const InputDecoration(hintText: 'Email'), - validator: validator.byField('email'), + validator: validator.byField(credentials, 'email'), ), TextFormField( decoration: const InputDecoration(hintText: 'Password'), - validator: validator.byField('password'), + validator: validator.byField(credentials, 'password'), obscureText: true, ), ], @@ -157,12 +160,13 @@ You can apply CascadeMode to your validation chain using the cascaded method: ```dart return notEmpty() // + .cascade(CascadeMode.stopOnFirstFailure); // change cascade mode + .minLength(5, message: 'Must be at least 8 characters long') .mustHaveLowercase() .mustHaveUppercase() .mustHaveNumbers() .mustHaveSpecialCharacter() - .cascaded(CascadeMode.stopOnFirstFailure); // change cascade mode ``` @@ -171,17 +175,17 @@ You can apply CascadeMode to your validation chain using the cascaded method: You can easily extend the functionality of `LucidValidation` by creating your own custom rules using `extensions`. Here’s an example of how to create a validation for phone numbers: ```dart -extension CustomValidPhoneValidator on LucidValidationBuilder { +extension CustomValidPhoneValidator on LucidValidationBuilder { LucidValidationBuilder customValidPhone({String message = 'Invalid phone number format'}) { - return registerRule( - (value) => RegExp(r'^\(?(\d{2})\)?\s?9?\d{4}-?\d{4}\$').hasMatch(value), + return matchesPattern( + r'^\(?(\d{2})\)?\s?9?\d{4}-?\d{4}\$', message, 'invalid_phone_format', ); } } -extension CustomValidPasswordValidator on LucidValidationBuilder { +extension CustomValidPasswordValidator on LucidValidationBuilder { LucidValidationBuilder customValidPassword() { return notEmpty() .minLength(8) diff --git a/example/lib/domain/dtos/register_param_dto.dart b/example/lib/domain/dtos/register_param_dto.dart index b567615..2382c3e 100644 --- a/example/lib/domain/dtos/register_param_dto.dart +++ b/example/lib/domain/dtos/register_param_dto.dart @@ -1,19 +1,23 @@ class RegisterParamDto { String email; - String password; String phone; + String password; + String confirmPassword; RegisterParamDto({ required this.email, - required this.password, required this.phone, + required this.password, + required this.confirmPassword, }); - factory RegisterParamDto.empty() => RegisterParamDto(email: '', password: '', phone: ''); + factory RegisterParamDto.empty() => RegisterParamDto(email: '', password: '', phone: '', confirmPassword: ''); setEmail(String value) => email = value; setPassword(String value) => password = value; + setConfirmPassword(String value) => confirmPassword = value; + setPhone(String value) => phone = value; } diff --git a/example/lib/domain/validations/extensions.dart b/example/lib/domain/validations/extensions.dart index e6635fa..1eec2a1 100644 --- a/example/lib/domain/validations/extensions.dart +++ b/example/lib/domain/validations/extensions.dart @@ -1,7 +1,7 @@ import 'package:lucid_validation/lucid_validation.dart'; -extension CustomValidPasswordValidator on LucidValidationBuilder { - LucidValidationBuilder customValidPassword() { +extension CustomValidPasswordValidator on LucidValidationBuilder { + LucidValidationBuilder customValidPassword() { return notEmpty() // .minLength(5) .mustHaveLowercase() @@ -11,8 +11,8 @@ extension CustomValidPasswordValidator on LucidValidationBuilder { } } -extension CustomValidPhoneValidator on LucidValidationBuilder { - LucidValidationBuilder customValidPhone(String message) { - return registerRule((value) => RegExp(r'^\(?(\d{2})\)?\s?9?\d{4}-?\d{4}$').hasMatch(value), message, 'invalid_phone'); +extension CustomValidPhoneValidator on LucidValidationBuilder { + LucidValidationBuilder customValidPhone(String message) { + return must((value) => RegExp(r'^\(?(\d{2})\)?\s?9?\d{4}-?\d{4}$').hasMatch(value), message, 'invalid_phone'); } } diff --git a/example/lib/domain/validations/register_param_validation.dart b/example/lib/domain/validations/register_param_validation.dart index 0ff1195..32c76dc 100644 --- a/example/lib/domain/validations/register_param_validation.dart +++ b/example/lib/domain/validations/register_param_validation.dart @@ -11,6 +11,10 @@ class RegisterParamValidation extends LucidValidation { ruleFor((registerParamDto) => registerParamDto.password, key: 'password') // .customValidPassword(); + ruleFor((registerParamDto) => registerParamDto.confirmPassword, key: 'confirmPassword') // + .customValidPassword() + .equalTo((registerParamDto) => registerParamDto.password, message: 'Password and confirm password must match'); + ruleFor((registerParamDto) => registerParamDto.phone, key: 'phone') // .customValidPhone('Phone invalid format'); } diff --git a/example/lib/presentation/login_page/login_page.dart b/example/lib/presentation/login_page/login_page.dart index f2ede1f..e288bf6 100644 --- a/example/lib/presentation/login_page/login_page.dart +++ b/example/lib/presentation/login_page/login_page.dart @@ -50,7 +50,7 @@ class _LoginPageState extends State { TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, onChanged: loginParamDto.setEmail, - validator: validator.byField('email'), + validator: validator.byField(loginParamDto, 'email'), decoration: const InputDecoration( hintText: 'Email', ), @@ -59,7 +59,7 @@ class _LoginPageState extends State { TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, onChanged: loginParamDto.setPassword, - validator: validator.byField('password'), + validator: validator.byField(loginParamDto, 'password'), obscureText: true, decoration: const InputDecoration( hintText: 'Password', diff --git a/example/lib/presentation/register_page/register_page.dart b/example/lib/presentation/register_page/register_page.dart index 1b247d4..86f6c80 100644 --- a/example/lib/presentation/register_page/register_page.dart +++ b/example/lib/presentation/register_page/register_page.dart @@ -63,14 +63,27 @@ class _RegisterPageState extends State { style: TextStyle(fontWeight: FontWeight.w800, fontSize: 24), ), const SizedBox(height: 24), - TextField( + TextFormField( + autovalidateMode: AutovalidateMode.onUserInteraction, onChanged: registerParamDto.setEmail, + validator: validator.byField(registerParamDto, 'email'), decoration: const InputDecoration( hintText: 'Email', ), ), const SizedBox(height: 12), - TextField( + TextFormField( + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: validator.byField(registerParamDto, 'phone'), + onChanged: registerParamDto.setPhone, + decoration: const InputDecoration( + hintText: '(11) 99999-9999', + ), + ), + const SizedBox(height: 12), + TextFormField( + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: validator.byField(registerParamDto, 'password'), onChanged: registerParamDto.setPassword, obscureText: true, decoration: const InputDecoration( @@ -78,10 +91,13 @@ class _RegisterPageState extends State { ), ), const SizedBox(height: 12), - TextField( - onChanged: registerParamDto.setPhone, + TextFormField( + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: validator.byField(registerParamDto, 'confirmPassword'), + onChanged: registerParamDto.setConfirmPassword, + obscureText: true, decoration: const InputDecoration( - hintText: '(11) 99999-9999', + hintText: 'Confirm Password', ), ), const SizedBox(height: 12), diff --git a/lib/src/lucid_validation.dart b/lib/src/lucid_validation.dart index b50797e..15d0612 100644 --- a/lib/src/lucid_validation.dart +++ b/lib/src/lucid_validation.dart @@ -4,7 +4,7 @@ part 'validator_builder.dart'; class _PropSelector { final TProp Function(E entity) selector; - final LucidValidationBuilder builder; + final LucidValidationBuilder builder; _PropSelector({required this.selector, required this.builder}); } @@ -27,8 +27,8 @@ abstract class LucidValidation { /// final validator = UserValidation(); /// validator.ruleFor((user) => user.email).validEmail(); /// ``` - LucidValidationBuilder ruleFor(TProp Function(E entity) func, {String key = ''}) { - final builder = LucidValidationBuilder(key: key); + LucidValidationBuilder ruleFor(TProp Function(E entity) func, {String key = ''}) { + final builder = LucidValidationBuilder(key: key); final propSelector = _PropSelector(selector: func, builder: builder); _propSelectors.add(propSelector); @@ -46,7 +46,7 @@ abstract class LucidValidation { /// final emailValidator = validator.byField('email'); /// String? validationResult = emailValidator('user@example.com'); /// ``` - String? Function(String?)? byField(String key) { + String? Function(String?)? byField(E entity, String key) { final propSelector = _propSelectors .where( (propSelector) => propSelector.builder.key == key, @@ -58,9 +58,9 @@ abstract class LucidValidation { return (value) { if (value == null) return null; final builder = propSelector.builder; - final rules = builder._rules.cast>(); + final rules = builder._rules.cast>(); for (var rule in rules) { - final result = rule(value); + final result = rule(value, entity); if (!result.isValid) { return result.error.message; @@ -92,7 +92,7 @@ abstract class LucidValidation { final mode = propSelector.builder._mode; for (var rule in propSelector.builder._rules) { - final result = rule(propValue); + final result = rule(propValue, entity); if (!result.isValid) { errors.add(result.error); diff --git a/lib/src/validator_builder.dart b/lib/src/validator_builder.dart index da448ea..a78f91c 100644 --- a/lib/src/validator_builder.dart +++ b/lib/src/validator_builder.dart @@ -8,11 +8,11 @@ enum CascadeMode { /// Builder class used to define validation rules for a specific property type [TProp]. /// /// [TProp] represents the type of the property being validated. -typedef RuleFunc = ValidatorResult Function(dynamic value); +typedef RuleFunc = ValidatorResult Function(dynamic value, dynamic entity); -class LucidValidationBuilder { +class LucidValidationBuilder { final String key; - final List> _rules = []; + final List> _rules = []; var _mode = CascadeMode.continueExecution; /// Creates a [LucidValidationBuilder] instance with an optional [key]. @@ -30,10 +30,10 @@ class LucidValidationBuilder { /// Example: /// ```dart /// final builder = LucidValidationBuilder(key: 'username'); - /// builder.registerRule((username) => username.isNotEmpty, 'Username cannot be empty'); + /// builder.must((username) => username.isNotEmpty, 'Username cannot be empty'); /// ``` - LucidValidationBuilder registerRule(bool Function(TProp value) validator, String message, String code) { - ValidatorResult callback(value) => ValidatorResult( + LucidValidationBuilder must(bool Function(TProp value) validator, String message, String code) { + ValidatorResult callback(value, entity) => ValidatorResult( isValid: validator(value), error: ValidatorError( message: message, @@ -47,8 +47,23 @@ class LucidValidationBuilder { return this; } + LucidValidationBuilder mustWith(bool Function(TProp value, Entity enntity) validator, String message, String code) { + ValidatorResult callback(value, entity) => ValidatorResult( + isValid: validator(value, entity), + error: ValidatorError( + message: message, + key: key, + code: code, + ), + ); + + _rules.add(callback); + + return this; + } + /// Changes the cascade mode for this validation. - LucidValidationBuilder cascaded(CascadeMode mode) { + LucidValidationBuilder cascade(CascadeMode mode) { _mode = mode; return this; } diff --git a/lib/src/validators/equal_validator_validator.dart b/lib/src/validators/equal_validator_validator.dart index 7bb46a7..97a3dfd 100644 --- a/lib/src/validators/equal_validator_validator.dart +++ b/lib/src/validators/equal_validator_validator.dart @@ -4,10 +4,10 @@ part of 'validators.dart'; /// /// This extension adds an `equalTo` method that can be used to ensure that a value /// is equal to a specific value. -extension EqualValidator on LucidValidationBuilder { +extension EqualValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the value is equal to [comparison]. /// - /// [comparison] is the value that the field must match. + /// [predicate] is a function that returns the value to compare against. /// [message] is the error message returned if the validation fails. Defaults to "Must be equal to $comparison". /// [code] is an optional error code for translation purposes. /// @@ -18,10 +18,13 @@ extension EqualValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'confirmPassword'); /// builder.equalTo('password123'); /// ``` - LucidValidationBuilder equalTo(T comparison, {String message = r'Must be equal to $comparison', String code = 'equal_to_error'}) { - return registerRule( - (value) => value == comparison, - message.replaceAll(r'$comparison', comparison.toString()), + LucidValidationBuilder equalTo(T Function(E entity) predicate, {String message = r'Must be equal', String code = 'equal_error'}) { + return mustWith( + (value, entity) { + final comparison = predicate(entity); + return value == comparison; + }, + message, code, ); } diff --git a/lib/src/validators/greater_than_validator.dart b/lib/src/validators/greater_than_validator.dart index 9154a29..28dbca9 100644 --- a/lib/src/validators/greater_than_validator.dart +++ b/lib/src/validators/greater_than_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `greaterThan` method that can be used to ensure that a number /// is greater than a specified value. -extension GreaterThanValidator on LucidValidationBuilder { +extension GreaterThanValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [num] is greater than [minValue]. /// /// [minValue] is the value that the number must be greater than. @@ -18,8 +18,8 @@ extension GreaterThanValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'age'); /// builder.greaterThan(18); /// ``` - LucidValidationBuilder greaterThan(num minValue, {String message = r'Must be greater than $minValue', String code = 'greater_than'}) { - return registerRule( + LucidValidationBuilder greaterThan(num minValue, {String message = r'Must be greater than $minValue', String code = 'greater_than'}) { + return must( (value) => value > minValue, message.replaceAll('$minValue', minValue.toString()), code, diff --git a/lib/src/validators/is_empty_validator.dart b/lib/src/validators/is_empty_validator.dart index 4ff20ce..c256730 100644 --- a/lib/src/validators/is_empty_validator.dart +++ b/lib/src/validators/is_empty_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds an `isEmpty` method that can be used to ensure that a string /// is empty. -extension IsEmptyValidator on LucidValidationBuilder { +extension IsEmptyValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is empty. /// /// [message] is the error message returned if the validation fails. Defaults to "Must be empty". @@ -17,8 +17,8 @@ extension IsEmptyValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'field'); /// builder.isEmpty(); /// ``` - LucidValidationBuilder isEmpty({String message = 'Must be empty', String code = 'must_be_empty'}) { - return registerRule( + LucidValidationBuilder isEmpty({String message = 'Must be empty', String code = 'must_be_empty'}) { + return must( (value) => value.isEmpty, message, code, diff --git a/lib/src/validators/is_not_null_validator.dart b/lib/src/validators/is_not_null_validator.dart index aba82e0..e42b0a5 100644 --- a/lib/src/validators/is_not_null_validator.dart +++ b/lib/src/validators/is_not_null_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds an `isNotNull` method that can be used to ensure that a value /// is not null. -extension IsNotNullValidator on LucidValidationBuilder { +extension IsNotNullValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the value is not null. /// /// [message] is the error message returned if the validation fails. Defaults to "Cannot be null". @@ -17,8 +17,8 @@ extension IsNotNullValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'requiredField'); /// builder.isNotNull(); /// ``` - LucidValidationBuilder isNotNull({String message = 'Cannot be null', String code = 'cannot_be_null'}) { - return registerRule( + LucidValidationBuilder isNotNull({String message = 'Cannot be null', String code = 'cannot_be_null'}) { + return must( (value) => value != null, message, code, diff --git a/lib/src/validators/is_null_validator.dart b/lib/src/validators/is_null_validator.dart index 8114c1e..80fe141 100644 --- a/lib/src/validators/is_null_validator.dart +++ b/lib/src/validators/is_null_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds an `isNull` method that can be used to ensure that a value /// is null. -extension IsNullValidator on LucidValidationBuilder { +extension IsNullValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the value is null. /// /// [message] is the error message returned if the validation fails. Defaults to "Must be null". @@ -17,8 +17,8 @@ extension IsNullValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'optionalField'); /// builder.isNull(); /// ``` - LucidValidationBuilder isNull({String message = 'Must be null', String code = 'must_be_null'}) { - return registerRule( + LucidValidationBuilder isNull({String message = 'Must be null', String code = 'must_be_null'}) { + return must( (value) => value == null, message, code, diff --git a/lib/src/validators/less_then_validator.dart b/lib/src/validators/less_then_validator.dart index 649ae34..e1883c7 100644 --- a/lib/src/validators/less_then_validator.dart +++ b/lib/src/validators/less_then_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `lessThan` method that can be used to ensure that a number /// is less than a specified value. -extension LessThanValidator on LucidValidationBuilder { +extension LessThanValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [num] is less than [maxValue]. /// /// [maxValue] is the value that the number must be less than. @@ -18,8 +18,8 @@ extension LessThanValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'discount'); /// builder.lessThan(100); /// ``` - LucidValidationBuilder lessThan(num maxValue, {String message = r'Must be less than $maxValue', String code = 'less_than'}) { - return registerRule( + LucidValidationBuilder lessThan(num maxValue, {String message = r'Must be less than $maxValue', String code = 'less_than'}) { + return must( (value) => value < maxValue, message.replaceAll('$maxValue', maxValue.toString()), code, diff --git a/lib/src/validators/matches_pattern_validator.dart b/lib/src/validators/matches_pattern_validator.dart index 9ed2ebb..a96e7e3 100644 --- a/lib/src/validators/matches_pattern_validator.dart +++ b/lib/src/validators/matches_pattern_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `matchesPattern` method that can be used to ensure that a string /// matches a specific regex pattern. -extension MatchesPatternValidator on LucidValidationBuilder { +extension MatchesPatternValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] matches the [pattern]. /// /// [pattern] is the regex pattern that the string must match. @@ -18,8 +18,8 @@ extension MatchesPatternValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'phoneNumber'); /// builder.matchesPattern(r'^\d{3}-\d{3}-\d{4}$'); /// ``` - LucidValidationBuilder matchesPattern(String pattern, {String message = 'Invalid format', String code = 'invalid_format'}) { - return registerRule( + LucidValidationBuilder matchesPattern(String pattern, {String message = 'Invalid format', String code = 'invalid_format'}) { + return must( (value) => RegExp(pattern).hasMatch(value), message, code, diff --git a/lib/src/validators/max_length_validator.dart b/lib/src/validators/max_length_validator.dart index 13549ca..f20cc61 100644 --- a/lib/src/validators/max_length_validator.dart +++ b/lib/src/validators/max_length_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `maxLength` method that can be used to ensure that the length of a string /// does not exceed a specified maximum number of characters. -extension MaxLengthValidator on LucidValidationBuilder { +extension MaxLengthValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the length of a [String] is less than or equal to [num]. /// /// [num] is the maximum allowed length for the string. @@ -18,8 +18,8 @@ extension MaxLengthValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'username'); /// builder.maxLength(10); /// ``` - LucidValidationBuilder maxLength(int num, {String message = r'Must be at most $num characters long', String code = 'max_length'}) { - return registerRule( + LucidValidationBuilder maxLength(int num, {String message = r'Must be at most $num characters long', String code = 'max_length'}) { + return must( (value) => value.length <= num, message.replaceAll(r'$num', num.toString()), code, diff --git a/lib/src/validators/max_validator.dart b/lib/src/validators/max_validator.dart index 5a462d0..5c94fc8 100644 --- a/lib/src/validators/max_validator.dart +++ b/lib/src/validators/max_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `max` method that can be used to ensure that a numerical value /// does not exceed a specified maximum. -extension MaxValidator on LucidValidationBuilder { +extension MaxValidator on LucidValidationBuilder { /// Adds a validation rule that checks if a [num] value is less than or equal to [num]. /// /// [num] is the maximum allowed value. @@ -18,8 +18,8 @@ extension MaxValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'age'); /// builder.max(18); /// ``` - LucidValidationBuilder max(num num, {String message = r'Must be less than or equal to $num', String code = 'max_value'}) { - return registerRule( + LucidValidationBuilder max(num num, {String message = r'Must be less than or equal to $num', String code = 'max_value'}) { + return must( (value) => value <= num, message.replaceAll(r'$num', num.toString()), code, diff --git a/lib/src/validators/min_length_validator.dart b/lib/src/validators/min_length_validator.dart index 31c83f5..ff0f01d 100644 --- a/lib/src/validators/min_length_validator.dart +++ b/lib/src/validators/min_length_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `minLength` method that can be used to ensure that the length of a string /// meets a specified minimum number of characters. -extension MinLengthValidator on LucidValidationBuilder { +extension MinLengthValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the length of a [String] is greater than or equal to [num]. /// /// [num] is the minimum required length for the string. @@ -18,8 +18,8 @@ extension MinLengthValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'password'); /// builder.minLength(8); /// ``` - LucidValidationBuilder minLength(int num, {String message = r'Must be at least $num characters long', String code = 'min_length'}) { - return registerRule( + LucidValidationBuilder minLength(int num, {String message = r'Must be at least $num characters long', String code = 'min_length'}) { + return must( (value) => value.length >= num, message.replaceAll(r'$num', num.toString()), code, diff --git a/lib/src/validators/min_validator.dart b/lib/src/validators/min_validator.dart index 70eb885..1dc158c 100644 --- a/lib/src/validators/min_validator.dart +++ b/lib/src/validators/min_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `min` method that can be used to ensure that a numerical value /// meets or exceeds a specified minimum. -extension MinValidator on LucidValidationBuilder { +extension MinValidator on LucidValidationBuilder { /// Adds a validation rule that checks if a [num] value is greater than or equal to [num]. /// /// [num] is the minimum allowed value. @@ -18,8 +18,8 @@ extension MinValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'age'); /// builder.min(18); /// ``` - LucidValidationBuilder min(num num, {String message = r'Must be greater than or equal to $num', String code = 'min_value'}) { - return registerRule( + LucidValidationBuilder min(num num, {String message = r'Must be greater than or equal to $num', String code = 'min_value'}) { + return must( (value) => value >= num, message.replaceAll(r'$num', num.toString()), code, diff --git a/lib/src/validators/must_have_lowercase_validator.dart b/lib/src/validators/must_have_lowercase_validator.dart index 024da22..22abcdc 100644 --- a/lib/src/validators/must_have_lowercase_validator.dart +++ b/lib/src/validators/must_have_lowercase_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `mustHaveLowercase` method that can be used to ensure that a string /// contains at least one lowercase letter. -extension MustHaveLowercase on LucidValidationBuilder { +extension MustHaveLowercase on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] contains at least one lowercase letter. /// /// [message] is the error message returned if the validation fails. Defaults to "Must contain at least one lowercase letter". @@ -17,8 +17,8 @@ extension MustHaveLowercase on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'password'); /// builder.mustHaveLowercase(); /// ``` - LucidValidationBuilder mustHaveLowercase({String message = 'Must contain at least one lowercase letter', String code = 'must_have_lowercase'}) { - return registerRule( + LucidValidationBuilder mustHaveLowercase({String message = 'Must contain at least one lowercase letter', String code = 'must_have_lowercase'}) { + return must( (value) => RegExp(r'[a-z]').hasMatch(value), message, code, diff --git a/lib/src/validators/must_have_numbers_validator.dart b/lib/src/validators/must_have_numbers_validator.dart index 841ea5d..bf7b1bc 100644 --- a/lib/src/validators/must_have_numbers_validator.dart +++ b/lib/src/validators/must_have_numbers_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `mustHaveNumbers` method that can be used to ensure that a string /// contains at least one numeric digit. -extension MustHaveNumbers on LucidValidationBuilder { +extension MustHaveNumbers on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] contains at least one numeric digit. /// /// [message] is the error message returned if the validation fails. Defaults to "Must contain at least one numeric digit". @@ -17,8 +17,8 @@ extension MustHaveNumbers on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'password'); /// builder.mustHaveNumbers(); /// ``` - LucidValidationBuilder mustHaveNumbers({String message = 'Must contain at least one numeric digit', String code = 'must_have_numbers'}) { - return registerRule( + LucidValidationBuilder mustHaveNumbers({String message = 'Must contain at least one numeric digit', String code = 'must_have_numbers'}) { + return must( (value) => RegExp(r'[0-9]').hasMatch(value), message, code, diff --git a/lib/src/validators/must_have_special_character_validator.dart b/lib/src/validators/must_have_special_character_validator.dart index 841c082..d0e8105 100644 --- a/lib/src/validators/must_have_special_character_validator.dart +++ b/lib/src/validators/must_have_special_character_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `mustHaveSpecialCharacter` method that can be used to ensure that a string /// contains at least one special character. -extension MustHaveSpecialCharacter on LucidValidationBuilder { +extension MustHaveSpecialCharacter on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] contains at least one special character. /// /// [message] is the error message returned if the validation fails. Defaults to "Must contain at least one special character". @@ -17,8 +17,8 @@ extension MustHaveSpecialCharacter on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'password'); /// builder.mustHaveSpecialCharacter(); /// ``` - LucidValidationBuilder mustHaveSpecialCharacter({String message = 'Must contain at least one special character', String code = 'must_have_special_character'}) { - return registerRule( + LucidValidationBuilder mustHaveSpecialCharacter({String message = 'Must contain at least one special character', String code = 'must_have_special_character'}) { + return must( (value) => RegExp(r'[!@#\$%\^&\*(),.?":{}|<>]').hasMatch(value), message, code, diff --git a/lib/src/validators/must_have_uppercase_validator.dart b/lib/src/validators/must_have_uppercase_validator.dart index a0656bc..099c95f 100644 --- a/lib/src/validators/must_have_uppercase_validator.dart +++ b/lib/src/validators/must_have_uppercase_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `mustHaveUppercase` method that can be used to ensure that a string /// contains at least one uppercase letter. -extension MustHaveUppercase on LucidValidationBuilder { +extension MustHaveUppercase on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] contains at least one uppercase letter. /// /// [message] is the error message returned if the validation fails. Defaults to "Must contain at least one uppercase letter". @@ -17,8 +17,8 @@ extension MustHaveUppercase on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'password'); /// builder.mustHaveUppercase(); /// ``` - LucidValidationBuilder mustHaveUppercase({String message = 'Must contain at least one uppercase letter', String code = 'must_have_uppercase'}) { - return registerRule( + LucidValidationBuilder mustHaveUppercase({String message = 'Must contain at least one uppercase letter', String code = 'must_have_uppercase'}) { + return must( (value) => RegExp(r'[A-Z]').hasMatch(value), message, code, diff --git a/lib/src/validators/not_empty_validator.dart b/lib/src/validators/not_empty_validator.dart index f3b4471..97c8363 100644 --- a/lib/src/validators/not_empty_validator.dart +++ b/lib/src/validators/not_empty_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `notEmpty` method that can be used to ensure that a string /// is not empty. -extension NotEmptyValidator on LucidValidationBuilder { +extension NotEmptyValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is not empty. /// /// [message] is the error message returned if the validation fails. Defaults to "Cannot be empty". @@ -17,8 +17,8 @@ extension NotEmptyValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'username'); /// builder.notEmpty(); /// ``` - LucidValidationBuilder notEmpty({String message = 'Cannot be empty', String code = 'not_empty'}) { - return registerRule( + LucidValidationBuilder notEmpty({String message = 'Cannot be empty', String code = 'not_empty'}) { + return must( (value) => value.isNotEmpty, message, code, diff --git a/lib/src/validators/not_equal_validator.dart b/lib/src/validators/not_equal_validator.dart index 2a26318..3b2fa58 100644 --- a/lib/src/validators/not_equal_validator.dart +++ b/lib/src/validators/not_equal_validator.dart @@ -4,10 +4,10 @@ part of 'validators.dart'; /// /// This extension adds a `notEqualTo` method that can be used to ensure that a value /// is not equal to a specific value. -extension NotEqualValidator on LucidValidationBuilder { +extension NotEqualValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the value is not equal to [comparison]. /// - /// [comparison] is the value that the field must not match. + /// [predicate] is the value that the field must not match. /// [message] is the error message returned if the validation fails. Defaults to "Must not be equal to $comparison". /// [code] is an optional error code for translation purposes. /// @@ -18,10 +18,13 @@ extension NotEqualValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'newUsername'); /// builder.notEqualTo('oldUsername'); /// ``` - LucidValidationBuilder notEqualTo(T comparison, {String message = r'Must not be equal to $comparison', String code = 'not_equal_to_error'}) { - return registerRule( - (value) => value != comparison, - message.replaceAll(r'$comparison', comparison.toString()), + LucidValidationBuilder notEqualTo(T Function(E entity) predicate, {String message = r'Must not be equal', String code = 'not_equal_to_error'}) { + return mustWith( + (value, entity) { + final comparison = predicate(entity); + return value != comparison; + }, + message, code, ); } diff --git a/lib/src/validators/range_validator.dart b/lib/src/validators/range_validator.dart index 5489c90..509275f 100644 --- a/lib/src/validators/range_validator.dart +++ b/lib/src/validators/range_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `range` method that can be used to ensure that a number /// is within a specified range. -extension RangeValidator on LucidValidationBuilder { +extension RangeValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [num] is within the range of [min] and [max]. /// /// [min] and [max] define the acceptable range for the number. @@ -18,8 +18,8 @@ extension RangeValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'age'); /// builder.range(18, 65); /// ``` - LucidValidationBuilder range(num min, num max, {String message = r'Must be between $min and $max', String code = 'range_error'}) { - return registerRule( + LucidValidationBuilder range(num min, num max, {String message = r'Must be between $min and $max', String code = 'range_error'}) { + return must( (value) => value >= min && value <= max, message.replaceAll(r'$min', min.toString()).replaceAll('$max', max.toString()), code, diff --git a/lib/src/validators/valid_cep_validator.dart b/lib/src/validators/valid_cep_validator.dart index dec479a..f8d20c3 100644 --- a/lib/src/validators/valid_cep_validator.dart +++ b/lib/src/validators/valid_cep_validator.dart @@ -1,6 +1,6 @@ part of 'validators.dart'; -extension ValidCEPValidator on LucidValidationBuilder { +extension ValidCEPValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is a valid CEP (Brazilian postal code). /// /// This method verifies that the CEP is in the correct format (#####-###) and consists @@ -10,8 +10,8 @@ extension ValidCEPValidator on LucidValidationBuilder { /// [code] is an optional error code for translation purposes. /// /// Returns the [LucidValidationBuilder] to allow for method chaining. - LucidValidationBuilder validCEP({String message = 'Invalid CEP', String code = 'invalid_cep'}) { - return registerRule( + LucidValidationBuilder validCEP({String message = 'Invalid CEP', String code = 'invalid_cep'}) { + return must( (value) => RegExp(r'^\d{5}-?\d{3}$').hasMatch(value), message, code, diff --git a/lib/src/validators/valid_cnpj_validator.dart b/lib/src/validators/valid_cnpj_validator.dart index f31675e..47953e4 100644 --- a/lib/src/validators/valid_cnpj_validator.dart +++ b/lib/src/validators/valid_cnpj_validator.dart @@ -1,6 +1,6 @@ part of 'validators.dart'; -extension ValidCNPJValidator on LucidValidationBuilder { +extension ValidCNPJValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is a valid CNPJ number. /// /// The CNPJ is the national identifier for Brazilian companies. This method @@ -11,8 +11,8 @@ extension ValidCNPJValidator on LucidValidationBuilder { /// [code] is an optional error code for translation purposes. /// /// Returns the [LucidValidationBuilder] to allow for method chaining. - LucidValidationBuilder validCNPJ({String message = 'Invalid CNPJ', String code = 'invalid_cnpj'}) { - return registerRule( + LucidValidationBuilder validCNPJ({String message = 'Invalid CNPJ', String code = 'invalid_cnpj'}) { + return must( (value) => _validateCNPJ(value), message, code, diff --git a/lib/src/validators/valid_cpf_validator.dart b/lib/src/validators/valid_cpf_validator.dart index 3745e8b..45fd2c0 100644 --- a/lib/src/validators/valid_cpf_validator.dart +++ b/lib/src/validators/valid_cpf_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `validCPF` method that can be used to ensure that a string /// is a valid CPF number. -extension ValidCPFValidator on LucidValidationBuilder { +extension ValidCPFValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is a valid CPF number. /// /// [message] is the error message returned if the validation fails. Defaults to "Invalid CPF". @@ -17,8 +17,8 @@ extension ValidCPFValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'cpf'); /// builder.validCPF(); /// ``` - LucidValidationBuilder validCPF({String message = 'Invalid CPF', String code = 'invalid_cpf'}) { - return registerRule( + LucidValidationBuilder validCPF({String message = 'Invalid CPF', String code = 'invalid_cpf'}) { + return must( (value) => _validateCPF(value), message, code, diff --git a/lib/src/validators/valid_creditcard_validator.dart b/lib/src/validators/valid_creditcard_validator.dart index 386e617..95c7286 100644 --- a/lib/src/validators/valid_creditcard_validator.dart +++ b/lib/src/validators/valid_creditcard_validator.dart @@ -1,6 +1,6 @@ part of 'validators.dart'; -extension ValidCreditCardValidator on LucidValidationBuilder { +extension ValidCreditCardValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is a valid credit card number. /// /// This method uses the Luhn algorithm to verify the validity of a credit card number. @@ -10,8 +10,8 @@ extension ValidCreditCardValidator on LucidValidationBuilder { /// [code] is an optional error code for translation purposes. /// /// Returns the [LucidValidationBuilder] to allow for method chaining. - LucidValidationBuilder validCreditCard({String message = 'Invalid credit card number', String code = 'invalid_credit_card'}) { - return registerRule( + LucidValidationBuilder validCreditCard({String message = 'Invalid credit card number', String code = 'invalid_credit_card'}) { + return must( (value) => _validateCreditCard(value), message, code, diff --git a/lib/src/validators/valid_email_validator.dart b/lib/src/validators/valid_email_validator.dart index e318ec7..ab44021 100644 --- a/lib/src/validators/valid_email_validator.dart +++ b/lib/src/validators/valid_email_validator.dart @@ -4,7 +4,7 @@ part of 'validators.dart'; /// /// This extension adds a `validEmail` method that can be used to ensure that a string /// is a valid email address. -extension ValidEmailValidator on LucidValidationBuilder { +extension ValidEmailValidator on LucidValidationBuilder { /// Adds a validation rule that checks if the [String] is a valid email address. /// /// [message] is the error message returned if the validation fails. Defaults to "Invalid email address". @@ -17,8 +17,8 @@ extension ValidEmailValidator on LucidValidationBuilder { /// final builder = LucidValidationBuilder(key: 'email'); /// builder.validEmail(); /// ``` - LucidValidationBuilder validEmail({String message = 'Invalid email address', String code = 'invalid_email'}) { - return registerRule( + LucidValidationBuilder validEmail({String message = 'Invalid email address', String code = 'invalid_email'}) { + return must( (value) => RegExp(r'^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$').hasMatch(value), message, code, diff --git a/pubspec.yaml b/pubspec.yaml index 9ded79b..86a9e19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: "A Dart/Flutter package for building strongly typed validation rule repository: https://github.com/Flutterando/lucid_validation homepage: https://pub.dev/documentation/lucid_validation/latest/ -version: 0.0.3 +version: 0.0.4 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/test/lucid_validation_test.dart b/test/lucid_validation_test.dart index e2c8ef8..6935361 100644 --- a/test/lucid_validation_test.dart +++ b/test/lucid_validation_test.dart @@ -90,4 +90,22 @@ void main() { {'email', 'password', 'age', 'phone'}, ); }); + + test('EqualTo', () { + var credentials = CredentialsRegister( + email: 'test@test.com', + confirmPassword: '123asdASD@', + password: '123asdASD@', + ); + final registerValidator = CredentialsRegisterValidation(); + + var errors = registerValidator.validate(credentials); + + expect(errors.length, 0); + + credentials = credentials.copyWith(confirmPassword: '123asdASDsdsdw'); + errors = registerValidator.validate(credentials); + + expect(errors.length, 2); + }); } diff --git a/test/mocks/mocks.dart b/test/mocks/mocks.dart index b806e5d..4006a9e 100644 --- a/test/mocks/mocks.dart +++ b/test/mocks/mocks.dart @@ -1,3 +1,4 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:lucid_validation/lucid_validation.dart'; class UserModel { @@ -33,9 +34,9 @@ class UserValidation extends LucidValidation { } } -extension CustomValidPhoneValidator on LucidValidationBuilder { - LucidValidationBuilder customValidPhone(String message) { - return registerRule( +extension CustomValidPhoneValidator on LucidValidationBuilder { + LucidValidationBuilder customValidPhone(String message) { + return must( (value) => RegExp(r'^\(?(\d{2})\)?\s?9?\d{4}-?\d{4}$').hasMatch(value), message, 'invalid_phone_format', @@ -43,14 +44,52 @@ extension CustomValidPhoneValidator on LucidValidationBuilder { } } -extension CustomValidPasswordValidator on LucidValidationBuilder { - LucidValidationBuilder customValidPassword() { +extension CustomValidPasswordValidator on LucidValidationBuilder { + LucidValidationBuilder customValidPassword() { return notEmpty() // .minLength(5, message: 'Must be at least 8 characters long') .mustHaveLowercase() .mustHaveUppercase() .mustHaveNumbers() - .mustHaveSpecialCharacter() - .cascaded(CascadeMode.stopOnFirstFailure); + .mustHaveSpecialCharacter(); + } +} + +class CredentialsRegister { + String email; + String password; + String confirmPassword; + + CredentialsRegister({ + required this.email, + required this.password, + required this.confirmPassword, + }); + + CredentialsRegister copyWith({ + String? email, + String? password, + String? confirmPassword, + }) { + return CredentialsRegister( + email: email ?? this.email, + password: password ?? this.password, + confirmPassword: confirmPassword ?? this.confirmPassword, + ); + } +} + +class CredentialsRegisterValidation extends LucidValidation { + CredentialsRegisterValidation() { + ruleFor((credentials) => credentials.email, key: 'email') // + .notEmpty() + .validEmail(); + + ruleFor((credentials) => credentials.password, key: 'password') // + .customValidPassword() + .equalTo((entity) => entity.confirmPassword, message: 'Must be equal to confirmPassword'); + + ruleFor((credentials) => credentials.confirmPassword, key: 'confirmPassword') // + .equalTo((entity) => entity.password, message: 'Must be equal to password'); } }