Commit e855dda2 authored by Mohamad Ahmadi's avatar Mohamad Ahmadi Committed by Commit Bot

[PR] Moves validation logic from the coordinators to the mediators objects.

Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Ie818accb5c56a472ce424b442d13ee0a1dfa3a95
Reviewed-on: https://chromium-review.googlesource.com/741092Reviewed-by: default avatarMarc-Antoine Courteau <macourteau@chromium.org>
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512586}
parent 7154fd06
......@@ -110,6 +110,7 @@ source_set("payments_ui") {
"payment_request_edit_view_controller.mm",
"payment_request_edit_view_controller_actions.h",
"payment_request_edit_view_controller_data_source.h",
"payment_request_edit_view_controller_validator.h",
"payment_request_editor_field.h",
"payment_request_editor_field.mm",
"payment_request_error_view_controller.h",
......
......@@ -40,7 +40,6 @@ class PaymentRequest;
// provided in the initializer.
@interface AddressEditCoordinator
: ChromeCoordinator<PaymentRequestEditViewControllerDelegate,
PaymentRequestEditViewControllerValidator,
CountrySelectionCoordinatorDelegate>
// The address to be edited, if any. This pointer is not owned by this class
......
......@@ -11,10 +11,8 @@
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/payments/core/payments_profile_comparator.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/payments/address_edit_mediator.h"
......@@ -58,12 +56,12 @@ using ::AutofillTypeFromAutofillUIType;
- (void)start {
self.editViewController = [[PaymentRequestEditViewController alloc] init];
[self.editViewController setDelegate:self];
[self.editViewController setValidatorDelegate:self];
self.mediator =
[[AddressEditMediator alloc] initWithPaymentRequest:self.paymentRequest
address:self.address];
[self.mediator setConsumer:self.editViewController];
[self.editViewController setDataSource:self.mediator];
[self.editViewController setValidatorDelegate:self.mediator];
[self.editViewController loadModel];
self.viewController = [[PaymentRequestNavigationController alloc]
......@@ -88,33 +86,6 @@ using ::AutofillTypeFromAutofillUIType;
self.viewController = nil;
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
switch (field.autofillUIType) {
case AutofillUITypeProfileHomePhoneWholeNumber: {
const std::string selectedCountryCode =
base::SysNSStringToUTF8(self.mediator.selectedCountryCode);
if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
selectedCountryCode)) {
return l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE);
}
break;
}
default:
break;
}
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - PaymentRequestEditViewControllerDelegate
- (void)paymentRequestEditViewController:
......
......@@ -6,6 +6,7 @@
#define IOS_CHROME_BROWSER_UI_PAYMENTS_ADDRESS_EDIT_MEDIATOR_H_
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_data_source.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_validator.h"
#import "ios/chrome/browser/ui/payments/region_data_loader.h"
@protocol PaymentRequestEditConsumer;
......@@ -21,6 +22,7 @@ class PaymentRequest;
// Serves as data source for AddressEditViewController.
@interface AddressEditMediator
: NSObject<PaymentRequestEditViewControllerDataSource,
PaymentRequestEditViewControllerValidator,
RegionDataLoaderConsumer>
// The consumer for this object. This can change during the lifetime of this
......
......@@ -23,6 +23,7 @@
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/phone_number_i18n.h"
#include "components/autofill/core/browser/validation.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request.h"
......@@ -181,6 +182,33 @@ NSString* NormalizeRegionName(NSString* region, NSArray<RegionData*>* regions) {
return nil;
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
switch (field.autofillUIType) {
case AutofillUITypeProfileHomePhoneWholeNumber: {
const std::string selectedCountryCode =
base::SysNSStringToUTF8(self.selectedCountryCode);
if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
selectedCountryCode)) {
return l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE);
}
break;
}
default:
break;
}
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - RegionDataLoaderConsumer
- (void)regionDataLoaderDidSucceedWithRegions:(NSArray<RegionData*>*)regions {
......
......@@ -8,6 +8,8 @@
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/payments/payment_request_unittest_base.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
#include "testing/platform_test.h"
#include "third_party/ocmock/gtest_support.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -28,6 +30,67 @@ class PaymentRequestAddressEditMediatorTest : public PaymentRequestUnitTestBase,
void TearDown() override { PaymentRequestUnitTestBase::TearDown(); }
};
// Tests that no validation error should be expected if validating an empty
// field that is not required.
TEST_F(PaymentRequestAddressEditMediatorTest, ValidateEmptyField) {
AddressEditMediator* mediator =
[[AddressEditMediator alloc] initWithPaymentRequest:payment_request()
address:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:NO];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE(!validationError);
}
// Tests that the appropriate validation error should be expected if validating
// an empty field that is required.
TEST_F(PaymentRequestAddressEditMediatorTest, ValidateEmptyRequiredField) {
AddressEditMediator* mediator =
[[AddressEditMediator alloc] initWithPaymentRequest:payment_request()
address:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE)]);
}
// Tests that the appropriate validation error should be expected if validating
// a field with an invalid value.
TEST_F(PaymentRequestAddressEditMediatorTest, ValidateFieldInvalidValue) {
AddressEditMediator* mediator =
[[AddressEditMediator alloc] initWithPaymentRequest:payment_request()
address:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@"1506853121" // Missing one last digit.
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE)]);
}
// Tests that the editor's title is correct in various situations.
TEST_F(PaymentRequestAddressEditMediatorTest, Title) {
// No address, so the title should ask to add an address.
......
......@@ -39,8 +39,7 @@ class PaymentRequest;
// controller. This view controller will be presented by the view controller
// provided in the initializer.
@interface ContactInfoEditCoordinator
: ChromeCoordinator<PaymentRequestEditViewControllerDelegate,
PaymentRequestEditViewControllerValidator>
: ChromeCoordinator<PaymentRequestEditViewControllerDelegate>
// The profile to be edited, if any. This pointer is not owned by this class
// and should outlive it.
......
......@@ -14,7 +14,6 @@
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/payments/core/payments_profile_comparator.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/payments/contact_info_edit_mediator.h"
......@@ -53,12 +52,12 @@ using ::AutofillTypeFromAutofillUIType;
- (void)start {
self.editViewController = [[PaymentRequestEditViewController alloc] init];
[self.editViewController setDelegate:self];
[self.editViewController setValidatorDelegate:self];
self.mediator = [[ContactInfoEditMediator alloc]
initWithPaymentRequest:self.paymentRequest
profile:self.profile];
[self.mediator setConsumer:self.editViewController];
[self.editViewController setDataSource:self.mediator];
[self.editViewController setValidatorDelegate:self.mediator];
[self.editViewController loadModel];
self.viewController = [[PaymentRequestNavigationController alloc]
......@@ -81,42 +80,6 @@ using ::AutofillTypeFromAutofillUIType;
self.viewController = nil;
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
switch (field.autofillUIType) {
case AutofillUITypeProfileHomePhoneWholeNumber: {
const std::string countryCode =
autofill::AutofillCountry::CountryCodeForLocale(
self.paymentRequest->GetApplicationLocale());
if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
countryCode)) {
return l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE);
}
break;
}
case AutofillUITypeProfileEmailAddress: {
if (!autofill::IsValidEmailAddress(
base::SysNSStringToUTF16(field.value))) {
return l10n_util::GetNSString(
IDS_PAYMENTS_EMAIL_INVALID_VALIDATION_MESSAGE);
}
break;
}
default:
break;
}
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - PaymentRequestEditViewControllerDelegate
- (void)paymentRequestEditViewController:
......
......@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_data_source.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_validator.h"
@protocol PaymentRequestEditConsumer;
......@@ -21,7 +22,8 @@ class PaymentRequest;
// Serves as data source for AddressEditViewController.
@interface ContactInfoEditMediator
: NSObject<PaymentRequestEditViewControllerDataSource>
: NSObject<PaymentRequestEditViewControllerDataSource,
PaymentRequestEditViewControllerValidator>
// The consumer for this object. This can change during the lifetime of this
// object and may be nil.
......
......@@ -11,6 +11,7 @@
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/phone_number_i18n.h"
#include "components/autofill/core/browser/validation.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request.h"
......@@ -104,6 +105,42 @@
return nil;
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
switch (field.autofillUIType) {
case AutofillUITypeProfileHomePhoneWholeNumber: {
const std::string countryCode =
autofill::AutofillCountry::CountryCodeForLocale(
self.paymentRequest->GetApplicationLocale());
if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
countryCode)) {
return l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE);
}
break;
}
case AutofillUITypeProfileEmailAddress: {
if (!autofill::IsValidEmailAddress(
base::SysNSStringToUTF16(field.value))) {
return l10n_util::GetNSString(
IDS_PAYMENTS_EMAIL_INVALID_VALIDATION_MESSAGE);
}
break;
}
default:
break;
}
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - Helper methods
// Creates and returns an array of editor fields.
......
......@@ -10,6 +10,7 @@
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request_test_util.h"
#import "ios/chrome/browser/payments/payment_request_unittest_base.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h"
#import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
......@@ -197,6 +198,80 @@ TEST_F(PaymentRequestContactInfoEditMediatorTest, TestFieldsRequestPhoneEmail) {
EXPECT_OCMOCK_VERIFY(consumer);
}
// Tests that no validation error should be expected if validating an empty
// field that is not required.
TEST_F(PaymentRequestContactInfoEditMediatorTest, ValidateEmptyField) {
ContactInfoEditMediator* mediator =
[[ContactInfoEditMediator alloc] initWithPaymentRequest:payment_request()
profile:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:NO];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE(!validationError);
}
// Tests that the appropriate validation error should be expected if validating
// an empty field that is required.
TEST_F(PaymentRequestContactInfoEditMediatorTest, ValidateEmptyRequiredField) {
ContactInfoEditMediator* mediator =
[[ContactInfoEditMediator alloc] initWithPaymentRequest:payment_request()
profile:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE)]);
}
// Tests that the appropriate validation error should be expected if validating
// a field with an invalid value.
TEST_F(PaymentRequestContactInfoEditMediatorTest, ValidateFieldInvalidValue) {
ContactInfoEditMediator* mediator =
[[ContactInfoEditMediator alloc] initWithPaymentRequest:payment_request()
profile:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@"1506853121" // Missing one last digit.
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE)]);
field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileEmailAddress
fieldType:EditorFieldTypeTextField
label:@""
value:@"example.com" // Invalid email address.
required:YES];
validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_EMAIL_INVALID_VALIDATION_MESSAGE)]);
}
// Tests that the editor's title is correct in various situations.
TEST_F(PaymentRequestContactInfoEditMediatorTest, Title) {
// No profile, so the title should ask to add contact details.
......
......@@ -44,8 +44,7 @@ class PaymentRequest;
@interface CreditCardEditCoordinator
: ChromeCoordinator<AddressEditCoordinatorDelegate,
BillingAddressSelectionCoordinatorDelegate,
PaymentRequestEditViewControllerDelegate,
PaymentRequestEditViewControllerValidator>
PaymentRequestEditViewControllerDelegate>
// The payment method to be edited, if any. This pointer is not owned by this
// class and should outlive it.
......
......@@ -11,13 +11,10 @@
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
#import "components/autofill/ios/browser/credit_card_util.h"
#include "components/payments/core/autofill_payment_instrument.h"
#include "components/payments/core/payment_instrument.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/payments/payment_request.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/payments/credit_card_edit_mediator.h"
......@@ -29,46 +26,6 @@
#error "This file requires ARC support."
#endif
namespace {
using ::AutofillUITypeFromAutofillType;
using ::AutofillTypeFromAutofillUIType;
// Returns true if |card_number| is a supported card type and a valid credit
// card number and no other credit card with the same number exists.
// |error_message| can't be null and will be filled with the appropriate error
// message iff the return value is false.
bool IsValidCreditCardNumber(const base::string16& card_number,
payments::PaymentRequest* payment_request,
const autofill::CreditCard* credit_card_to_edit,
base::string16* error_message) {
std::set<std::string> supported_card_networks(
payment_request->supported_card_networks().begin(),
payment_request->supported_card_networks().end());
if (!::autofill::IsValidCreditCardNumberForBasicCardNetworks(
card_number, supported_card_networks, error_message)) {
return false;
}
// Check if another credit card has already been created with this number.
// TODO(crbug.com/725604): the UI should offer to load / update the existing
// credit card info.
autofill::CreditCard* existing_card =
payment_request->GetPersonalDataManager()->GetCreditCardByNumber(
base::UTF16ToASCII(card_number));
// If a card exists, it could be the one currently being edited.
if (!existing_card || (credit_card_to_edit && credit_card_to_edit->guid() ==
existing_card->guid())) {
return true;
}
if (error_message) {
*error_message = l10n_util::GetStringUTF16(
IDS_PAYMENTS_VALIDATION_ALREADY_USED_CREDIT_CARD_NUMBER);
}
return false;
}
} // namespace
@interface CreditCardEditCoordinator ()
@property(nonatomic, assign) autofill::CreditCard* creditCard;
......@@ -105,12 +62,12 @@ bool IsValidCreditCardNumber(const base::string16& card_number,
_editViewController = [[PaymentRequestEditViewController alloc] init];
[_editViewController setDelegate:self];
[_editViewController setValidatorDelegate:self];
_mediator = [[CreditCardEditViewControllerMediator alloc]
initWithPaymentRequest:_paymentRequest
creditCard:_creditCard];
[_mediator setConsumer:_editViewController];
[_editViewController setDataSource:_mediator];
[_editViewController setValidatorDelegate:_mediator];
[_editViewController loadModel];
self.viewController = [[PaymentRequestNavigationController alloc]
......@@ -137,41 +94,6 @@ bool IsValidCreditCardNumber(const base::string16& card_number,
self.viewController = nil;
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
base::string16 errorMessage;
base::string16 valueString = base::SysNSStringToUTF16(field.value);
if (field.autofillUIType == AutofillUITypeCreditCardNumber) {
::IsValidCreditCardNumber(valueString, _paymentRequest, _creditCard,
&errorMessage);
} else if (field.autofillUIType == AutofillUITypeCreditCardExpDate) {
NSArray<NSString*>* fieldComponents =
[field.value componentsSeparatedByString:@" / "];
int expMonth = [fieldComponents[0] intValue];
int expYear = [fieldComponents[1] intValue];
if (!autofill::IsValidCreditCardExpirationDate(
expYear, expMonth, autofill::AutofillClock::Now())) {
errorMessage = l10n_util::GetStringUTF16(
IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
}
} else if (field.autofillUIType != AutofillUITypeCreditCardBillingAddress &&
field.autofillUIType != AutofillUITypeCreditCardSaveToChrome) {
autofill::IsValidForType(
valueString, AutofillTypeFromAutofillUIType(field.autofillUIType),
&errorMessage);
}
return !errorMessage.empty() ? base::SysUTF16ToNSString(errorMessage) : nil;
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - PaymentRequestEditViewControllerDelegate
- (void)paymentRequestEditViewController:
......
......@@ -6,6 +6,7 @@
#define IOS_CHROME_BROWSER_UI_PAYMENTS_CREDIT_CARD_EDIT_MEDIATOR_H_
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_data_source.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_validator.h"
@protocol PaymentRequestEditConsumer;
......@@ -20,7 +21,8 @@ class PaymentRequest;
// Serves as data source for CreditCardEditViewController.
@interface CreditCardEditViewControllerMediator
: NSObject<PaymentRequestEditViewControllerDataSource>
: NSObject<PaymentRequestEditViewControllerDataSource,
PaymentRequestEditViewControllerValidator>
// The consumer for this object. This can change during the lifetime of this
// object and may be nil.
......
......@@ -5,11 +5,13 @@
#import "ios/chrome/browser/ui/payments/credit_card_edit_mediator.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
#import "components/autofill/ios/browser/credit_card_util.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/payments/core/strings_util.h"
......@@ -17,6 +19,7 @@
#include "ios/chrome/browser/payments/payment_request.h"
#import "ios/chrome/browser/payments/payment_request_util.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/payments/cells/accepted_payment_methods_item.h"
#import "ios/chrome/browser/ui/payments/cells/payment_method_item.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h"
......@@ -32,6 +35,41 @@ namespace {
using ::autofill::data_util::GetIssuerNetworkForBasicCardIssuerNetwork;
using ::autofill::data_util::GetPaymentRequestData;
using ::payment_request_util::GetBillingAddressLabelFromAutofillProfile;
// Returns true if |card_number| is a supported card type and a valid credit
// card number and no other credit card with the same number exists.
// |error_message| can't be null and will be filled with the appropriate error
// message iff the return value is false.
bool IsValidCreditCardNumber(const base::string16& card_number,
payments::PaymentRequest* payment_request,
const autofill::CreditCard* credit_card_to_edit,
base::string16* error_message) {
std::set<std::string> supported_card_networks(
payment_request->supported_card_networks().begin(),
payment_request->supported_card_networks().end());
if (!::autofill::IsValidCreditCardNumberForBasicCardNetworks(
card_number, supported_card_networks, error_message)) {
return false;
}
// Check if another credit card has already been created with this number.
// TODO(crbug.com/725604): the UI should offer to load / update the existing
// credit card info.
autofill::CreditCard* existing_card =
payment_request->GetPersonalDataManager()->GetCreditCardByNumber(
base::UTF16ToASCII(card_number));
// If a card exists, it could be the one currently being edited.
if (!existing_card || (credit_card_to_edit && credit_card_to_edit->guid() ==
existing_card->guid())) {
return true;
}
if (error_message) {
*error_message = l10n_util::GetStringUTF16(
IDS_PAYMENTS_VALIDATION_ALREADY_USED_CREDIT_CARD_NUMBER);
}
return false;
}
} // namespace
@interface CreditCardEditViewControllerMediator ()
......@@ -186,6 +224,41 @@ using ::payment_request_util::GetBillingAddressLabelFromAutofillProfile;
return NativeImage(resourceID);
}
#pragma mark - PaymentRequestEditViewControllerValidator
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field {
if (field.value.length) {
base::string16 errorMessage;
base::string16 valueString = base::SysNSStringToUTF16(field.value);
if (field.autofillUIType == AutofillUITypeCreditCardNumber) {
::IsValidCreditCardNumber(valueString, _paymentRequest, _creditCard,
&errorMessage);
} else if (field.autofillUIType == AutofillUITypeCreditCardExpDate) {
NSArray<NSString*>* fieldComponents =
[field.value componentsSeparatedByString:@" / "];
int expMonth = [fieldComponents[0] intValue];
int expYear = [fieldComponents[1] intValue];
if (!autofill::IsValidCreditCardExpirationDate(
expYear, expMonth, autofill::AutofillClock::Now())) {
errorMessage = l10n_util::GetStringUTF16(
IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
}
} else if (field.autofillUIType != AutofillUITypeCreditCardBillingAddress &&
field.autofillUIType != AutofillUITypeCreditCardSaveToChrome) {
autofill::IsValidForType(
valueString, ::AutofillTypeFromAutofillUIType(field.autofillUIType),
&errorMessage);
}
return !errorMessage.empty() ? base::SysUTF16ToNSString(errorMessage) : nil;
} else if (field.isRequired) {
return l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
}
return nil;
}
#pragma mark - Helper methods
// Queries the month and year numbers and informs the consumer.
......
......@@ -4,9 +4,12 @@
#import "ios/chrome/browser/ui/payments/credit_card_edit_mediator.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/payments/payment_request_unittest_base.h"
#import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
#include "testing/platform_test.h"
#include "third_party/ocmock/gtest_support.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -15,12 +18,19 @@
#error "This file requires ARC support."
#endif
namespace {
const base::Time kOct2017 = base::Time::FromDoubleT(1509050356);
} // namespace
class PaymentRequestCreditCardEditMediatorTest
: public PaymentRequestUnitTestBase,
public PlatformTest {
protected:
void SetUp() override {
PaymentRequestUnitTestBase::SetUp();
AddAutofillProfile(autofill::test::GetFullProfile());
CreateTestPaymentRequest();
}
......@@ -28,6 +38,88 @@ class PaymentRequestCreditCardEditMediatorTest
void TearDown() override { PaymentRequestUnitTestBase::TearDown(); }
};
// Tests that no validation error should be expected if validating an empty
// field that is not required.
TEST_F(PaymentRequestCreditCardEditMediatorTest, ValidateEmptyField) {
CreditCardEditViewControllerMediator* mediator =
[[CreditCardEditViewControllerMediator alloc]
initWithPaymentRequest:payment_request()
creditCard:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:NO];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE(!validationError);
}
// Tests that the appropriate validation error should be expected if validating
// an empty field that is required.
TEST_F(PaymentRequestCreditCardEditMediatorTest, ValidateEmptyRequiredField) {
CreditCardEditViewControllerMediator* mediator =
[[CreditCardEditViewControllerMediator alloc]
initWithPaymentRequest:payment_request()
creditCard:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@""
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:l10n_util::GetNSString(
IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE)]);
}
// Tests that the appropriate validation error should be expected if validating
// a field with an invalid value.
TEST_F(PaymentRequestCreditCardEditMediatorTest, ValidateFieldInvalidValue) {
CreditCardEditViewControllerMediator* mediator =
[[CreditCardEditViewControllerMediator alloc]
initWithPaymentRequest:payment_request()
creditCard:nil];
EditorField* field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeCreditCardNumber
fieldType:EditorFieldTypeTextField
label:@""
value:@"411111111111111" // Missing one last digit.
required:YES];
NSString* validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:
l10n_util::GetNSString(
IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE)]);
autofill::TestAutofillClock test_clock;
test_clock.SetNow(kOct2017);
field = [[EditorField alloc]
initWithAutofillUIType:AutofillUITypeCreditCardExpDate
fieldType:EditorFieldTypeTextField
label:@""
value:@"09 / 17" // September 2017.
required:YES];
validationError =
[mediator paymentRequestEditViewController:nil
validateField:(EditorField*)field];
EXPECT_TRUE([validationError
isEqualToString:
l10n_util::GetNSString(
IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED)]);
}
// Tests that the editor's title is correct in various situations.
TEST_F(PaymentRequestCreditCardEditMediatorTest, Title) {
// No card, so the title should ask to add a card.
......
......@@ -12,6 +12,7 @@
#import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_data_source.h"
#import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller_validator.h"
extern NSString* const kWarningMessageAccessibilityID;
......@@ -40,17 +41,6 @@ extern NSString* const kWarningMessageAccessibilityID;
@end
// Validator protocol for PaymentRequestEditViewController.
@protocol PaymentRequestEditViewControllerValidator<NSObject>
// Returns the validation error string for |field|. Returns nil if there are no
// validation errors.
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field;
@end
// The collection view controller for a generic Payment Request edit screen. It
// features sections for every EditorField supplied to the initializer. Each
// section has a text field as well as an error message item which is visible
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_PAYMENTS_PAYMENT_REQUEST_EDIT_VIEW_CONTROLLER_VALIDATOR_H_
#define IOS_CHROME_BROWSER_UI_PAYMENTS_PAYMENT_REQUEST_EDIT_VIEW_CONTROLLER_VALIDATOR_H_
#import <Foundation/Foundation.h>
@class PaymentRequestEditViewController;
@class EditorField;
// Validator protocol for PaymentRequestEditViewController.
@protocol PaymentRequestEditViewControllerValidator<NSObject>
// Returns the validation error string for |field|. Returns nil if there are no
// validation errors.
- (NSString*)paymentRequestEditViewController:
(PaymentRequestEditViewController*)controller
validateField:(EditorField*)field;
@end
#endif // IOS_CHROME_BROWSER_UI_PAYMENTS_PAYMENT_REQUEST_EDIT_VIEW_CONTROLLER_VALIDATOR_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment