From 11b63f14cd5b3df4b7f26f981069482ad1877336 Mon Sep 17 00:00:00 2001 From: laogao Date: Thu, 11 May 2023 09:17:48 +0800 Subject: [PATCH 1/2] Add RequiredNonEmptyValidator --- README.md | 1 + .../required_non_empty_validator.dart | 28 ++++++++++ lib/src/validators/required_validator.dart | 2 +- lib/src/validators/validation_message.dart | 3 ++ lib/src/validators/validators.dart | 10 +++- .../required_non_empty_validator_test.dart | 52 +++++++++++++++++++ 6 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 lib/src/validators/required_non_empty_validator.dart create mode 100644 test/src/validators/required_non_empty_validator_test.dart diff --git a/README.md b/README.md index d9f7836f..5579e952 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ There are common predefined validators, but you can implement custom validators #### FormControl - Validators.required +- Validators.requiredNonEmpty - Validators.requiredTrue - Validators.email - Validators.number diff --git a/lib/src/validators/required_non_empty_validator.dart b/lib/src/validators/required_non_empty_validator.dart new file mode 100644 index 00000000..0f6114a8 --- /dev/null +++ b/lib/src/validators/required_non_empty_validator.dart @@ -0,0 +1,28 @@ +// Copyright 2020 Joan Pablo Jimenez Milian. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +import 'package:reactive_forms/reactive_forms.dart'; + +/// Validator that requires the control's value to not be null or empty. +class RequiredNonEmptyValidator extends Validator { + @override + Map? validate(AbstractControl control) { + final error = {ValidationMessage.requiredNonEmpty: true}; + final dynamic value = control.value; + + if (value == null) { + return error; + } else if (value is String) { + return value.trim().isEmpty ? error : null; + } else if (value is Iterable && value.isEmpty) { + return error; + } else if (value is Map && value.isEmpty) { + return error; + } else if (value is Set && value.isEmpty) { + return error; + } + + return null; + } +} diff --git a/lib/src/validators/required_validator.dart b/lib/src/validators/required_validator.dart index bebfe002..c485d501 100644 --- a/lib/src/validators/required_validator.dart +++ b/lib/src/validators/required_validator.dart @@ -4,7 +4,7 @@ import 'package:reactive_forms/reactive_forms.dart'; -/// Validator that requires the control have a non-empty value. +/// Validator that requires the control's value to not be null or blank string. class RequiredValidator extends Validator { @override Map? validate(AbstractControl control) { diff --git a/lib/src/validators/validation_message.dart b/lib/src/validators/validation_message.dart index e91c2eda..736a484f 100644 --- a/lib/src/validators/validation_message.dart +++ b/lib/src/validators/validation_message.dart @@ -20,6 +20,9 @@ class ValidationMessage { /// Key text for required validation message. static const String required = 'required'; + /// Key text for `requiredNonEmpty` validation message. + static const String requiredNonEmpty = 'requiredNonEmpty'; + /// Key text for pattern validation message. static const String pattern = 'pattern'; diff --git a/lib/src/validators/validators.dart b/lib/src/validators/validators.dart index 86932c9f..f72e267a 100644 --- a/lib/src/validators/validators.dart +++ b/lib/src/validators/validators.dart @@ -21,6 +21,7 @@ import 'package:reactive_forms/src/validators/pattern/default_pattern_evaluator. import 'package:reactive_forms/src/validators/pattern/pattern_evaluator.dart'; import 'package:reactive_forms/src/validators/pattern/regex_pattern_evaluator.dart'; import 'package:reactive_forms/src/validators/pattern_validator.dart'; +import 'package:reactive_forms/src/validators/required_non_empty_validator.dart'; import 'package:reactive_forms/src/validators/required_validator.dart'; /// Signature of a function that receives a control and synchronously @@ -35,9 +36,16 @@ typedef AsyncValidatorFunction = Future?> Function( /// Provides a set of built-in validators that can be used by form controls. class Validators { - /// Gets a validator that requires the control have a non-empty value. + /// Gets a validator that requires the control's value to not be null or + /// blank string. static ValidatorFunction get required => RequiredValidator().validate; + /// Gets a validator that requires the control's value be non-empty. + /// This validator is commonly used for fields that are both required and + /// should have a non-empty value. + static ValidatorFunction get requiredNonEmpty => + RequiredNonEmptyValidator().validate; + /// Gets a validator that requires the control's value be true. /// This validator is commonly used for required checkboxes. static ValidatorFunction get requiredTrue => EqualsValidator(true, diff --git a/test/src/validators/required_non_empty_validator_test.dart b/test/src/validators/required_non_empty_validator_test.dart new file mode 100644 index 00000000..67242ed2 --- /dev/null +++ b/test/src/validators/required_non_empty_validator_test.dart @@ -0,0 +1,52 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:reactive_forms/reactive_forms.dart'; + +void main() { + group('Required Non Empty validator tests', () { + test('FormControl is invalid if value is null', () { + // Given: an invalid control + final control = FormControl( + value: null, + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is invalid + expect(control.valid, false); + expect(control.errors, {ValidationMessage.requiredNonEmpty: true}); + }); + + test('FormControl is valid if value is not null', () { + // Given: a valid control + final control = FormControl( + value: 42, + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is valid + expect(control.valid, true); + }); + + test('FormControl is invalid if value is an emtpy list', () { + // Given: a valid control + final control = FormControl>( + value: [], + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is invalid + expect(control.valid, false); + expect(control.errors, {ValidationMessage.requiredNonEmpty: true}); + }); + + test('FormControl is valid if value is non emtpy list', () { + // Given: a valid control + final control = FormControl>( + value: [1], + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is valid + expect(control.valid, true); + }); + }); +} From c262379097207f47391e4fa5c1093d5747bb8941 Mon Sep 17 00:00:00 2001 From: laogao Date: Thu, 11 May 2023 13:37:23 +0800 Subject: [PATCH 2/2] test coverage --- .../required_non_empty_validator_test.dart | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/src/validators/required_non_empty_validator_test.dart b/test/src/validators/required_non_empty_validator_test.dart index 67242ed2..7e298943 100644 --- a/test/src/validators/required_non_empty_validator_test.dart +++ b/test/src/validators/required_non_empty_validator_test.dart @@ -26,6 +26,41 @@ void main() { expect(control.valid, true); }); + test('FormControl is invalid if string value is null', () { + // Given: an invalid control + final control = FormControl( + value: null, + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is invalid + expect(control.valid, false); + expect(control.errors, {ValidationMessage.requiredNonEmpty: true}); + }); + + test('FormControl is invalid if string value is empty', () { + // Given: an invalid control + final control = FormControl( + value: " ", + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is invalid + expect(control.valid, false); + expect(control.errors, {ValidationMessage.requiredNonEmpty: true}); + }); + + test('FormControl is valid if string value is not null', () { + // Given: a valid control + final control = FormControl( + value: "42", + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is valid + expect(control.valid, true); + }); + test('FormControl is invalid if value is an emtpy list', () { // Given: a valid control final control = FormControl>( @@ -48,5 +83,28 @@ void main() { // Expect: control is valid expect(control.valid, true); }); + + test('FormControl is invalid if value is an emtpy map', () { + // Given: a valid control + final control = FormControl>( + value: {}, + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is invalid + expect(control.valid, false); + expect(control.errors, {ValidationMessage.requiredNonEmpty: true}); + }); + + test('FormControl is valid if value is non emtpy map', () { + // Given: a valid control + final control = FormControl>( + value: {"answer": 42}, + validators: [Validators.requiredNonEmpty], + ); + + // Expect: control is valid + expect(control.valid, true); + }); }); }