-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(validator): add PhoneValidator helper
- Loading branch information
Showing
4 changed files
with
242 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
library phone_number_input; | ||
|
||
export 'src/widgets/phone_form_field.dart'; | ||
export 'src/widgets/base_phone_form_field.dart'; | ||
|
||
export 'src/models/selector_config.dart'; | ||
export 'src/widgets/country_picker/country_selector.dart'; | ||
export 'src/widgets/flag_dial_code_chip.dart'; | ||
export 'src/localization/phone_field_localization.dart'; | ||
export 'package:phone_numbers_parser/phone_numbers_parser.dart' | ||
show PhoneNumber, PhoneNumberType, PhoneParser, LightPhoneParser; | ||
|
||
export 'src/localization/phone_field_localization.dart'; | ||
export 'src/models/all_countries.dart'; | ||
export 'src/models/country.dart'; | ||
export 'src/models/selector_config.dart'; | ||
export 'src/validator/phone_validator.dart'; | ||
export 'src/widgets/base_phone_form_field.dart'; | ||
export 'src/widgets/country_picker/country_selector.dart'; | ||
export 'src/widgets/country_picker/country_selector_navigator.dart'; | ||
export 'src/models/all_countries.dart'; | ||
export 'src/widgets/flag_dial_code_chip.dart'; | ||
export 'src/widgets/phone_form_field.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import 'package:dart_countries/dart_countries.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
import 'package:phone_form_field/phone_form_field.dart'; | ||
|
||
typedef PhoneNumberInputValidator = String? Function(PhoneNumber? phoneNumber); | ||
|
||
class PhoneValidator { | ||
static PhoneNumberInputValidator compose( | ||
List<PhoneNumberInputValidator> validators) { | ||
return (valueCandidate) { | ||
for (var validator in validators) { | ||
final validatorResult = validator.call(valueCandidate); | ||
if (validatorResult != null) { | ||
return validatorResult; | ||
} | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
static PhoneNumberInputValidator required<T>( | ||
BuildContext context, { | ||
String? errorText, | ||
}) { | ||
final defaultMessage = "Phone number required"; | ||
return (PhoneNumber? valueCandidate) { | ||
if (valueCandidate == null || (valueCandidate.nsn.trim().isEmpty)) { | ||
return errorText ?? | ||
PhoneFieldLocalization.of(context)?.translate(defaultMessage) ?? | ||
defaultMessage; | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
static PhoneNumberInputValidator invalid( | ||
BuildContext context, { | ||
String? errorText, | ||
bool useLightParser = false, | ||
}) { | ||
final defaultMessage = "Phone number invalid"; | ||
final parser = useLightParser ? LightPhoneParser() : PhoneParser(); | ||
return (PhoneNumber? valueCandidate) { | ||
if (valueCandidate == null || !parser.validate(valueCandidate)) { | ||
return errorText ?? | ||
PhoneFieldLocalization.of(context)?.translate(defaultMessage) ?? | ||
defaultMessage; | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
static PhoneNumberInputValidator type( | ||
BuildContext context, | ||
PhoneNumberType expectedType, { | ||
String? errorText, | ||
bool useLightParser = false, | ||
}) { | ||
final defaultMessage = expectedType == PhoneNumberType.mobile | ||
? "Invalid mobile phone number" | ||
: "Invalid landline phone number"; | ||
final parser = useLightParser ? LightPhoneParser() : PhoneParser(); | ||
return (PhoneNumber? valueCandidate) { | ||
if (valueCandidate == null || | ||
!parser.validate(valueCandidate, expectedType)) { | ||
return errorText ?? | ||
PhoneFieldLocalization.of(context)?.translate(defaultMessage) ?? | ||
defaultMessage; | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
static PhoneNumberInputValidator country( | ||
BuildContext context, | ||
List<String> expectedCountries, { | ||
String? errorText, | ||
}) { | ||
assert(expectedCountries.every((isoCode) => isoCodes.contains(isoCode)), | ||
'Each expectedCountries value be valid country isoCode'); | ||
final defaultMessage = "Invalid country"; | ||
return (PhoneNumber? valueCandidate) { | ||
if (valueCandidate == null || | ||
!expectedCountries.contains(valueCandidate.isoCode)) { | ||
return errorText ?? | ||
PhoneFieldLocalization.of(context)?.translate(defaultMessage) ?? | ||
defaultMessage; | ||
} | ||
return null; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:phone_form_field/phone_form_field.dart'; | ||
import 'package:phone_form_field/src/validator/phone_validator.dart'; | ||
|
||
void main() async { | ||
Future<BuildContext> getBuildContext(WidgetTester tester) async { | ||
await tester.pumpWidget(MaterialApp(home: Material(child: Container()))); | ||
return tester.element(find.byType(Container)); | ||
} | ||
|
||
group('PhoneValidator.compose', () { | ||
testWidgets('compose should test each validator until first failure', | ||
(WidgetTester tester) async { | ||
bool firstValidationDone = false; | ||
bool lastValidationDone = false; | ||
final validator = PhoneValidator.compose([ | ||
(PhoneNumber? p) { | ||
firstValidationDone = true; | ||
}, | ||
(PhoneNumber? p) { | ||
return "validation failed"; | ||
}, | ||
(PhoneNumber? p) { | ||
lastValidationDone = true; | ||
}, | ||
]); | ||
expect(validator(PhoneNumber(isoCode: '', nsn: '')), | ||
equals("validation failed")); | ||
expect(firstValidationDone, isTrue); | ||
expect(lastValidationDone, isFalse); | ||
}); | ||
}); | ||
|
||
group('PhoneValidator.required', () { | ||
testWidgets('should be required value', (WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
final validator = PhoneValidator.required(context); | ||
expect( | ||
validator(PhoneNumber(isoCode: 'US', nsn: '')), | ||
equals("Phone number required"), | ||
); | ||
|
||
final validatorWithText = PhoneValidator.required( | ||
context, | ||
errorText: 'custom message', | ||
); | ||
expect( | ||
validatorWithText(PhoneNumber(isoCode: 'US', nsn: '')), | ||
equals("custom message"), | ||
); | ||
}); | ||
}); | ||
|
||
group('PhoneValidator.invalid', () { | ||
testWidgets('should be invalid', (WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
final validator = PhoneValidator.invalid(context); | ||
expect( | ||
validator(PhoneNumber(isoCode: 'FR', nsn: '123')), | ||
equals("Phone number invalid"), | ||
); | ||
|
||
final validatorWithText = PhoneValidator.invalid( | ||
context, | ||
errorText: 'custom message', | ||
); | ||
expect( | ||
validatorWithText(PhoneNumber(isoCode: 'FR', nsn: '123')), | ||
equals("custom message"), | ||
); | ||
}); | ||
}); | ||
|
||
group('PhoneValidator.type', () { | ||
testWidgets('should be invalid mobile type', (WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
final validator = PhoneValidator.type(context, PhoneNumberType.mobile); | ||
expect( | ||
validator(PhoneNumber(isoCode: 'FR', nsn: '412345678')), | ||
equals("Invalid mobile phone number"), | ||
); | ||
|
||
final validatorWithText = PhoneValidator.type( | ||
context, | ||
PhoneNumberType.mobile, | ||
errorText: 'custom type message', | ||
); | ||
expect( | ||
validatorWithText(PhoneNumber(isoCode: 'FR', nsn: '412345678')), | ||
equals("custom type message"), | ||
); | ||
}); | ||
|
||
testWidgets('should be invalid landline type', (WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
final validator = PhoneValidator.type(context, PhoneNumberType.fixedLine); | ||
expect( | ||
validator(PhoneNumber(isoCode: 'FR', nsn: '612345678')), | ||
equals("Invalid landline phone number"), | ||
); | ||
|
||
final validatorWithText = PhoneValidator.type( | ||
context, | ||
PhoneNumberType.fixedLine, | ||
errorText: 'custom fixed type message', | ||
); | ||
expect( | ||
validatorWithText(PhoneNumber(isoCode: 'FR', nsn: '612345678')), | ||
equals("custom fixed type message"), | ||
); | ||
}); | ||
}); | ||
|
||
group('PhoneValidator.country', () { | ||
testWidgets('should be invalid country', (WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
final validator = PhoneValidator.country(context, ['FR', 'BE']); | ||
expect( | ||
validator(PhoneNumber(isoCode: 'US', nsn: '')), | ||
equals("Invalid country"), | ||
); | ||
}); | ||
|
||
testWidgets('country validator should refuse invalid isoCode', | ||
(WidgetTester tester) async { | ||
final context = await getBuildContext(tester); | ||
|
||
expect( | ||
() => PhoneValidator.country(context, ['INVALID_ISO_CODE']), | ||
throwsAssertionError, | ||
); | ||
}); | ||
}); | ||
} |