Skip to content

Commit afcd27e

Browse files
committed
fix: add tests for nullable
1 parent fd44bcd commit afcd27e

32 files changed

+494
-5
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ Here’s a complete list of available validators you can use:
125125
- **inclusiveBetween**: Checks if the datetime value is between two datetime values, including both bounds.
126126
- **exclusiveBetween**: Checks if the datetime value is between two datetime values, excluding both bounds.
127127

128+
**Observation: for almost all validators, there is an equivalent with the `OrNull` suffix. Example: `validEmailOrNull`**
129+
128130
## Usage with Flutter
129131

130132
If you’re using the `lucid_validation` package in a Flutter app, integrating with `TextFormField` is straightforward.
@@ -200,7 +202,7 @@ Example:
200202
```dart
201203
ruleFor((user) => user.phoneNumber, key: 'phoneNumber')
202204
.when((user) => user.requiresPhoneNumber)
203-
.must((value) => value.isNotEmpty, 'Phone number is required', 'phone_required')
205+
.isEmpty()
204206
.must((value) => value.length == 10, 'Phone number must be 10 digits', 'phone_length');
205207
```
206208

example/pubspec.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ packages:
126126
path: ".."
127127
relative: true
128128
source: path
129-
version: "1.0.1"
129+
version: "1.1.0"
130130
matcher:
131131
dependency: transitive
132132
description:

