[VCN] Add card info chip to the unmask cvc prompt
Mock: https://docs.google.com/presentation/d/1LZHzYcuTaVMZ7h3H71k3NZ2hB1VZMbuPbaBaGAqlCug/edit?pli=1&resourcekey=0-MBuDkOkUtHPUtAcPN9VPKg#slide=id.g219780aa2d0_0_1 Implementation: https://screenshot.googleplex.com/BevdsXaVnAYYyFM Uses the TableViewDetailIconItem. The main text will always be the card product/network name, while the detail text will be the expiration date of the card if it is an actual card, or "Virtual card" if it is a virtual card. The icon will use card art image icon first then the static network icon. Bug: b/303715882 Change-Id: I070322cf19cc4f5fbdd788961908158089319277 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5255821 Commit-Queue: Siyu An <siyua@chromium.org> Reviewed-by: Tommy Martino <tmartino@chromium.org> Reviewed-by: Slobodan Pejic <slobodan@chromium.org> Cr-Commit-Position: refs/heads/main@{#1255846}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a363830a97
commit
bc7b9a4bdc
components/autofill/core/browser/ui/payments
card_unmask_prompt_controller.hcard_unmask_prompt_controller_impl.cccard_unmask_prompt_controller_impl.h
ios/chrome/browser/ui/autofill
@ -38,6 +38,9 @@ class CardUnmaskPromptController {
|
||||
virtual std::u16string GetOkButtonLabel() const = 0;
|
||||
virtual int GetCvcImageRid() const = 0;
|
||||
virtual bool ShouldRequestExpirationDate() const = 0;
|
||||
// TODO(b/303715882): Should consider removing these detailed information
|
||||
// accessors and instead return the credit card object directly. Only exposing
|
||||
// necessary information is good but this list is growing larger.
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
virtual Suggestion::Icon GetCardIcon() const = 0;
|
||||
virtual std::u16string GetCardName() const = 0;
|
||||
@ -52,6 +55,7 @@ class CardUnmaskPromptController {
|
||||
virtual base::TimeDelta GetSuccessMessageDuration() const = 0;
|
||||
virtual AutofillClient::PaymentsRpcResult GetVerificationResult() const = 0;
|
||||
virtual bool IsVirtualCard() const = 0;
|
||||
virtual const CreditCard& GetCreditCard() const = 0;
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
virtual int GetCvcTooltipResourceId() = 0;
|
||||
#endif
|
||||
|
@ -439,6 +439,10 @@ bool CardUnmaskPromptControllerImpl::IsVirtualCard() const {
|
||||
return card_.record_type() == CreditCard::RecordType::kVirtualCard;
|
||||
}
|
||||
|
||||
const CreditCard& CardUnmaskPromptControllerImpl::GetCreditCard() const {
|
||||
return card_;
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
int CardUnmaskPromptControllerImpl::GetCvcTooltipResourceId() {
|
||||
return IsCvcInFront()
|
||||
|
@ -86,6 +86,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController {
|
||||
base::TimeDelta GetSuccessMessageDuration() const override;
|
||||
AutofillClient::PaymentsRpcResult GetVerificationResult() const override;
|
||||
bool IsVirtualCard() const override;
|
||||
const CreditCard& GetCreditCard() const override;
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
int GetCvcTooltipResourceId() override;
|
||||
#endif
|
||||
|
@ -84,6 +84,7 @@ source_set("bridges") {
|
||||
"//base",
|
||||
"//components/autofill/core/browser",
|
||||
"//components/strings",
|
||||
"//ios/chrome/browser/autofill/model/credit_card:credit_card",
|
||||
"//ios/chrome/browser/net/model:crurl",
|
||||
"//ios/chrome/browser/shared/ui/list_model",
|
||||
"//ios/chrome/browser/shared/ui/table_view:utils",
|
||||
@ -91,6 +92,7 @@ source_set("bridges") {
|
||||
"//ios/chrome/browser/shared/ui/util",
|
||||
"//ios/chrome/browser/ui/autofill/cells",
|
||||
"//ios/chrome/common/ui/colors",
|
||||
"//ios/chrome/common/ui/table_view:cells_constants",
|
||||
"//ui/base",
|
||||
"//url",
|
||||
]
|
||||
|
@ -12,16 +12,20 @@
|
||||
@class CardUnmaskPromptViewController;
|
||||
@class UIViewController;
|
||||
@class UINavigationController;
|
||||
@class CreditCardData;
|
||||
@class UIImage;
|
||||
|
||||
namespace autofill {
|
||||
|
||||
class CardUnmaskPromptController;
|
||||
class PersonalDataManager;
|
||||
|
||||
// iOS implementation of the unmask prompt UI.
|
||||
class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
|
||||
public:
|
||||
CardUnmaskPromptViewBridge(CardUnmaskPromptController* controller,
|
||||
UIViewController* base_view_controller);
|
||||
UIViewController* base_view_controller,
|
||||
PersonalDataManager* personal_data_manager);
|
||||
CardUnmaskPromptViewBridge(const CardUnmaskPromptViewBridge&) = delete;
|
||||
CardUnmaskPromptViewBridge& operator=(const CardUnmaskPromptViewBridge&) =
|
||||
delete;
|
||||
@ -44,6 +48,8 @@ class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
|
||||
// This call destroys `this`.
|
||||
void NavigationControllerDismissed();
|
||||
|
||||
CreditCardData* credit_card_data() { return credit_card_data_; }
|
||||
|
||||
protected:
|
||||
// The presented UINavigationController containing `prompt_view_controller_`.
|
||||
UINavigationController* navigation_controller_;
|
||||
@ -59,9 +65,15 @@ class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
|
||||
// dismissing its own UI elements.
|
||||
void DeleteSelf();
|
||||
|
||||
UIImage* GetCardIcon();
|
||||
|
||||
// Weak reference to the view controller used to present UI.
|
||||
__weak UIViewController* base_view_controller_;
|
||||
|
||||
raw_ptr<PersonalDataManager> personal_data_manager_;
|
||||
|
||||
CreditCardData* credit_card_data_;
|
||||
|
||||
base::WeakPtrFactory<CardUnmaskPromptViewBridge> weak_ptr_factory_;
|
||||
};
|
||||
|
||||
|
@ -4,9 +4,14 @@
|
||||
|
||||
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
|
||||
|
||||
#import "base/apple/foundation_util.h"
|
||||
#import "base/notreached.h"
|
||||
#import "base/strings/sys_string_conversions.h"
|
||||
#import "components/autofill/core/browser/data_model/credit_card.h"
|
||||
#import "components/autofill/core/browser/personal_data_manager.h"
|
||||
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
|
||||
#import "ios/chrome/browser/autofill/model/credit_card/credit_card_data.h"
|
||||
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
|
||||
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.h"
|
||||
|
||||
namespace autofill {
|
||||
@ -15,11 +20,17 @@ namespace autofill {
|
||||
|
||||
CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge(
|
||||
CardUnmaskPromptController* controller,
|
||||
UIViewController* base_view_controller)
|
||||
UIViewController* base_view_controller,
|
||||
PersonalDataManager* personal_data_manager)
|
||||
: controller_(controller),
|
||||
base_view_controller_(base_view_controller),
|
||||
personal_data_manager_(personal_data_manager),
|
||||
weak_ptr_factory_(this) {
|
||||
DCHECK(controller_);
|
||||
CHECK(controller_);
|
||||
CHECK(personal_data_manager_);
|
||||
credit_card_data_ =
|
||||
[[CreditCardData alloc] initWithCreditCard:&(controller_->GetCreditCard())
|
||||
icon:GetCardIcon()];
|
||||
}
|
||||
|
||||
CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() {
|
||||
@ -100,4 +111,21 @@ void CardUnmaskPromptViewBridge::DeleteSelf() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
UIImage* CardUnmaskPromptViewBridge::GetCardIcon() {
|
||||
// Firstly check if card art image is available.
|
||||
const CreditCard& credit_card = GetController()->GetCreditCard();
|
||||
gfx::Image* image = personal_data_manager_->GetCreditCardArtImageForUrl(
|
||||
credit_card.card_art_url());
|
||||
if (image) {
|
||||
return image->ToUIImage();
|
||||
}
|
||||
|
||||
// Use card network icon.
|
||||
Suggestion::Icon icon = credit_card.CardIconForAutofillSuggestion();
|
||||
return icon == Suggestion::Icon::kNoIcon
|
||||
? nil
|
||||
: NativeImage(CreditCard::IconResourceId(
|
||||
credit_card.CardIconForAutofillSuggestion()));
|
||||
}
|
||||
|
||||
} // namespace autofill
|
||||
|
@ -9,8 +9,11 @@
|
||||
#import "base/memory/raw_ptr.h"
|
||||
#import "base/strings/sys_string_conversions.h"
|
||||
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
|
||||
#import "components/autofill/core/common/autofill_payments_features.h"
|
||||
#import "components/strings/grit/components_strings.h"
|
||||
#import "ios/chrome/browser/autofill/model/credit_card/credit_card_data.h"
|
||||
#import "ios/chrome/browser/net/model/crurl.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item_delegate.h"
|
||||
@ -21,6 +24,7 @@
|
||||
#import "ios/chrome/browser/ui/autofill/cells/expiration_date_edit_item.h"
|
||||
#import "ios/chrome/browser/ui/autofill/cells/expiration_date_edit_item_delegate.h"
|
||||
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
|
||||
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
|
||||
#import "ui/base/l10n/l10n_util.h"
|
||||
#import "url/gurl.h"
|
||||
|
||||
@ -29,6 +33,11 @@ NSString* const kCardUnmaskPromptTableViewAccessibilityID =
|
||||
|
||||
namespace {
|
||||
|
||||
BOOL VirtualCardFeatureEnabled() {
|
||||
return base::FeatureList::IsEnabled(
|
||||
autofill::features::kAutofillEnableVirtualCards);
|
||||
}
|
||||
|
||||
typedef NS_ENUM(NSInteger, SectionIdentifier) {
|
||||
SectionIdentifierHeader = kSectionIdentifierEnumZero,
|
||||
SectionIdentifierInputs,
|
||||
@ -36,6 +45,7 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) {
|
||||
|
||||
typedef NS_ENUM(NSInteger, ItemType) {
|
||||
ItemTypeHeader = kItemTypeEnumZero,
|
||||
ItemTypeCardInfo,
|
||||
ItemTypeCVCInput,
|
||||
ItemTypeFooter,
|
||||
ItemTypeExpirationDateInput,
|
||||
@ -73,6 +83,8 @@ const char kFooterDummyLinkTarget[] = "about:blank";
|
||||
// Owns `self`. A value of nullptr means the view controller is dismissed or
|
||||
// about to be dismissed.
|
||||
raw_ptr<autofill::CardUnmaskPromptViewBridge> _bridge; // weak
|
||||
// Model of the card info cell.
|
||||
TableViewDetailIconItem* _cardInfoItem;
|
||||
// Model of the CVC input cell.
|
||||
TableViewTextEditItem* _CVCInputItem;
|
||||
// Model of the footer.
|
||||
@ -161,6 +173,14 @@ const char kFooterDummyLinkTarget[] = "about:blank";
|
||||
[model setHeader:_headerItem
|
||||
forSectionWithIdentifier:SectionIdentifierHeader];
|
||||
|
||||
if (VirtualCardFeatureEnabled()) {
|
||||
_cardInfoItem = [self createCardInfoItem];
|
||||
if (_cardInfoItem != nil) {
|
||||
[self.tableViewModel addItem:_cardInfoItem
|
||||
toSectionWithIdentifier:SectionIdentifierHeader];
|
||||
}
|
||||
}
|
||||
|
||||
[model addSectionWithIdentifier:SectionIdentifierInputs];
|
||||
|
||||
_CVCInputItem = [self createCVCInputItem];
|
||||
@ -380,6 +400,24 @@ const char kFooterDummyLinkTarget[] = "about:blank";
|
||||
return confirmButton;
|
||||
}
|
||||
|
||||
// Returns the model for the card info cell.
|
||||
- (TableViewDetailIconItem*)createCardInfoItem {
|
||||
if (_bridge == nullptr) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CreditCardData* data = _bridge->credit_card_data();
|
||||
|
||||
TableViewDetailIconItem* cardInfoItem =
|
||||
[[TableViewDetailIconItem alloc] initWithType:ItemTypeCardInfo];
|
||||
cardInfoItem.textLayoutConstraintAxis = UILayoutConstraintAxisVertical;
|
||||
cardInfoItem.text = data.cardNameAndLastFourDigits;
|
||||
cardInfoItem.detailText = data.cardDetails;
|
||||
cardInfoItem.iconBackgroundColor = UIColor.clearColor;
|
||||
cardInfoItem.iconImage = data.icon;
|
||||
return cardInfoItem;
|
||||
}
|
||||
|
||||
// Returns the model for the CVC input cell.
|
||||
- (TableViewTextEditItem*)createCVCInputItem {
|
||||
if (_bridge == nullptr) {
|
||||
@ -615,6 +653,16 @@ const char kFooterDummyLinkTarget[] = "about:blank";
|
||||
rowCell.identifyingIconButton.isAccessibilityElement = NO;
|
||||
}
|
||||
|
||||
if (rowItemType == ItemTypeCardInfo) {
|
||||
TableViewDetailIconCell* rowCell =
|
||||
base::apple::ObjCCastStrict<TableViewDetailIconCell>(cell);
|
||||
rowCell.backgroundColor = [UIColor colorNamed:kGrey200Color];
|
||||
rowCell.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
rowCell.textLabel.numberOfLines = 1;
|
||||
rowCell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
rowCell.detailTextLabel.numberOfLines = 1;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,14 @@
|
||||
#import "base/apple/foundation_util.h"
|
||||
#import "base/strings/sys_string_conversions.h"
|
||||
#import "base/test/ios/wait_util.h"
|
||||
#import "base/test/scoped_feature_list.h"
|
||||
#import "components/autofill/core/browser/autofill_test_utils.h"
|
||||
#import "components/autofill/core/browser/payments/card_unmask_delegate.h"
|
||||
#import "components/autofill/core/browser/test_personal_data_manager.h"
|
||||
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h"
|
||||
#import "components/autofill/core/common/autofill_payments_features.h"
|
||||
#import "components/prefs/testing_pref_service.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item.h"
|
||||
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h"
|
||||
@ -54,6 +59,11 @@ class MockCardUnmaskPromptController
|
||||
bool was_checkbox_visible),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(const autofill::CreditCard&,
|
||||
GetCreditCard,
|
||||
(),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(bool, ShouldRequestExpirationDate, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool,
|
||||
@ -66,9 +76,11 @@ class MockCardUnmaskPromptViewBridge
|
||||
: public autofill::CardUnmaskPromptViewBridge {
|
||||
public:
|
||||
explicit MockCardUnmaskPromptViewBridge(
|
||||
autofill::CardUnmaskPromptControllerImpl* controller)
|
||||
autofill::CardUnmaskPromptControllerImpl* controller,
|
||||
autofill::TestPersonalDataManager* personal_data_manager)
|
||||
: CardUnmaskPromptViewBridge(controller,
|
||||
[[UIViewController alloc] init]) {}
|
||||
[[UIViewController alloc] init],
|
||||
personal_data_manager) {}
|
||||
|
||||
MockCardUnmaskPromptViewBridge(const MockCardUnmaskPromptViewBridge&) =
|
||||
delete;
|
||||
@ -87,11 +99,17 @@ class MockCardUnmaskPromptViewBridge
|
||||
};
|
||||
|
||||
class CardUnmaskPromptViewControllerTest
|
||||
: public LegacyChromeTableViewControllerTest {
|
||||
: public LegacyChromeTableViewControllerTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
CardUnmaskPromptViewControllerTest() = default;
|
||||
CardUnmaskPromptViewControllerTest()
|
||||
: virtual_card_enrollment_enabled_(GetParam()) {}
|
||||
|
||||
void SetUp() override {
|
||||
feature_list_.InitWithFeatureState(
|
||||
autofill::features::kAutofillEnableVirtualCards,
|
||||
virtual_card_enrollment_enabled_);
|
||||
|
||||
LegacyChromeTableViewControllerTest::SetUp();
|
||||
root_view_controller_ = [[UIViewController alloc] init];
|
||||
|
||||
@ -100,10 +118,15 @@ class CardUnmaskPromptViewControllerTest
|
||||
card_unmask_prompt_controller_ =
|
||||
std::make_unique<NiceMock<MockCardUnmaskPromptController>>(
|
||||
pref_service_.get());
|
||||
ON_CALL(*card_unmask_prompt_controller_, GetCreditCard)
|
||||
.WillByDefault(testing::ReturnRef(card_));
|
||||
|
||||
personal_data_manager_ =
|
||||
std::make_unique<autofill::TestPersonalDataManager>();
|
||||
|
||||
card_unmask_prompt_bridge_ =
|
||||
std::make_unique<NiceMock<MockCardUnmaskPromptViewBridge>>(
|
||||
card_unmask_prompt_controller_.get());
|
||||
card_unmask_prompt_controller_.get(), personal_data_manager_.get());
|
||||
|
||||
CreateController();
|
||||
}
|
||||
@ -149,6 +172,17 @@ class CardUnmaskPromptViewControllerTest
|
||||
cellForRowAtIndexPath:cvc_index_path];
|
||||
}
|
||||
|
||||
// Fetches the card info cell from the tableView's datasource.
|
||||
TableViewDetailIconCell* GetCardInfoCell() {
|
||||
return virtual_card_enrollment_enabled_ ? GetCell(/*item=*/0, /*section*/ 0)
|
||||
: nil;
|
||||
}
|
||||
|
||||
TableViewDetailIconItem* GetCardInfoItem() {
|
||||
return virtual_card_enrollment_enabled_ ? GetItem(/*item=*/0, /*section=*/0)
|
||||
: nil;
|
||||
}
|
||||
|
||||
// Fetches the CVC input cell from the tableView's datasource.
|
||||
TableViewTextEditCell* GetCVCInputCell() {
|
||||
return GetCell(/*item=*/0, /*section*/ 1);
|
||||
@ -274,26 +308,32 @@ class CardUnmaskPromptViewControllerTest
|
||||
EXPECT_EQ(footer.urls.count, 1LU);
|
||||
}
|
||||
|
||||
// Verifies that the correct items are in the tableViewModel when the
|
||||
// expiration date form is displayed.
|
||||
void CheckUpdateExpirationDateForm() {
|
||||
// Verifies that the correct items are in the tableViewModel.
|
||||
void CheckTableViewModel(bool with_expiration_date_item) {
|
||||
// Check expected number of sections and items.
|
||||
EXPECT_EQ(NumberOfSections(), 2);
|
||||
// First section only has a header.
|
||||
EXPECT_EQ(NumberOfItemsInSection(0), 0);
|
||||
// CVC and expiration date fields.
|
||||
EXPECT_EQ(NumberOfItemsInSection(1), 2);
|
||||
// First section only has a header when experiment is disabled. It has two
|
||||
// (a header and a card info cell) when experiment is enabled.
|
||||
EXPECT_EQ(NumberOfItemsInSection(0),
|
||||
virtual_card_enrollment_enabled_ ? 1 : 0);
|
||||
// CVC and expiration date fields if applicable.
|
||||
EXPECT_EQ(NumberOfItemsInSection(1), with_expiration_date_item ? 2 : 1);
|
||||
|
||||
CheckHeaderAndInstructions();
|
||||
|
||||
// Verifying card info is in model.
|
||||
EXPECT_EQ(virtual_card_enrollment_enabled_, !!GetCardInfoItem());
|
||||
|
||||
// Verifing CVC field is in model.
|
||||
EXPECT_TRUE(CVCInputItem());
|
||||
|
||||
// Verifying expiration date field is in model.
|
||||
EXPECT_TRUE(ExpirationDateItem());
|
||||
// Verifying expiration date field is updated correctly.
|
||||
EXPECT_EQ(with_expiration_date_item, !!ExpirationDateItem());
|
||||
|
||||
// Footer shouldn't be in model.
|
||||
EXPECT_FALSE(FooterItem());
|
||||
}
|
||||
|
||||
// Validates that the first responder has the given accessibility identifier.
|
||||
void CheckFirstResponderHasAccessibilityIdentifier(
|
||||
NSString* accessibility_identifier) {
|
||||
@ -309,30 +349,26 @@ class CardUnmaskPromptViewControllerTest
|
||||
std::unique_ptr<NiceMock<MockCardUnmaskPromptController>>
|
||||
card_unmask_prompt_controller_;
|
||||
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
|
||||
std::unique_ptr<autofill::TestPersonalDataManager> personal_data_manager_;
|
||||
ScopedKeyWindow scoped_window_;
|
||||
UIViewController* root_view_controller_;
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
autofill::CreditCard card_ = autofill::test::GetMaskedServerCard();
|
||||
bool virtual_card_enrollment_enabled_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
CardUnmaskPromptViewControllerTest,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace
|
||||
|
||||
// Validates that the CVC form is displayed as the initial state of the
|
||||
// controller when the card is not expired.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
|
||||
TEST_P(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
|
||||
CheckController();
|
||||
// Check expected number of sections and items.
|
||||
EXPECT_EQ(NumberOfSections(), 2);
|
||||
// First section only has a header.
|
||||
EXPECT_EQ(NumberOfItemsInSection(0), 0);
|
||||
// CVC and expiration date fields.
|
||||
EXPECT_EQ(NumberOfItemsInSection(1), 1);
|
||||
|
||||
CheckHeaderAndInstructions();
|
||||
|
||||
// Verifing CVC field is in model.
|
||||
EXPECT_TRUE(CVCInputItem());
|
||||
|
||||
// Footer shouldn't be in model.
|
||||
EXPECT_FALSE(FooterItem());
|
||||
CheckTableViewModel(/*with_expiration_date_item=*/false);
|
||||
|
||||
// Confirm button should be disabled until valid input is entered.
|
||||
EXPECT_FALSE(ConfirmButton().enabled);
|
||||
@ -344,7 +380,7 @@ TEST_F(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
|
||||
|
||||
// Validates that the Expiration Date form is displayed as the initial state of
|
||||
// the controller when the card is expired.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
ExpirationDateFormDisplayedAsInitialState) {
|
||||
// Recreate controller for the expiration date form state.
|
||||
ResetController();
|
||||
@ -354,7 +390,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
CreateController();
|
||||
|
||||
CheckUpdateExpirationDateForm();
|
||||
CheckTableViewModel(/*with_expiration_date_item=*/true);
|
||||
|
||||
// Add controller to view hierarchy to verify CVC field is focused.
|
||||
PresentController();
|
||||
@ -363,7 +399,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
// Validates that the tableViewModel is properly setup for displaying update
|
||||
// expiration date form.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
UpdateExpirationDateFormStateHasExpectedItems) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
@ -374,7 +410,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
[prompt_controller showUpdateExpirationDateForm];
|
||||
|
||||
CheckUpdateExpirationDateForm();
|
||||
CheckTableViewModel(/*with_expiration_date_item=*/true);
|
||||
|
||||
// Add controller to view hierarchy to verify Expiration Date field is
|
||||
// focused.
|
||||
@ -384,7 +420,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
// Validates the model is properly setup for displaying the expiration card link
|
||||
// in the controller's footer.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
ShowUpdateExpirationCardLinkAddsFooterWithLink) {
|
||||
// Footer shouldn't be in model before link is shown.
|
||||
EXPECT_FALSE(FooterItem());
|
||||
@ -399,7 +435,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
// Validates that the loading state displays an activity indicator in the
|
||||
// navigation bar and disables interactions with the input fields.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
ShowLoadingStateDisplaysActivityIndicatorAndDisablesInteractions) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
@ -414,7 +450,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
}
|
||||
|
||||
// Validates that an alert is presented for the error state.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
|
||||
TEST_P(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
|
||||
PresentController();
|
||||
|
||||
auto* prompt_controller =
|
||||
@ -441,13 +477,13 @@ TEST_F(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
|
||||
|
||||
// Verifies that submitting the CVC form forwards the CVC value to
|
||||
// CardUnmaskPromptController.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingCVCForm) {
|
||||
TEST_P(CardUnmaskPromptViewControllerTest, TestSubmittingCVCForm) {
|
||||
CheckSubmittingForm(/*CVC=*/@"123");
|
||||
}
|
||||
|
||||
// Verifies that submitting the update expiration date form forwards CVC and
|
||||
// expiration date to CardUnmaskPromptController/
|
||||
TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
|
||||
TEST_P(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
[prompt_controller showUpdateExpirationDateForm];
|
||||
@ -456,7 +492,7 @@ TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
|
||||
}
|
||||
|
||||
// Verifies that the controller is dismissed after an error.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
TestDismissingViewControllerAfterError) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
@ -467,7 +503,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
}
|
||||
|
||||
// Verifies that the expiration date link is displayed after an error.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
TestShowingUpdateExpirationDateLinkAfterError) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
@ -478,7 +514,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
}
|
||||
|
||||
// Verifies that the expiration date form is displayed after an error.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
TestShowingUpdateExpirationDateFormAfterError) {
|
||||
auto* prompt_controller =
|
||||
static_cast<CardUnmaskPromptViewController*>(controller());
|
||||
@ -488,11 +524,11 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
[prompt_controller onErrorAlertDismissedAndShouldCloseOnDismiss:NO];
|
||||
|
||||
CheckUpdateExpirationDateForm();
|
||||
CheckTableViewModel(/*with_expiration_date_item=*/true);
|
||||
}
|
||||
|
||||
// Verifies that the icon in the CVC input cell is hidden from Voice Over.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
TestCVCCellIconIsHiddenFromVoiceOver) {
|
||||
auto* CVC_cell = GetCVCInputCell();
|
||||
ASSERT_TRUE(CVC_cell);
|
||||
@ -501,7 +537,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
|
||||
// Verifies that the textField in the CVC input cell does not accept more than 4
|
||||
// digits.
|
||||
TEST_F(CardUnmaskPromptViewControllerTest,
|
||||
TEST_P(CardUnmaskPromptViewControllerTest,
|
||||
TestCVCTextFieldRejectsTooLongCVCValues) {
|
||||
auto* CVC_field = GetCVCInputCell().textField;
|
||||
ASSERT_TRUE(CVC_field);
|
||||
|
@ -287,7 +287,8 @@ void ChromeAutofillClientIOS::ShowUnmaskPrompt(
|
||||
unmask_controller_.ShowPrompt(
|
||||
base::BindOnce(&CreateCardUnmaskPromptViewBridge,
|
||||
base::Unretained(&unmask_controller_),
|
||||
base::Unretained(base_view_controller_)),
|
||||
base::Unretained(base_view_controller_),
|
||||
base::Unretained(personal_data_manager_)),
|
||||
card, card_unmask_prompt_options, delegate);
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,14 @@
|
||||
namespace autofill {
|
||||
|
||||
class CardUnmaskPromptController;
|
||||
class PersonalDataManager;
|
||||
|
||||
// Creates the view bridge for the iOS implementation of the Card Unmask
|
||||
// Prompt.
|
||||
CardUnmaskPromptView* CreateCardUnmaskPromptViewBridge(
|
||||
CardUnmaskPromptController* unmask_controller,
|
||||
UIViewController* base_view_controller);
|
||||
UIViewController* base_view_controller,
|
||||
PersonalDataManager* personal_data_manager);
|
||||
} // namespace autofill
|
||||
|
||||
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_CREATE_CARD_UNMASK_PROMPT_VIEW_BRIDGE_H_
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#import "ios/chrome/browser/ui/autofill/create_card_unmask_prompt_view_bridge.h"
|
||||
|
||||
#import "components/autofill/core/browser/personal_data_manager.h"
|
||||
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
|
||||
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
|
||||
|
||||
@ -11,9 +12,10 @@ namespace autofill {
|
||||
|
||||
CardUnmaskPromptView* CreateCardUnmaskPromptViewBridge(
|
||||
CardUnmaskPromptController* unmask_controller,
|
||||
UIViewController* base_view_controller) {
|
||||
return new CardUnmaskPromptViewBridge(unmask_controller,
|
||||
base_view_controller);
|
||||
UIViewController* base_view_controller,
|
||||
PersonalDataManager* personal_data_manager) {
|
||||
return new CardUnmaskPromptViewBridge(unmask_controller, base_view_controller,
|
||||
personal_data_manager);
|
||||
}
|
||||
|
||||
} // namespace autofill
|
||||
|
@ -222,6 +222,7 @@ source_set("requesters") {
|
||||
"//components/autofill/core/browser",
|
||||
"//components/autofill/core/common",
|
||||
"//components/autofill/ios/browser",
|
||||
"//ios/chrome/browser/autofill/model",
|
||||
"//ios/chrome/browser/shared/model/browser_state",
|
||||
"//ios/chrome/browser/shared/model/web_state_list:web_state_list",
|
||||
"//ios/chrome/browser/ui/autofill:bridges",
|
||||
|
@ -19,6 +19,7 @@ namespace autofill {
|
||||
class BrowserAutofillManager;
|
||||
class CreditCard;
|
||||
struct CardUnmaskPromptOptions;
|
||||
class PersonalDataManager;
|
||||
} // namespace autofill
|
||||
|
||||
// Receives the full credit card details. Also displays the unmask prompt UI.
|
||||
@ -49,6 +50,7 @@ class FullCardRequester
|
||||
private:
|
||||
__weak UIViewController* base_view_controller_;
|
||||
autofill::CardUnmaskPromptControllerImpl unmask_controller_;
|
||||
raw_ptr<autofill::PersonalDataManager> personal_data_manager_;
|
||||
};
|
||||
|
||||
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_FULL_CARD_REQUESTER_H_
|
||||
|
@ -5,13 +5,18 @@
|
||||
#import "ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.h"
|
||||
|
||||
#import "components/autofill/core/browser/browser_autofill_manager.h"
|
||||
#import "components/autofill/core/browser/personal_data_manager.h"
|
||||
#import "ios/chrome/browser/autofill/model/personal_data_manager_factory.h"
|
||||
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
|
||||
#import "ios/chrome/browser/ui/autofill/create_card_unmask_prompt_view_bridge.h"
|
||||
|
||||
FullCardRequester::FullCardRequester(UIViewController* base_view_controller,
|
||||
ChromeBrowserState* browser_state)
|
||||
: base_view_controller_(base_view_controller),
|
||||
unmask_controller_(browser_state->GetPrefs()) {}
|
||||
unmask_controller_(browser_state->GetPrefs()),
|
||||
personal_data_manager_(
|
||||
autofill::PersonalDataManagerFactory::GetForBrowserState(
|
||||
browser_state)) {}
|
||||
|
||||
void FullCardRequester::GetFullCard(
|
||||
const autofill::CreditCard& card,
|
||||
@ -36,7 +41,8 @@ void FullCardRequester::ShowUnmaskPrompt(
|
||||
unmask_controller_.ShowPrompt(
|
||||
base::BindOnce(&autofill::CreateCardUnmaskPromptViewBridge,
|
||||
base::Unretained(&unmask_controller_),
|
||||
base::Unretained(base_view_controller_)),
|
||||
base::Unretained(base_view_controller_),
|
||||
base::Unretained(personal_data_manager_)),
|
||||
card, card_unmask_prompt_options, delegate);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user