| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h" |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/task_environment.h" |
| #include "components/autofill/core/browser/autofill_test_utils.h" |
| #include "components/autofill/core/browser/data_model/credit_card.h" |
| #include "components/autofill/core/browser/form_structure.h" |
| #include "components/autofill/core/browser/payments_data_manager.h" |
| #include "components/autofill/core/browser/test_autofill_client.h" |
| #include "components/autofill/core/browser/test_autofill_driver.h" |
| #include "components/autofill/core/browser/test_browser_autofill_manager.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_constants.h" |
| #include "components/autofill/core/common/autofill_test_utils.h" |
| #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace autofill { |
| |
| namespace { |
| |
| using test::CreateTestCreditCardFormData; |
| using ::testing::_; |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::NiceMock; |
| using ::testing::Pointee; |
| using ::testing::Ref; |
| using ::testing::Return; |
| |
| class MockAutofillClient : public TestAutofillClient { |
| public: |
| MockAutofillClient() = default; |
| MockAutofillClient(const MockAutofillClient&) = delete; |
| MockAutofillClient& operator=(const MockAutofillClient&) = delete; |
| ~MockAutofillClient() override = default; |
| |
| MOCK_METHOD(void, |
| ScanCreditCard, |
| (CreditCardScanCallback callback), |
| (override)); |
| MOCK_METHOD(void, ShowAutofillSettings, (FillingProduct), (override)); |
| MOCK_METHOD(bool, |
| ShowTouchToFillCreditCard, |
| (base::WeakPtr<autofill::TouchToFillDelegate> delegate, |
| base::span<const CreditCard> cards_to_suggest), |
| (override)); |
| MOCK_METHOD(void, HideTouchToFillCreditCard, (), (override)); |
| MOCK_METHOD(void, |
| HideAutofillSuggestions, |
| (SuggestionHidingReason reason), |
| (override)); |
| |
| void ExpectDelegateWeakPtrFromShowInvalidatedOnHide() { |
| EXPECT_CALL(*this, ShowTouchToFillCreditCard) |
| .WillOnce([this](base::WeakPtr<autofill::TouchToFillDelegate> delegate, |
| base::span<const CreditCard> cards_to_suggest) { |
| captured_delegate_ = delegate; |
| return true; |
| }); |
| EXPECT_CALL(*this, HideTouchToFillCreditCard).WillOnce([this]() { |
| EXPECT_FALSE(captured_delegate_); |
| }); |
| } |
| |
| private: |
| base::WeakPtr<autofill::TouchToFillDelegate> captured_delegate_; |
| }; |
| |
| class MockBrowserAutofillManager : public TestBrowserAutofillManager { |
| public: |
| explicit MockBrowserAutofillManager(TestAutofillDriver* driver) |
| : TestBrowserAutofillManager(driver) {} |
| MockBrowserAutofillManager(const MockBrowserAutofillManager&) = delete; |
| MockBrowserAutofillManager& operator=(const MockBrowserAutofillManager&) = |
| delete; |
| ~MockBrowserAutofillManager() override = default; |
| |
| MOCK_METHOD(void, |
| FillOrPreviewCreditCardForm, |
| (mojom::ActionPersistence action_persistence, |
| const FormData& form, |
| const FormFieldData& field, |
| const CreditCard& credit_card, |
| const std::u16string& cvc, |
| const AutofillTriggerDetails& trigger_details), |
| (override)); |
| MOCK_METHOD(void, |
| AuthenticateThenFillCreditCardForm, |
| (const FormData& form, |
| const FormFieldData& field, |
| const CreditCard& credit_card, |
| const AutofillTriggerDetails& trigger_details)); |
| MOCK_METHOD(void, |
| DidShowSuggestions, |
| (base::span<const SuggestionType> shown_suggestions_types, |
| const FormData& form, |
| const FormFieldData& field), |
| (override)); |
| MOCK_METHOD(bool, CanShowAutofillUi, (), (const, override)); |
| MOCK_METHOD(AutofillField*, |
| GetAutofillField, |
| (const FormData& form, const FormFieldData& field)); |
| }; |
| |
| } // namespace |
| |
| class TouchToFillDelegateAndroidImplUnitTest : public testing::Test { |
| public: |
| TouchToFillDelegateAndroidImplUnitTest() { |
| // Some date after in the 2000s because Autofill doesn't allow expiration |
| // dates before 2000. |
| task_environment_.AdvanceClock(base::Days(365 * 50)); |
| } |
| |
| protected: |
| void SetUp() override { |
| autofill_client_.SetPrefs(test::PrefServiceForTesting()); |
| autofill_client_.GetPersonalDataManager()->SetPrefService( |
| autofill_client_.GetPrefs()); |
| autofill_driver_ = std::make_unique<TestAutofillDriver>(&autofill_client_); |
| browser_autofill_manager_ = |
| std::make_unique<NiceMock<MockBrowserAutofillManager>>( |
| autofill_driver_.get()); |
| |
| auto touch_to_fill_delegate = |
| std::make_unique<TouchToFillDelegateAndroidImpl>( |
| browser_autofill_manager_.get()); |
| touch_to_fill_delegate_ = touch_to_fill_delegate.get(); |
| base::WeakPtr<TouchToFillDelegateAndroidImpl> touch_to_fill_delegate_weak = |
| touch_to_fill_delegate->GetWeakPtr(); |
| browser_autofill_manager_->set_touch_to_fill_delegate( |
| std::move(touch_to_fill_delegate)); |
| |
| // Default setup for successful `TryToShowTouchToFill`. |
| ON_CALL(*browser_autofill_manager_, CanShowAutofillUi) |
| .WillByDefault(Return(true)); |
| ON_CALL(autofill_client_, ShowTouchToFillCreditCard) |
| .WillByDefault(Return(true)); |
| // Calling HideTouchToFillCreditCard in production code leads to that |
| // OnDismissed gets triggered (HideTouchToFillCreditCard calls view->Hide() |
| // on java side, which in its turn triggers onDismissed). Here we mock this |
| // call. |
| ON_CALL(autofill_client_, HideTouchToFillCreditCard) |
| .WillByDefault([delegate = touch_to_fill_delegate_weak]() -> void { |
| if (delegate) { |
| delegate->OnDismissed(/*dismissed_by_user=*/false); |
| } |
| }); |
| autofill::MockFastCheckoutClient* fast_checkout_client = |
| static_cast<autofill::MockFastCheckoutClient*>( |
| autofill_client_.GetFastCheckoutClient()); |
| ON_CALL(*fast_checkout_client, IsNotShownYet) |
| .WillByDefault(testing::Return(true)); |
| } |
| |
| // Helper method to add the given `card` and create a card form. |
| void ConfigureForCreditCards(const CreditCard& card) { |
| form_ = test::CreateTestCreditCardFormData(/*is_https=*/true, |
| /*use_month_type=*/false); |
| form_.fields[0].set_is_focusable(true); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(card); |
| } |
| |
| // Helper method to add an IBAN and create an IBAN form. |
| std::string ConfigureForIbans() { |
| Iban iban; |
| iban.set_value(std::u16string(test::kIbanValue16)); |
| std::string guid = autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .AddAsLocalIban(std::move(iban)); |
| form_ = test::CreateTestIbanFormData(/*value=*/""); |
| form_.fields[0].set_is_focusable(true); |
| return guid; |
| } |
| |
| void OnFormsSeen() { |
| if (!browser_autofill_manager_->FindCachedFormById(form_.global_id())) { |
| browser_autofill_manager_->OnFormsSeen({form_}, {}); |
| } |
| } |
| |
| void IntendsToShowTouchToFill(bool expected_success) { |
| OnFormsSeen(); |
| EXPECT_EQ(expected_success, |
| touch_to_fill_delegate_->IntendsToShowTouchToFill( |
| form_.global_id(), form_.fields[0].global_id(), form_)); |
| } |
| |
| void TryToShowTouchToFill(bool expected_success) { |
| EXPECT_CALL(autofill_client_, |
| HideAutofillSuggestions( |
| SuggestionHidingReason::kOverlappingWithTouchToFillSurface)) |
| .Times(expected_success ? 1 : 0); |
| |
| OnFormsSeen(); |
| EXPECT_EQ(expected_success, touch_to_fill_delegate_->TryToShowTouchToFill( |
| form_, form_.fields[0])); |
| EXPECT_EQ(expected_success, |
| touch_to_fill_delegate_->IsShowingTouchToFill()); |
| } |
| |
| FormData form_; |
| |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| test::AutofillUnitTestEnvironment autofill_test_environment_; |
| NiceMock<MockAutofillClient> autofill_client_; |
| std::unique_ptr<TestAutofillDriver> autofill_driver_; |
| std::unique_ptr<MockBrowserAutofillManager> browser_autofill_manager_; |
| raw_ptr<TouchToFillDelegateAndroidImpl> touch_to_fill_delegate_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| // Params of TouchToFillDelegateAndroidImplPaymentMethodUnitTest: |
| // -- bool IsCreditCard: Indicates whether the payment method is a credit card |
| // or an IBAN. |
| class TouchToFillDelegateAndroidImplPaymentMethodUnitTest |
| : public TouchToFillDelegateAndroidImplUnitTest, |
| public testing::WithParamInterface<bool> { |
| protected: |
| void SetUp() override { |
| TouchToFillDelegateAndroidImplUnitTest::SetUp(); |
| if (IsCreditCard()) { |
| ConfigureForCreditCards(test::GetCreditCard()); |
| } else { |
| ConfigureForIbans(); |
| } |
| } |
| |
| bool IsCreditCard() const { return GetParam(); } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| testing::Bool()); |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| TryToShowTouchToFillFailsForInvalidForm) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| browser_autofill_manager_->ClearFormStructures(); |
| |
| EXPECT_EQ(false, touch_to_fill_delegate_->TryToShowTouchToFill( |
| form_, form_.fields[0])); |
| } |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| HideTouchToFillDoesNothingIfNotShown) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard).Times(0); |
| touch_to_fill_delegate_->HideTouchToFill(); |
| EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| } |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| HideTouchToFillHidesIfShown) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard); |
| touch_to_fill_delegate_->HideTouchToFill(); |
| EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| } |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| ResetHidesTouchToFillIfShown) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard); |
| touch_to_fill_delegate_->Reset(); |
| EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| } |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| ResetAllowsShowingTouchToFillAgain) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| touch_to_fill_delegate_->HideTouchToFill(); |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| touch_to_fill_delegate_->Reset(); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, |
| OnDismissSetsTouchToFillToNotShowingState) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| touch_to_fill_delegate_->OnDismissed(false); |
| |
| EXPECT_EQ(touch_to_fill_delegate_->IsShowingTouchToFill(), false); |
| } |
| |
| class TouchToFillDelegateAndroidImplCreditCardUnitTest |
| : public TouchToFillDelegateAndroidImplUnitTest { |
| public: |
| static std::vector<CreditCard> GetCardsToSuggest( |
| std::vector<CreditCard*> credit_cards) { |
| std::vector<CreditCard> cards_to_suggest; |
| cards_to_suggest.reserve(credit_cards.size()); |
| for (const CreditCard* card : credit_cards) { |
| cards_to_suggest.push_back(*card); |
| } |
| return cards_to_suggest; |
| } |
| |
| protected: |
| void SetUp() override { |
| TouchToFillDelegateAndroidImplUnitTest::SetUp(); |
| ConfigureForCreditCards(test::GetCreditCard()); |
| } |
| }; |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfNotCreditCardField) { |
| form_.fields.insert(form_.fields.begin(), |
| test::CreateTestFormField("Arbitrary", "arbitrary", "", |
| FormControlType::kInputText)); |
| |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillPaymentMethodSucceeds) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| EXPECT_CALL(*browser_autofill_manager_, DidShowSuggestions); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kShown, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForPaymentMethodIfWasShown) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| touch_to_fill_delegate_->HideTouchToFill(); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectBucketCount( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kShownBefore, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForPaymentMethodIfFieldIsNotFocusable) { |
| form_.fields[0].set_is_focusable(false); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFieldNotEmptyOrNotFocusable, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForPaymentMethodIfFieldHasValue) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| form_.fields[0].set_value(u"Initial value"); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFieldNotEmptyOrNotFocusable, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForPaymentMethodIfNoPaymentMethodsOnFile) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| autofill_client_.GetPersonalDataManager()->ClearAllLocalData(); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kNoValidPaymentMethods, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfCanNotShowUi) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| EXPECT_CALL(*browser_autofill_manager_, CanShowAutofillUi) |
| .WillRepeatedly(Return(false)); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kCannotShowAutofillUi, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForIncompleteForm) { |
| // Erase expiration month and expiration year fields. |
| ASSERT_EQ(form_.fields[2].name(), u"ccmonth"); |
| form_.fields.erase(form_.fields.begin() + 2); |
| ASSERT_EQ(form_.fields[2].name(), u"ccyear"); |
| form_.fields.erase(form_.fields.begin() + 2); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kIncompleteForm, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsForPrefilledCardNumber) { |
| // Force the form to be parsed here to test the case, when form values are |
| // changed after the form is added to the cache. |
| browser_autofill_manager_->OnFormsSeen({form_}, {}); |
| // Set credit card value. |
| // TODO(crbug.com/40900766): Retrieve the card number field by name here. |
| ASSERT_EQ(form_.fields[1].name(), u"cardnumber"); |
| form_.fields[1].set_value(u"411111111111"); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFormAlreadyFilled, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillSucceedsForPrefilledYear) { |
| // Force the form to be parsed here to test the case, when form values are |
| // changed after the form is added to the cache. |
| browser_autofill_manager_->OnFormsSeen({form_}, {}); |
| // Set card expiration year. |
| // TODO(crbug.com/40900766): Retrieve the card expiry year field by name here. |
| ASSERT_EQ(form_.fields[3].name(), u"ccyear"); |
| form_.fields[3].set_value(u"2023"); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kShown, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfFormIsNotSecure) { |
| // Simulate non-secure form. |
| form_ = test::CreateTestCreditCardFormData(/*is_https=*/false, |
| /*use_month_type=*/false); |
| |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFormOrClientNotSecure, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfClientIsNotSecure) { |
| // Simulate non-secure client. |
| autofill_client_.set_form_origin(GURL("http://example.com")); |
| |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFormOrClientNotSecure, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfShownBefore) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| touch_to_fill_delegate_->OnDismissed(/*dismissed_by_user=*/true); |
| |
| EXPECT_CALL(autofill_client_, |
| HideAutofillSuggestions( |
| SuggestionHidingReason::kOverlappingWithTouchToFillSurface)) |
| .Times(0); |
| TryToShowTouchToFill(/*expected_success=*/false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfShownCurrently) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, |
| HideAutofillSuggestions( |
| SuggestionHidingReason::kOverlappingWithTouchToFillSurface)) |
| .Times(0); |
| EXPECT_FALSE( |
| touch_to_fill_delegate_->TryToShowTouchToFill(form_, form_.fields[0])); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillToleratesFormattingCharacters) { |
| form_.fields[0].set_value(u"____-____-____-____"); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| histogram_tester_.ExpectBucketCount( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kShown, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfCardIsIncomplete) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard cc_no_number = test::GetCreditCard(); |
| cc_no_number.SetNumber(u""); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(cc_no_number); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| CreditCard cc_no_exp_date = test::GetCreditCard(); |
| cc_no_exp_date.SetExpirationMonth(0); |
| cc_no_exp_date.SetExpirationYear(0); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(cc_no_exp_date); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| |
| CreditCard cc_no_name = test::GetCreditCard(); |
| cc_no_name.SetRawInfo(CREDIT_CARD_NAME_FULL, u""); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(cc_no_name); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kNoValidPaymentMethods, 3); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfTheOnlyCardIsExpired) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(test::GetExpiredCreditCard()); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kNoValidPaymentMethods, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfCardNumberIsInvalid) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard cc_invalid_number = test::GetCreditCard(); |
| cc_invalid_number.SetNumber(u"invalid number"); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(cc_invalid_number); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kNoValidPaymentMethods, 1); |
| |
| // But succeeds for existing masked server card with incomplete number. |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(test::GetMaskedServerCard()); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| histogram_tester_.ExpectBucketCount( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kShown, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfShowFails) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| EXPECT_CALL(autofill_client_, ShowTouchToFillCreditCard) |
| .WillOnce(Return(false)); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillFailsIfFastCheckoutWasShown) { |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| autofill::MockFastCheckoutClient* fast_checkout_client = |
| static_cast<autofill::MockFastCheckoutClient*>( |
| autofill_client_.GetFastCheckoutClient()); |
| EXPECT_CALL(*fast_checkout_client, IsNotShownYet).WillOnce(Return(false)); |
| |
| TryToShowTouchToFill(/*expected_success=*/false); |
| histogram_tester_.ExpectUniqueSample( |
| kUmaTouchToFillCreditCardTriggerOutcome, |
| TouchToFillPaymentMethodTriggerOutcome::kFastCheckoutWasShown, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillSucceedsIfAtLestOneCardIsValid) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| CreditCard expired_card = test::GetExpiredCreditCard(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(expired_card); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| EXPECT_CALL(autofill_client_, ShowTouchToFillCreditCard) |
| .WillOnce(Return(true)); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillShowsExpiredCards) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| CreditCard expired_card = test::GetExpiredCreditCard(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(expired_card); |
| std::vector<CreditCard*> credit_cards = |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .GetCreditCardsToSuggest(); |
| |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| EXPECT_CALL(autofill_client_, |
| ShowTouchToFillCreditCard( |
| _, ElementsAreArray(GetCardsToSuggest(credit_cards)))); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillDoesNotShowDisusedExpiredCards) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| CreditCard disused_expired_card = test::GetExpiredCreditCard(); |
| credit_card.set_use_date(AutofillClock::Now()); |
| disused_expired_card.set_use_date(AutofillClock::Now() - |
| kDisusedDataModelTimeDelta * 2); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(disused_expired_card); |
| ASSERT_TRUE(credit_card.IsCompleteValidCard()); |
| ASSERT_FALSE(disused_expired_card.IsCompleteValidCard()); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| EXPECT_CALL(autofill_client_, |
| ShowTouchToFillCreditCard(_, ElementsAre(credit_card))); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillShowsVirtualCardSuggestionsForEnrolledCards) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = |
| autofill::test::GetMaskedServerCardEnrolledIntoVirtualCardNumber(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| ON_CALL(*static_cast<MockAutofillOptimizationGuide*>( |
| autofill_client_.GetAutofillOptimizationGuide()), |
| ShouldBlockFormFieldSuggestion) |
| .WillByDefault(testing::Return(false)); |
| |
| // Since the card is enrolled into the virtual cards feature, a virtual card |
| // suggestion should be created and added before the real card. |
| EXPECT_CALL( |
| autofill_client_, |
| ShowTouchToFillCreditCard( |
| _, ElementsAreArray( |
| {CreditCard::CreateVirtualCard(credit_card), credit_card}))); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| // Since merchant has opted out of virtual cards, no virtual card suggestion |
| // is shown for virtual card number enrolled card. |
| TEST_F( |
| TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| TryToShowTouchToFillDoesNotShowVirtualCardSuggestionsForOptedOutMerchants) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = |
| test::GetMaskedServerCardEnrolledIntoVirtualCardNumber(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); |
| ON_CALL(*static_cast<MockAutofillOptimizationGuide*>( |
| autofill_client_.GetAutofillOptimizationGuide()), |
| ShouldBlockFormFieldSuggestion) |
| .WillByDefault(testing::Return(true)); |
| EXPECT_CALL(autofill_client_, |
| ShowTouchToFillCreditCard(_, ElementsAre(credit_card))); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| SafelyHideTouchToFillInDtor) { |
| autofill_client_.ExpectDelegateWeakPtrFromShowInvalidatedOnHide(); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| browser_autofill_manager_.reset(); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| PassTheCreditCardsToTheClient) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card1 = autofill::test::GetCreditCard(); |
| CreditCard credit_card2 = autofill::test::GetCreditCard2(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card1); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card2); |
| std::vector<CreditCard*> credit_cards = |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .GetCreditCardsToSuggest(); |
| |
| EXPECT_CALL(autofill_client_, |
| ShowTouchToFillCreditCard( |
| _, ElementsAreArray(GetCardsToSuggest(credit_cards)))); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| browser_autofill_manager_.reset(); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| ScanCreditCardIsCalled) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| EXPECT_CALL(autofill_client_, ScanCreditCard); |
| touch_to_fill_delegate_->ScanCreditCard(); |
| |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| EXPECT_CALL(*browser_autofill_manager_, FillOrPreviewCreditCardForm); |
| touch_to_fill_delegate_->OnCreditCardScanned(credit_card); |
| EXPECT_EQ(touch_to_fill_delegate_->IsShowingTouchToFill(), false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| ShowCreditCardSettingsIsCalled) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, |
| ShowAutofillSettings(testing::Eq(FillingProduct::kCreditCard))); |
| touch_to_fill_delegate_->ShowPaymentMethodSettings(); |
| |
| ASSERT_EQ(touch_to_fill_delegate_->IsShowingTouchToFill(), true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| CardSelectionClosesTheSheet) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard); |
| touch_to_fill_delegate_->CreditCardSuggestionSelected(credit_card.guid(), |
| false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| CardSelectionFillsCardForm) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = autofill::test::GetCreditCard(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(*browser_autofill_manager_, AuthenticateThenFillCreditCardForm); |
| touch_to_fill_delegate_->CreditCardSuggestionSelected(credit_card.guid(), |
| false); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| VirtualCardSelectionFillsCardForm) { |
| autofill_client_.GetPersonalDataManager() |
| ->test_payments_data_manager() |
| .ClearCreditCards(); |
| CreditCard credit_card = |
| autofill::test::GetMaskedServerCardEnrolledIntoVirtualCardNumber(); |
| autofill_client_.GetPersonalDataManager() |
| ->payments_data_manager() |
| .AddCreditCard(credit_card); |
| |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(*browser_autofill_manager_, AuthenticateThenFillCreditCardForm); |
| touch_to_fill_delegate_->CreditCardSuggestionSelected(credit_card.guid(), |
| true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| AutofillUsedAfterTouchToFillDismissal) { |
| TryToShowTouchToFill(/*expected_success=*/true); |
| touch_to_fill_delegate_->OnDismissed(/*dismissed_by_user=*/true); |
| |
| // Simulate that the form was autofilled by other means |
| FormStructure submitted_form(form_); |
| for (const std::unique_ptr<AutofillField>& field : submitted_form) { |
| field->set_is_autofilled(true); |
| } |
| |
| touch_to_fill_delegate_->LogMetricsAfterSubmission(submitted_form); |
| histogram_tester_.ExpectUniqueSample( |
| "Autofill.TouchToFill.CreditCard.AutofillUsedAfterTouchToFillDismissal", |
| true, 1); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, |
| IsFormPrefilledHandlesNullAutofillField) { |
| // `IntendsToShowTouchToFill()` invokes `DryRun()` that checks if form_ is |
| // prefilled. `IsFormPrefilled()` calls |
| // BrowserAutofillManager::GetAutofillField(). This tests the scenario where |
| // `GetAutofillField()` returns a nullptr does not crash. |
| ON_CALL(*browser_autofill_manager_, GetAutofillField(_, _)) |
| .WillByDefault(Return(nullptr)); |
| IntendsToShowTouchToFill(/*expected_success=*/true); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplUnitTest, IbanSelectionClosesTheSheet) { |
| std::string guid = ConfigureForIbans(); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard); |
| touch_to_fill_delegate_->IbanSuggestionSelected(Iban::Guid(guid)); |
| } |
| |
| TEST_F(TouchToFillDelegateAndroidImplUnitTest, IbanSelectionFillsIbanForm) { |
| std::string guid = ConfigureForIbans(); |
| TryToShowTouchToFill(/*expected_success=*/true); |
| |
| EXPECT_CALL(*(autofill_client_.GetMockIbanAccessManager()), FetchValue); |
| touch_to_fill_delegate_->IbanSuggestionSelected(Iban::Guid(guid)); |
| } |
| |
| } // namespace autofill |