blob: f6c2b3792a7a2a03f733cc12a0bb1c70584ee6b1 [file] [log] [blame]
// 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.
#import "ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h"
#include <memory>
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/payments/core/autofill_payment_instrument.h"
#include "components/payments/core/payment_instrument.h"
#include "ios/chrome/browser/payments/payment_request_test_util.h"
#include "ios/chrome/browser/payments/payment_request_unittest_base.h"
#include "ios/chrome/browser/payments/test_payment_request.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
#import "ios/chrome/browser/ui/payments/payment_request_navigation_controller.h"
#import "ios/chrome/test/scoped_key_window.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
using ::testing::_;
class MockPaymentRequest : public payments::TestPaymentRequest {
public:
MockPaymentRequest(payments::WebPaymentRequest web_payment_request,
ios::ChromeBrowserState* browser_state,
web::WebState* web_state,
autofill::PersonalDataManager* personal_data_manager)
: payments::TestPaymentRequest(web_payment_request,
browser_state,
web_state,
personal_data_manager) {}
MOCK_METHOD1(
CreateAndAddAutofillPaymentInstrument,
payments::AutofillPaymentInstrument*(const autofill::CreditCard&));
MOCK_METHOD1(UpdateAutofillPaymentInstrument,
void(const autofill::CreditCard&));
};
MATCHER_P5(CreditCardMatches,
credit_card_number,
cardholder_name,
expiration_month,
expiration_year,
billing_address_id,
"") {
return arg.GetRawInfo(autofill::CREDIT_CARD_NUMBER) ==
base::ASCIIToUTF16(credit_card_number) &&
arg.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL) ==
base::ASCIIToUTF16(cardholder_name) &&
arg.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH) ==
base::ASCIIToUTF16(expiration_month) &&
arg.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR) ==
base::ASCIIToUTF16(expiration_year) &&
arg.billing_address_id() == billing_address_id;
}
NSArray<EditorField*>* GetEditorFields(bool save_card) {
return @[
[[EditorField alloc] initWithAutofillUIType:AutofillUITypeCreditCardNumber
fieldType:EditorFieldTypeTextField
label:@"Credit Card Number"
value:@"4111111111111111" /* Visa */
required:YES],
[[EditorField alloc]
initWithAutofillUIType:AutofillUITypeCreditCardHolderFullName
fieldType:EditorFieldTypeTextField
label:@"Cardholder Name"
value:@"John Doe"
required:YES],
[[EditorField alloc] initWithAutofillUIType:AutofillUITypeCreditCardExpMonth
fieldType:EditorFieldTypeTextField
label:@"Expiration Month"
value:@"12"
required:YES],
[[EditorField alloc] initWithAutofillUIType:AutofillUITypeCreditCardExpYear
fieldType:EditorFieldTypeTextField
label:@"Expiration Year"
value:@"2090"
required:YES],
[[EditorField alloc]
initWithAutofillUIType:AutofillUITypeCreditCardBillingAddress
fieldType:EditorFieldTypeSelector
label:@"Billing Address"
value:@"12345"
required:YES],
[[EditorField alloc]
initWithAutofillUIType:AutofillUITypeCreditCardSaveToChrome
fieldType:EditorFieldTypeSwitch
label:@"Save Card"
value:save_card ? @"YES" : @"NO"
required:YES],
];
}
} // namespace
class PaymentRequestCreditCardEditCoordinatorTest
: public PaymentRequestUnitTestBase,
public PlatformTest {
protected:
PaymentRequestCreditCardEditCoordinatorTest() {
personal_data_manager_.SetAutofillProfileEnabled(true);
personal_data_manager_.SetAutofillCreditCardEnabled(true);
personal_data_manager_.SetAutofillWalletImportEnabled(true);
}
void SetUp() override {
PaymentRequestUnitTestBase::SetUp();
payment_request_ = std::make_unique<MockPaymentRequest>(
payment_request_test_util::CreateTestWebPaymentRequest(),
browser_state(), web_state(), &personal_data_manager_);
}
void TearDown() override { PaymentRequestUnitTestBase::TearDown(); }
autofill::TestPersonalDataManager personal_data_manager_;
std::unique_ptr<MockPaymentRequest> payment_request_;
};
// Tests that invoking start and stop on the coordinator presents and dismisses
// the credit card edit view controller, respectively.
TEST_F(PaymentRequestCreditCardEditCoordinatorTest, StartAndStop) {
UIViewController* base_view_controller = [[UIViewController alloc] init];
ScopedKeyWindow scoped_key_window_;
[scoped_key_window_.Get() setRootViewController:base_view_controller];
CreditCardEditCoordinator* coordinator = [[CreditCardEditCoordinator alloc]
initWithBaseViewController:base_view_controller];
[coordinator setPaymentRequest:payment_request_.get()];
EXPECT_EQ(nil, base_view_controller.presentedViewController);
[coordinator start];
// Spin the run loop to trigger the animation.
base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
EXPECT_TRUE([base_view_controller.presentedViewController
isMemberOfClass:[PaymentRequestNavigationController class]]);
PaymentRequestNavigationController* navigation_controller =
base::mac::ObjCCastStrict<PaymentRequestNavigationController>(
base_view_controller.presentedViewController);
EXPECT_TRUE([navigation_controller.visibleViewController
isMemberOfClass:[PaymentRequestEditViewController class]]);
[coordinator stop];
// Wait until the animation completes and the presented view controller is
// dismissed.
base::test::ios::WaitUntilCondition(^bool() {
return !base_view_controller.presentedViewController;
});
EXPECT_EQ(nil, base_view_controller.presentedViewController);
}
// Tests that calling the view controller delegate method which signals that the
// user has finished creating a new payment method, causes the payment method to
// be added to the PaymentRequest instance and the corresponding coordinator
// delegate method to get called. The new payment method is expected to get
// added to the PersonalDataManager if user chooses to save it locally.
TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishCreatingWithSave) {
UIViewController* base_view_controller = [[UIViewController alloc] init];
ScopedKeyWindow scoped_key_window_;
[scoped_key_window_.Get() setRootViewController:base_view_controller];
CreditCardEditCoordinator* coordinator = [[CreditCardEditCoordinator alloc]
initWithBaseViewController:base_view_controller];
[coordinator setPaymentRequest:payment_request_.get()];
// Mock the coordinator delegate.
id delegate = [OCMockObject
mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
[[delegate expect]
creditCardEditCoordinator:coordinator
didFinishEditingPaymentMethod:static_cast<
payments::AutofillPaymentInstrument*>(
[OCMArg anyPointer])];
[coordinator setDelegate:delegate];
EXPECT_EQ(nil, base_view_controller.presentedViewController);
[coordinator start];
// Spin the run loop to trigger the animation.
base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
EXPECT_NE(nil, base_view_controller.presentedViewController);
// Expect a payment method to be added to the PaymentRequest.
EXPECT_CALL(*payment_request_,
CreateAndAddAutofillPaymentInstrument(CreditCardMatches(
"4111111111111111", "John Doe", "12", "2090", "12345")))
.Times(1);
// No payment method should get updated in the PaymentRequest.
EXPECT_CALL(*payment_request_, UpdateAutofillPaymentInstrument(_)).Times(0);
// Call the controller delegate method.
EXPECT_TRUE([base_view_controller.presentedViewController
isMemberOfClass:[PaymentRequestNavigationController class]]);
PaymentRequestNavigationController* navigation_controller =
base::mac::ObjCCastStrict<PaymentRequestNavigationController>(
base_view_controller.presentedViewController);
PaymentRequestEditViewController* view_controller =
base::mac::ObjCCastStrict<PaymentRequestEditViewController>(
navigation_controller.visibleViewController);
[coordinator paymentRequestEditViewController:view_controller
didFinishEditingFields:GetEditorFields(true)];
EXPECT_OCMOCK_VERIFY(delegate);
}
// Tests that calling the view controller delegate method which signals that the
// user has finished creating a new payment method, causes the payment method to
// be added to the PaymentRequest instance and the corresponding coordinator
// delegate method to get called. The new payment method should not get added to
// the PersonalDataManager if user chooses to not to save it locally.
TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishCreatingNoSave) {
UIViewController* base_view_controller = [[UIViewController alloc] init];
ScopedKeyWindow scoped_key_window_;
[scoped_key_window_.Get() setRootViewController:base_view_controller];
CreditCardEditCoordinator* coordinator = [[CreditCardEditCoordinator alloc]
initWithBaseViewController:base_view_controller];
[coordinator setPaymentRequest:payment_request_.get()];
// Mock the coordinator delegate.
id delegate = [OCMockObject
mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
[[delegate expect]
creditCardEditCoordinator:coordinator
didFinishEditingPaymentMethod:static_cast<
payments::AutofillPaymentInstrument*>(
[OCMArg anyPointer])];
[coordinator setDelegate:delegate];
EXPECT_EQ(nil, base_view_controller.presentedViewController);
[coordinator start];
// Spin the run loop to trigger the animation.
base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
EXPECT_NE(nil, base_view_controller.presentedViewController);
// No payment method should get added to the PaymentRequest.
EXPECT_CALL(*payment_request_, CreateAndAddAutofillPaymentInstrument(_))
.Times(0);
// No payment method should get updated in the PaymentRequest.
EXPECT_CALL(*payment_request_, UpdateAutofillPaymentInstrument(_)).Times(0);
// Call the controller delegate method.
EXPECT_TRUE([base_view_controller.presentedViewController
isMemberOfClass:[PaymentRequestNavigationController class]]);
PaymentRequestNavigationController* navigation_controller =
base::mac::ObjCCastStrict<PaymentRequestNavigationController>(
base_view_controller.presentedViewController);
PaymentRequestEditViewController* view_controller =
base::mac::ObjCCastStrict<PaymentRequestEditViewController>(
navigation_controller.visibleViewController);
[coordinator paymentRequestEditViewController:view_controller
didFinishEditingFields:GetEditorFields(false)];
EXPECT_OCMOCK_VERIFY(delegate);
}
// Tests that calling the view controller delegate method which signals that the
// user has finished editing a payment method, causes the corresponding
// coordinator delegate method to get called. The payment method should not get
// re-added to the PaymentRequest nor the PersonalDataManager.
TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishEditing) {
UIViewController* base_view_controller = [[UIViewController alloc] init];
ScopedKeyWindow scoped_key_window_;
[scoped_key_window_.Get() setRootViewController:base_view_controller];
CreditCardEditCoordinator* coordinator = [[CreditCardEditCoordinator alloc]
initWithBaseViewController:base_view_controller];
[coordinator setPaymentRequest:payment_request_.get()];
// Set the payment method to be edited.
autofill::CreditCard credit_card;
payments::AutofillPaymentInstrument payment_method(
"", credit_card, false, payment_request_->billing_profiles(), "", nil);
[coordinator setPaymentMethod:&payment_method];
// Mock the coordinator delegate.
id delegate = [OCMockObject
mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
[[delegate expect]
creditCardEditCoordinator:coordinator
didFinishEditingPaymentMethod:static_cast<
payments::AutofillPaymentInstrument*>(
[OCMArg anyPointer])];
[coordinator setDelegate:delegate];
EXPECT_EQ(nil, base_view_controller.presentedViewController);
[coordinator start];
// Spin the run loop to trigger the animation.
base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
EXPECT_NE(nil, base_view_controller.presentedViewController);
// No payment method should get added to the PaymentRequest.
EXPECT_CALL(*payment_request_, CreateAndAddAutofillPaymentInstrument(_))
.Times(0);
// No payment method should get updated in the PaymentRequest.
EXPECT_CALL(*payment_request_,
UpdateAutofillPaymentInstrument(CreditCardMatches(
"4111111111111111", "John Doe", "12", "2090", "12345")))
.Times(1);
// Call the controller delegate method.
EXPECT_TRUE([base_view_controller.presentedViewController
isMemberOfClass:[PaymentRequestNavigationController class]]);
PaymentRequestNavigationController* navigation_controller =
base::mac::ObjCCastStrict<PaymentRequestNavigationController>(
base_view_controller.presentedViewController);
PaymentRequestEditViewController* view_controller =
base::mac::ObjCCastStrict<PaymentRequestEditViewController>(
navigation_controller.visibleViewController);
[coordinator paymentRequestEditViewController:view_controller
didFinishEditingFields:GetEditorFields(true)];
EXPECT_OCMOCK_VERIFY(delegate);
}
// Tests that calling the view controller delegate method which signals that the
// user has chosen to cancel creating/editing a payment method, causes the
// corresponding coordinator delegate method to get called.
TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidCancel) {
UIViewController* base_view_controller = [[UIViewController alloc] init];
ScopedKeyWindow scoped_key_window_;
[scoped_key_window_.Get() setRootViewController:base_view_controller];
CreditCardEditCoordinator* coordinator = [[CreditCardEditCoordinator alloc]
initWithBaseViewController:base_view_controller];
[coordinator setPaymentRequest:payment_request_.get()];
// Mock the coordinator delegate.
id delegate = [OCMockObject
mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
[[delegate expect] creditCardEditCoordinatorDidCancel:coordinator];
[coordinator setDelegate:delegate];
EXPECT_EQ(nil, base_view_controller.presentedViewController);
[coordinator start];
// Spin the run loop to trigger the animation.
base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
EXPECT_NE(nil, base_view_controller.presentedViewController);
// Call the controller delegate method.
EXPECT_TRUE([base_view_controller.presentedViewController
isMemberOfClass:[PaymentRequestNavigationController class]]);
PaymentRequestNavigationController* navigation_controller =
base::mac::ObjCCastStrict<PaymentRequestNavigationController>(
base_view_controller.presentedViewController);
PaymentRequestEditViewController* view_controller =
base::mac::ObjCCastStrict<PaymentRequestEditViewController>(
navigation_controller.visibleViewController);
[coordinator paymentRequestEditViewControllerDidCancel:view_controller];
EXPECT_OCMOCK_VERIFY(delegate);
}