lib/src/validations/is_not_null_validation.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ part of 'validations.dart';
44
///
55
/// This extension adds an `isNotNull` method that can be used to ensure that a value
66
/// is not null.
7-
extension IsNotNullValidation<T> on SimpleValidationBuilder<T?> {
7+
extension IsNotNullValidation<T extends Object> on SimpleValidationBuilder<T?> {
88
/// Adds a validation rule that checks if the value is not null.
99
///
1010
/// [message] is the error message returned if the validation fails.
@@ -22,7 +22,7 @@ extension IsNotNullValidation<T> on SimpleValidationBuilder<T?> {
2222
/// String format args:
2323
/// - **{PropertyName}**: The name of the property.
2424
///
25-
SimpleValidationBuilder<String> isNotNull({String? message, String? code}) {
25+
SimpleValidationBuilder<T> isNotNull({String? message, String? code}) {
2626
return useNotNull(
2727
(value, entity) {
2828
if (value != null) return null;

lib/src/validations/range_validation.dart

+93
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,96 @@ extension RangeValidation on SimpleValidationBuilder<num> {
4949
);
5050
}
5151
}
52+
53+
extension RangeNullableValidation on SimpleValidationBuilder<num?> {
54+
/// Adds a validation rule that checks if the [num?] is within the range of [min] and [max].
55+
///
56+
/// [min] and [max] define the acceptable range for the number.
57+
/// [message] is the error message returned if the validation fails. Defaults to "Must be between $min and $max".
58+
/// [code] is an optional error code for translation purposes.
59+
///
60+
/// Returns the [LucidValidationBuilder] to allow for method chaining.
61+
///
62+
/// Example:
63+
/// ```dart
64+
/// ...
65+
/// ruleFor((user) => user.age, key: 'age') // user.age is nullable
66+
/// .range(18, 65);
67+
/// ```
68+
///
69+
/// String format args:
70+
/// - **{PropertyName}**: The name of the property.
71+
/// - **{From}**: The minimum value of the range.
72+
/// - **{To}**: The maximum value of the range.
73+
/// - **{PropertyValue}**: The value of the property.
74+
///
75+
SimpleValidationBuilder<num?> range(num min, num max,
76+
{String? message, String? code}) {
77+
return use(
78+
(value, entity) {
79+
if (value != null && value >= min && value <= max) return null;
80+
81+
final currentCode = code ?? Language.code.range;
82+
final currentMessage = LucidValidation.global.languageManager.translate(
83+
currentCode,
84+
parameters: {
85+
'PropertyName': label.isNotEmpty ? label : key,
86+
'From': '$min',
87+
'To': '$max',
88+
'PropertyValue': '$value',
89+
},
90+
defaultMessage: message,
91+
);
92+
93+
return ValidationException(message: currentMessage, code: currentCode);
94+
},
95+
);
96+
}
97+
}
98+
99+
extension RangeOrNullableValidation on SimpleValidationBuilder<num?> {
100+
/// Adds a validation rule that checks if the [num?] is within the range of [min] and [max].
101+
///
102+
/// [min] and [max] define the acceptable range for the number.
103+
/// [message] is the error message returned if the validation fails. Defaults to "Must be between $min and $max".
104+
/// [code] is an optional error code for translation purposes.
105+
///
106+
/// Returns the [LucidValidationBuilder] to allow for method chaining.
107+
///
108+
/// Example:
109+
/// ```dart
110+
/// ...
111+
/// ruleFor((user) => user.age, key: 'age') // user.age is nullable
112+
/// .range(18, 65);
113+
/// ```
114+
///
115+
/// String format args:
116+
/// - **{PropertyName}**: The name of the property.
117+
/// - **{From}**: The minimum value of the range.
118+
/// - **{To}**: The maximum value of the range.
119+
/// - **{PropertyValue}**: The value of the property.
120+
///
121+
SimpleValidationBuilder<num?> rangeOrNull(num min, num max,
122+
{String? message, String? code}) {
123+
return use(
124+
(value, entity) {
125+
if (value == null) return null;
126+
if (value >= min && value <= max) return null;
127+
128+
final currentCode = code ?? Language.code.range;
129+
final currentMessage = LucidValidation.global.languageManager.translate(
130+
currentCode,
131+
parameters: {
132+
'PropertyName': label.isNotEmpty ? label : key,
133+
'From': '$min',
134+
'To': '$max',
135+
'PropertyValue': '$value',
136+
},
137+
defaultMessage: message,
138+
);
139+
140+
return ValidationException(message: currentMessage, code: currentCode);
141+
},
142+
);
143+
}
144+
}

lib/src/validations/valid_cep_validation.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ extension ValidCEPNullableValidation on SimpleValidationBuilder<String?> {
6262
///
6363
SimpleValidationBuilder<String?> validCEP({String? message, String? code}) {
6464
return use((value, entity) {
65-
if (value != null && RegExp(r'^\d{5}-?\d{3}$').hasMatch(value))
65+
if (value != null && RegExp(r'^\d{5}-?\d{3}$').hasMatch(value)) {
6666
return null;
67+
}
6768

6869
final currentCode = code ?? Language.code.validCEP;
6970
final currentMessage = LucidValidation.global.languageManager.translate(

test/mocks/mocks.dart

+31
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ class UserModel {
1111
String? cpf = '';
1212
}
1313

14+
class UserNullableModel {
15+
String? email = '';
16+
String? password = '';
17+
String? confirmPassword = '';
18+
String? description;
19+
int? age = 0;
20+
String phone = '';
21+
String? cpf = '';
22+
}
23+
1424
class UserValidator extends LucidValidator<UserModel> {
1525
UserValidator() {
1626
ruleFor((user) => user.email, key: 'email') //
@@ -107,6 +117,11 @@ class Address {
107117
});
108118
}
109119

120+
class AddressNullable {
121+
String? country;
122+
String? postcode;
123+
}
124+
110125
class AddressValidator extends LucidValidator<Address> {
111126
AddressValidator() {
112127
ruleFor((address) => address.country, key: 'country') //
@@ -129,6 +144,12 @@ class Customer {
129144
});
130145
}
131146

147+
class CustomerNullable {
148+
String? name;
149+
Address? address;
150+
String? cnpj;
151+
}
152+
132153
class CustomerValidator extends LucidValidator<Customer> {
133154
final addressValidator = AddressValidator();
134155

@@ -151,10 +172,20 @@ class EventModel {
151172
DateTime dateEvent = DateTime.now();
152173
}
153174

175+
class EventNullableModel {
176+
DateTime? start = DateTime.now();
177+
DateTime? end = DateTime.now();
178+
DateTime? dateEvent = DateTime.now();
179+
}
180+
154181
class CreditCardModel {
155182
String number = '';
156183
}
157184

185+
class CreditCardNullableModel {
186+
String? number;
187+
}
188+
158189
class CreditCardValidator extends LucidValidator<CreditCardModel> {
159190
CreditCardValidator() {
160191
ruleFor((card) => card.number, key: 'number') //

test/src/validations/exclusive_between_datetime_validation_test.dart

+18
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,22 @@ void main() {
6666

6767
expect(result.isValid, true);
6868
});
69+
70+
test(
71+
'must return valid exclusively between validation when the event date is null',
72+
() {
73+
final validator = TestLucidValidator<EventNullableModel>();
74+
final now = DateTime.now();
75+
final afterTomorrow = now.add(Duration(days: 2));
76+
77+
validator
78+
.ruleFor((event) => event.dateEvent, key: 'dateEvent') //
79+
.exclusiveBetweenOrNull(start: now, end: afterTomorrow);
80+
81+
final event = EventNullableModel()..dateEvent = null;
82+
83+
final result = validator.validate(event);
84+
85+
expect(result.isValid, true);
86+
});
6987
}

test/src/validations/greater_than_datetime_validation_test.dart

+15
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,19 @@ void main() {
2121
expect(result.exceptions.first.message,
2222
"'start' must be greater than date '${now.toString()}'.");
2323
});
24+
25+
test('greater than validation or null ...', () {
26+
final validator = TestLucidValidator<EventNullableModel>();
27+
final now = DateTime.now();
28+
29+
validator
30+
.ruleFor((event) => event.start, key: 'start') //
31+
.greaterThanOrNull(now);
32+
33+
final event = EventNullableModel()..start = null;
34+
35+
final result = validator.validate(event);
36+
37+
expect(result.isValid, true);
38+
});
2439
}

test/src/validations/greater_than_or_equal_to_datetime_validation_test.dart

+15
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,19 @@ void main() {
3636

3737
expect(result.isValid, true);
3838
});
39+
40+
test('greater than or equal to validation or null ...', () {
41+
final validator = TestLucidValidator<EventNullableModel>();
42+
final now = DateTime.now();
43+
44+
validator
45+
.ruleFor((event) => event.start, key: 'start') //
46+
.greaterThanOrEqualToOrNull(now);
47+
48+
final event = EventNullableModel()..start = null;
49+
50+
final result = validator.validate(event);
51+
52+
expect(result.isValid, true);
53+
});
3954
}

test/src/validations/greater_than_validation_test.dart

+13
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,17 @@ void main() {
1818
expect(result.exceptions.length, 1);
1919
expect(result.exceptions.first.message, "'age' must be greater than '17'.");
2020
});
21+
22+
test('greater than validation or null ...', () {
23+
final validator = TestLucidValidator<UserNullableModel>();
24+
validator
25+
.ruleFor((user) => user.age, key: 'age') //
26+
.greaterThanOrNull(17);
27+
28+
final user = UserNullableModel()..age = null;
29+
30+
final result = validator.validate(user);
31+
32+
expect(result.isValid, true);
33+
});
2134
}

test/src/validations/inclusive_between_datetime_validation_test.dart

+17
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,21 @@ void main() {
2323
expect(result.exceptions.first.message,
2424
"'dateEvent' must be greater than or equal to '${tomorrow.toString()}' date and less than or equal to '${afterTomorrow.toString()}' date.");
2525
});
26+
27+
test('inclusive between validation or null...', () {
28+
final validator = TestLucidValidator<EventNullableModel>();
29+
final now = DateTime.now();
30+
final tomorrow = now.add(Duration(days: 1));
31+
final afterTomorrow = now.add(Duration(days: 2));
32+
33+
validator
34+
.ruleFor((event) => event.dateEvent, key: 'dateEvent') //
35+
.inclusiveBetweenOrNull(start: tomorrow, end: afterTomorrow);
36+
37+
final event = EventNullableModel()..dateEvent = null;
38+
39+
final result = validator.validate(event);
40+
41+
expect(result.isValid, true);
42+
});
2643
}

test/src/validations/is_empty_validation_test.dart

+13
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,17 @@ void main() {
2020

2121
expect(result.exceptions.first.message, "'email' must be empty.");
2222
});
23+
24+
test('is empty validation or null...', () {
25+
final validator = TestLucidValidator<UserNullableModel>();
26+
validator
27+
.ruleFor((user) => user.email, key: 'email') //
28+
.isEmptyOrNull();
29+
30+
final user = UserNullableModel()..email = null;
31+
32+
final result = validator.validate(user);
33+
34+
expect(result.isValid, true);
35+
});
2336
}

test/src/validations/is_not_null_validation_test.dart

+14
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,18 @@ void main() {
2222

2323
expect(error.message, r"'description' must not be null.");
2424
});
25+
26+
test('is not null validation is valid...', () {
27+
final validator = TestLucidValidator<UserModel>();
28+
validator
29+
.ruleFor((e) => e.description, key: 'description') //
30+
.isNotNull()
31+
.maxLength(4);
32+
33+
final user = UserModel()..description = 'desc';
34+
35+
final result = validator.validate(user);
36+
37+
expect(result.isValid, true);
38+
});
2539
}

test/src/validations/less_than_datetime_validation_test.dart

+15
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,19 @@ void main() {
2222
expect(result.exceptions.first.message,
2323
"'end' must be less than date '${now.toString()}'.");
2424
});
25+
26+
test('less than validation ...', () {
27+
final validator = TestLucidValidator<EventNullableModel>();
28+
final now = DateTime.now();
29+
30+
validator
31+
.ruleFor((event) => event.end, key: 'end') //
32+
.lessThanOrNull(now);
33+
34+
final event = EventNullableModel()..end = null;
35+
36+
final result = validator.validate(event);
37+
38+
expect(result.isValid, true);
39+
});
2540
}

test/src/validations/less_than_or_equal_to_datetime_validation_test.dart

+15
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,19 @@ void main() {
3737

3838
expect(result.isValid, true);
3939
});
40+
41+
test('less than or equal to validation or null ...', () {
42+
final validator = TestLucidValidator<EventNullableModel>();
43+
final now = DateTime.now();
44+
45+
validator
46+
.ruleFor((event) => event.end, key: 'end') //
47+
.lessThanOrEqualToOrNull(now);
48+
49+
final event = EventNullableModel()..end = null;
50+
51+
final result = validator.validate(event);
52+
53+
expect(result.isValid, true);
54+
});
4055
}

0 commit comments

Comments
 (0)