blob: bff03eb7a05c44d047e6f5d9df4e802b326b1286 [file] [log] [blame]
// Copyright 2018 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.
#include <ctime>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback_list.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/autofill/autofill_uitest_util.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/ui/autofill/chrome_autofill_client.h"
#include "chrome/browser/ui/autofill/payments/local_card_migration_bubble_controller_impl.h"
#include "chrome/browser/ui/autofill/payments/local_card_migration_dialog_controller_impl.h"
#include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/autofill/payments/dialog_view_ids.h"
#include "chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h"
#include "chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.h"
#include "chrome/browser/ui/views/autofill/payments/migratable_card_view.h"
#include "chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_container_view.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_loading_indicator_view.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/web_data_service_factory.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/form_data_importer.h"
#include "components/autofill/core/browser/payments/credit_card_save_manager.h"
#include "components/autofill/core/browser/payments/local_card_migration_manager.h"
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "components/autofill/core/browser/test_event_waiter.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/sync/test/fake_server/fake_server.h"
#include "components/sync/test/fake_server/fake_server_network_resources.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/window/dialog_client_view.h"
using base::Bucket;
using testing::ElementsAre;
namespace autofill {
namespace {
ACTION_P(QuitMessageLoop, loop) {
loop->Quit();
}
constexpr char kURLGetUploadDetailsRequest[] =
"https://payments.google.com/payments/apis/chromepaymentsservice/"
"getdetailsforsavecard";
constexpr char kURLMigrateCardRequest[] =
"https://payments.google.com/payments/apis-secure/chromepaymentsservice/"
"migratecards"
"?s7e_suffix=chromewallet";
constexpr char kResponseGetUploadDetailsSuccess[] =
"{\"legal_message\":{\"line\":[{\"template\":\"Legal message template with "
"link: "
"{0}.\",\"template_parameter\":[{\"display_text\":\"Link\",\"url\":\"https:"
"//www.example.com/\"}]}]},\"context_token\":\"dummy_context_token\"}";
constexpr char kResponseGetUploadDetailsFailure[] =
"{\"error\":{\"code\":\"FAILED_PRECONDITION\",\"user_error_message\":\"An "
"unexpected error has occurred. Please try again later.\"}}";
constexpr char kResponseMigrateCardSuccess[] =
"{\"save_result\":[{\"unique_id\":\"0\", \"status\":\"SUCCESS\"}, "
"{\"unique_id\":\"1\", \"status\":\"SUCCESS\"}], "
"\"value_prop_display_text\":\"example message.\"}";
constexpr char kCreditCardFormURL[] =
"/credit_card_upload_form_address_and_cc.html";
constexpr char kFirstCardNumber[] = "5428424047572420"; // Mastercard
constexpr char kSecondCardNumber[] = "4782187095085933"; // Visa
constexpr char kThirdCardNumber[] = "4111111111111111"; // Visa
constexpr char kInvalidCardNumber[] = "4444444444444444";
constexpr double kFakeGeolocationLatitude = 1.23;
constexpr double kFakeGeolocationLongitude = 4.56;
} // namespace
class PersonalDataLoadedObserverMock : public PersonalDataManagerObserver {
public:
PersonalDataLoadedObserverMock() = default;
~PersonalDataLoadedObserverMock() = default;
MOCK_METHOD0(OnPersonalDataChanged, void());
};
class LocalCardMigrationBrowserTest
: public SyncTest,
public LocalCardMigrationManager::ObserverForTest {
protected:
// Various events that can be waited on by the DialogEventWaiter.
enum class DialogEvent : int {
REQUESTED_LOCAL_CARD_MIGRATION,
RECEIVED_GET_UPLOAD_DETAILS_RESPONSE,
SENT_MIGRATE_CARDS_REQUEST,
RECEIVED_MIGRATE_CARDS_RESPONSE
};
LocalCardMigrationBrowserTest() : SyncTest(SINGLE_CLIENT) {}
~LocalCardMigrationBrowserTest() override {}
void SetUpOnMainThread() override {
SyncTest::SetUpOnMainThread();
// Set up the HTTPS server (uses the embedded_test_server).
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
embedded_test_server()->ServeFilesFromSourceDirectory(
"components/test/data/autofill");
embedded_test_server()->StartAcceptingConnections();
ProfileSyncServiceFactory::GetAsProfileSyncServiceForProfile(
browser()->profile())
->OverrideNetworkResourcesForTest(
std::make_unique<fake_server::FakeServerNetworkResources>(
GetFakeServer()->AsWeakPtr()));
std::string username;
#if defined(OS_CHROMEOS)
// In ChromeOS browser tests, the profile may already by authenticated with
// stub account |user_manager::kStubUserEmail|.
CoreAccountInfo info =
IdentityManagerFactory::GetForProfile(browser()->profile())
->GetPrimaryAccountInfo();
username = info.email;
#endif
if (username.empty())
username = "user@gmail.com";
harness_ = ProfileSyncServiceHarness::Create(
browser()->profile(), username, "password",
ProfileSyncServiceHarness::SigninType::FAKE_SIGNIN);
// Set up the URL loader factory for the payments client so we can intercept
// those network requests too.
test_shared_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
ContentAutofillDriver::GetForRenderFrameHost(
GetActiveWebContents()->GetMainFrame())
->autofill_manager()
->client()
->GetPaymentsClient()
->set_url_loader_factory_for_testing(test_shared_loader_factory_);
// Set up this class as the ObserverForTest implementation.
local_card_migration_manager_ =
ContentAutofillDriver::GetForRenderFrameHost(
GetActiveWebContents()->GetMainFrame())
->autofill_manager()
->client()
->GetFormDataImporter()
->local_card_migration_manager_.get();
local_card_migration_manager_->SetEventObserverForTesting(this);
personal_data_ =
PersonalDataManagerFactory::GetForProfile(browser()->profile());
// Set up the fake geolocation data.
geolocation_overrider_ =
std::make_unique<device::ScopedGeolocationOverrider>(
kFakeGeolocationLatitude, kFakeGeolocationLongitude);
ASSERT_TRUE(harness_->SetupSync());
// Set the billing_customer_number to designate existence of a Payments
// account.
const PaymentsCustomerData data =
PaymentsCustomerData(/*customer_id=*/"123456");
SetPaymentsCustomerData(data);
SetUploadDetailsRpcPaymentsAccepts();
SetUpMigrateCardsRpcPaymentsAccepts();
}
void SetPaymentsCustomerDataOnDBSequence(
AutofillWebDataService* wds,
const PaymentsCustomerData& customer_data) {
DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence());
AutofillTable::FromWebDatabase(wds->GetDatabase())
->SetPaymentsCustomerData(&customer_data);
}
void SetPaymentsCustomerData(const PaymentsCustomerData& customer_data) {
scoped_refptr<AutofillWebDataService> wds =
WebDataServiceFactory::GetAutofillWebDataForProfile(
browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS);
base::RunLoop loop;
wds->GetDBTaskRunner()->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&LocalCardMigrationBrowserTest::SetPaymentsCustomerDataOnDBSequence,
base::Unretained(this), base::Unretained(wds.get()), customer_data),
base::BindOnce(&base::RunLoop::Quit, base::Unretained(&loop)));
loop.Run();
WaitForOnPersonalDataChanged();
}
void WaitForOnPersonalDataChanged() {
personal_data_->AddObserver(&personal_data_observer_);
personal_data_->Refresh();
base::RunLoop run_loop;
EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
.WillRepeatedly(QuitMessageLoop(&run_loop));
run_loop.Run();
testing::Mock::VerifyAndClearExpectations(&personal_data_observer_);
personal_data_->RemoveObserver(&personal_data_observer_);
}
void NavigateTo(const std::string& file_path) {
ui_test_utils::NavigateToURL(
browser(), file_path.find("data:") == 0U
? GURL(file_path)
: embedded_test_server()->GetURL(file_path));
}
void OnDecideToRequestLocalCardMigration() override {
if (event_waiter_)
event_waiter_->OnEvent(DialogEvent::REQUESTED_LOCAL_CARD_MIGRATION);
}
void OnReceivedGetUploadDetailsResponse() override {
if (event_waiter_)
event_waiter_->OnEvent(DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE);
}
void OnSentMigrateCardsRequest() override {
if (event_waiter_)
event_waiter_->OnEvent(DialogEvent::SENT_MIGRATE_CARDS_REQUEST);
}
void OnReceivedMigrateCardsResponse() override {
if (event_waiter_)
event_waiter_->OnEvent(DialogEvent::RECEIVED_MIGRATE_CARDS_RESPONSE);
}
CreditCard SaveLocalCard(std::string card_number,
bool set_as_expired_card = false) {
CreditCard local_card;
test::SetCreditCardInfo(&local_card, "John Smith", card_number.c_str(),
"12",
set_as_expired_card ? test::LastYear().c_str()
: test::NextYear().c_str(),
"1");
local_card.set_guid("00000000-0000-0000-0000-" + card_number.substr(0, 12));
local_card.set_record_type(CreditCard::LOCAL_CARD);
AddTestCreditCard(browser(), local_card);
return local_card;
}
CreditCard SaveServerCard(std::string card_number) {
CreditCard server_card;
test::SetCreditCardInfo(&server_card, "John Smith", card_number.c_str(),
"12", test::NextYear().c_str(), "1");
server_card.set_guid("00000000-0000-0000-0000-" +
card_number.substr(0, 12));
server_card.set_record_type(CreditCard::FULL_SERVER_CARD);
server_card.set_server_id("full_id_" + card_number);
AddTestServerCreditCard(browser(), server_card);
return server_card;
}
void UseCardAndWaitForMigrationOffer(std::string card_number) {
// Reusing a card should show the migration offer bubble.
ResetEventWaiterForSequence(
{DialogEvent::REQUESTED_LOCAL_CARD_MIGRATION,
DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE});
FillAndSubmitFormWithCard(card_number);
WaitForObservedEvent();
}
void ClickOnSaveButtonAndWaitForMigrationResults() {
ResetEventWaiterForSequence({DialogEvent::SENT_MIGRATE_CARDS_REQUEST,
DialogEvent::RECEIVED_MIGRATE_CARDS_RESPONSE});
ClickOnOkButton(GetLocalCardMigrationMainDialogView());
WaitForObservedEvent();
}
void FillAndSubmitFormWithCard(std::string card_number) {
NavigateTo(kCreditCardFormURL);
content::WebContents* web_contents = GetActiveWebContents();
const std::string click_fill_button_js =
"(function() { document.getElementById('fill_form').click(); })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, click_fill_button_js));
const std::string fill_cc_number_js =
"(function() { document.getElementsByName(\"cc_number\")[0].value = " +
card_number + "; })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, fill_cc_number_js));
const std::string click_submit_button_js =
"(function() { document.getElementById('submit').click(); })();";
content::TestNavigationObserver nav_observer(web_contents);
ASSERT_TRUE(content::ExecuteScript(web_contents, click_submit_button_js));
nav_observer.Wait();
}
void SetUploadDetailsRpcPaymentsAccepts() {
test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest,
kResponseGetUploadDetailsSuccess);
}
void SetUploadDetailsRpcPaymentsDeclines() {
test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest,
kResponseGetUploadDetailsFailure);
}
void SetUpMigrateCardsRpcPaymentsAccepts() {
test_url_loader_factory()->AddResponse(kURLMigrateCardRequest,
kResponseMigrateCardSuccess);
}
void ClickOnView(views::View* view) {
CHECK(view);
ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
view->OnMousePressed(pressed);
ui::MouseEvent released_event =
ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
view->OnMouseReleased(released_event);
}
void ClickOnDialogViewAndWait(
views::View* view,
views::DialogDelegateView* local_card_migration_view) {
CHECK(local_card_migration_view);
views::test::WidgetDestroyedWaiter destroyed_waiter(
local_card_migration_view->GetWidget());
local_card_migration_view->GetDialogClientView()
->ResetViewShownTimeStampForTesting();
views::BubbleFrameView* bubble_frame_view =
static_cast<views::BubbleFrameView*>(
local_card_migration_view->GetWidget()
->non_client_view()
->frame_view());
bubble_frame_view->ResetViewShownTimeStampForTesting();
ClickOnView(view);
destroyed_waiter.Wait();
}
views::View* FindViewInDialogById(
DialogViewId view_id,
views::DialogDelegateView* local_card_migration_view) {
CHECK(local_card_migration_view);
views::View* specified_view =
local_card_migration_view->GetViewByID(static_cast<int>(view_id));
if (!specified_view) {
specified_view =
local_card_migration_view->GetDialogClientView()->GetViewByID(
static_cast<int>(view_id));
}
return specified_view;
}
void ClickOnOkButton(views::DialogDelegateView* local_card_migration_view) {
views::View* ok_button =
local_card_migration_view->GetDialogClientView()->ok_button();
ClickOnDialogViewAndWait(ok_button, local_card_migration_view);
}
void ClickOnCancelButton(
views::DialogDelegateView* local_card_migration_view) {
views::View* cancel_button =
local_card_migration_view->GetDialogClientView()->cancel_button();
ClickOnDialogViewAndWait(cancel_button, local_card_migration_view);
}
views::DialogDelegateView* GetLocalCardMigrationOfferBubbleViews() {
LocalCardMigrationBubbleControllerImpl*
local_card_migration_bubble_controller_impl =
LocalCardMigrationBubbleControllerImpl::FromWebContents(
GetActiveWebContents());
if (!local_card_migration_bubble_controller_impl)
return nullptr;
return static_cast<LocalCardMigrationBubbleViews*>(
local_card_migration_bubble_controller_impl
->local_card_migration_bubble_view());
}
views::DialogDelegateView* GetLocalCardMigrationMainDialogView() {
LocalCardMigrationDialogControllerImpl*
local_card_migration_dialog_controller_impl =
LocalCardMigrationDialogControllerImpl::FromWebContents(
GetActiveWebContents());
if (!local_card_migration_dialog_controller_impl)
return nullptr;
return static_cast<LocalCardMigrationDialogView*>(
local_card_migration_dialog_controller_impl
->local_card_migration_dialog_view());
}
PageActionIconView* GetLocalCardMigrationIconView() {
BrowserView* browser_view =
BrowserView::GetBrowserViewForBrowser(browser());
PageActionIconView* icon =
browser_view->toolbar_button_provider()->GetPageActionIconView(
PageActionIconType::kLocalCardMigration);
if (base::FeatureList::IsEnabled(
features::kAutofillEnableToolbarStatusChip)) {
EXPECT_TRUE(
browser_view->toolbar()->toolbar_account_icon_container()->Contains(
icon));
} else {
EXPECT_TRUE(browser_view->GetLocationBarView()->Contains(icon));
}
return icon;
}
views::View* GetCloseButton() {
LocalCardMigrationBubbleViews* local_card_migration_bubble_views =
static_cast<LocalCardMigrationBubbleViews*>(
GetLocalCardMigrationOfferBubbleViews());
CHECK(local_card_migration_bubble_views);
return local_card_migration_bubble_views->GetBubbleFrameView()
->GetCloseButtonForTest();
}
views::View* GetCardListView() {
return static_cast<LocalCardMigrationDialogView*>(
GetLocalCardMigrationMainDialogView())
->card_list_view_;
}
content::WebContents* GetActiveWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void ResetEventWaiterForSequence(std::list<DialogEvent> event_sequence) {
event_waiter_ =
std::make_unique<EventWaiter<DialogEvent>>(std::move(event_sequence));
}
void WaitForObservedEvent() { event_waiter_->Wait(); }
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
}
void WaitForCardDeletion() { WaitForPersonalDataChange(browser()); }
std::unique_ptr<
base::CallbackList<void(content::BrowserContext*)>::Subscription>
will_create_browser_context_services_subscription_;
LocalCardMigrationManager* local_card_migration_manager_;
PersonalDataManager* personal_data_;
PersonalDataLoadedObserverMock personal_data_observer_;
std::unique_ptr<ProfileSyncServiceHarness> harness_;
private:
std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationBrowserTest);
};
// TODO(crbug.com/932818): Remove this class after experiment flag is cleaned
// up. Otherwise we need it because the toolbar is init-ed before each test is
// set up. Thus need to enable the feature in the general browsertest SetUp().
class LocalCardMigrationBrowserTestForStatusChip
: public LocalCardMigrationBrowserTest {
protected:
LocalCardMigrationBrowserTestForStatusChip()
: LocalCardMigrationBrowserTest() {}
~LocalCardMigrationBrowserTestForStatusChip() override {}
void SetUp() override {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{features::kAutofillCreditCardUploadFeedback,
features::kAutofillEnableToolbarStatusChip,
features::kAutofillUpstream},
/*disabled_features=*/{});
LocalCardMigrationBrowserTest::SetUp();
}
};
// Ensures that migration is not offered when user saves a new card.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
UsingNewCardDoesNotShowIntermediateMigrationOffer) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
FillAndSubmitFormWithCard(kSecondCardNumber);
// No migration bubble should be showing, because the single card upload
// bubble should be displayed instead.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// No metrics are recorded when migration is not offered.
histogram_tester.ExpectTotalCount(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow", 0);
}
// Ensures that migration is not offered when payments declines the cards.
IN_PROC_BROWSER_TEST_F(
LocalCardMigrationBrowserTest,
IntermediateMigrationOfferDoesNotShowWhenPaymentsDeclines) {
base::HistogramTester histogram_tester;
SetUploadDetailsRpcPaymentsDeclines();
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
FillAndSubmitFormWithCard(kFirstCardNumber);
// No bubble should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// No metrics are recorded when migration is not offered.
histogram_tester.ExpectTotalCount(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow", 0);
}
// Ensures that the intermediate migration bubble is not shown after reusing
// a saved server card, if there are no other cards to migrate.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ReusingServerCardDoesNotShowIntermediateMigrationOffer) {
base::HistogramTester histogram_tester;
SaveServerCard(kFirstCardNumber);
FillAndSubmitFormWithCard(kFirstCardNumber);
// No bubble should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// No metrics are recorded when migration is not offered.
histogram_tester.ExpectTotalCount(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow", 0);
}
// Ensures that the intermediate migration bubble is shown after reusing
// a saved server card, if there is at least one card to migrate.
IN_PROC_BROWSER_TEST_F(
LocalCardMigrationBrowserTest,
ReusingServerCardWithMigratableLocalCardShowIntermediateMigrationOffer) {
base::HistogramTester histogram_tester;
SaveServerCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// The intermediate migration bubble should show.
EXPECT_TRUE(
FindViewInDialogById(DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_BUBBLE,
GetLocalCardMigrationOfferBubbleViews())
->GetVisible());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow"),
ElementsAre(
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED, 1),
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_SHOWN, 1)));
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationOrigin.UseOfServerCard",
AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1);
}
// Ensures that the intermediate migration bubble is not shown after reusing
// a previously saved local card, if there are no other cards to migrate.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ReusingLocalCardDoesNotShowIntermediateMigrationOffer) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
FillAndSubmitFormWithCard(kFirstCardNumber);
// No migration bubble should be showing, because the single card upload
// bubble should be displayed instead.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// No metrics are recorded when migration is not offered.
histogram_tester.ExpectTotalCount(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow", 0);
}
// Ensures that the intermediate migration bubble is triggered after reusing
// a saved local card, if there are multiple local cards available to migrate.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ReusingLocalCardShowsIntermediateMigrationOffer) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// The intermediate migration bubble should show.
EXPECT_TRUE(
FindViewInDialogById(DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_BUBBLE,
GetLocalCardMigrationOfferBubbleViews())
->GetVisible());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow"),
ElementsAre(
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED, 1),
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_SHOWN, 1)));
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard",
AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1);
}
// Ensures that clicking [X] on the offer bubble makes the bubble disappear.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClickingCloseClosesBubble) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
// No bubble should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// Metrics
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard",
AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1);
}
// Ensures that the credit card icon will show in location bar.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
CreditCardIconShownInLocationBar) {
SaveServerCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
}
// Ensures that clicking on the credit card icon in the omnibox reopens the
// offer bubble after closing it.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClickingOmniboxIconReshowsBubble) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
ClickOnView(GetLocalCardMigrationIconView());
// Clicking the icon should reshow the bubble.
EXPECT_TRUE(
FindViewInDialogById(DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_BUBBLE,
GetLocalCardMigrationOfferBubbleViews())
->GetVisible());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard"),
ElementsAre(Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1)));
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationBubbleOffer.Reshows"),
ElementsAre(
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED, 1),
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_SHOWN, 1)));
}
// Ensures that accepting the intermediate migration offer opens up the main
// migration dialog.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClickingContinueOpensDialog) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
// Dialog should be visible.
EXPECT_TRUE(FindViewInDialogById(
DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_OFFER_DIALOG,
GetLocalCardMigrationMainDialogView())
->GetVisible());
// Intermediate bubble should be gone.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard"),
ElementsAre(Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1),
Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1),
Bucket(AutofillMetrics::MAIN_DIALOG_SHOWN, 1)));
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationBubbleUserInteraction.FirstShow",
AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_ACCEPTED, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationDialogOffer",
AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_SHOWN, 1);
}
// Ensures that the migration dialog contains all the valid card stored in
// Chrome browser local storage.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
DialogContainsAllValidMigratableCard) {
base::HistogramTester histogram_tester;
CreditCard first_card = SaveLocalCard(kFirstCardNumber);
CreditCard second_card = SaveLocalCard(kSecondCardNumber);
SaveLocalCard(kThirdCardNumber, /*set_as_expired_card=*/true);
SaveLocalCard(kInvalidCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
views::View* card_list_view = GetCardListView();
EXPECT_TRUE(card_list_view->GetVisible());
ASSERT_EQ(2u, card_list_view->children().size());
// Cards will be added to database in a reversed order.
EXPECT_EQ(static_cast<MigratableCardView*>(card_list_view->children()[0])
->GetNetworkAndLastFourDigits(),
second_card.NetworkAndLastFourDigits());
EXPECT_EQ(static_cast<MigratableCardView*>(card_list_view->children()[1])
->GetNetworkAndLastFourDigits(),
first_card.NetworkAndLastFourDigits());
}
// Ensures that rejecting the main migration dialog closes the dialog.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClickingCancelClosesDialog) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
// Click the [Cancel] button.
ClickOnCancelButton(GetLocalCardMigrationMainDialogView());
// No dialog should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationMainDialogView());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard"),
ElementsAre(Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1),
Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1),
Bucket(AutofillMetrics::MAIN_DIALOG_SHOWN, 1)));
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationDialogUserInteraction",
AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED,
1);
}
// Ensures that accepting the main migration dialog closes the dialog.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClickingSaveClosesDialog) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button in the bubble.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
// Click the [Save] button in the dialog.
ClickOnSaveButtonAndWaitForMigrationResults();
// No dialog should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationMainDialogView());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard"),
ElementsAre(Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1),
Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1),
Bucket(AutofillMetrics::MAIN_DIALOG_SHOWN, 1),
Bucket(AutofillMetrics::MAIN_DIALOG_ACCEPTED, 1)));
histogram_tester.ExpectUniqueSample(
"Autofill.LocalCardMigrationDialogUserInteraction",
AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED,
1);
}
// Ensures local cards will be deleted from browser local storage after being
// successfully migrated.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
DeleteSuccessfullyMigratedCardsFromLocal) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button in the bubble.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
// Click the [Save] button in the dialog.
ClickOnSaveButtonAndWaitForMigrationResults();
WaitForCardDeletion();
EXPECT_EQ(nullptr, personal_data_->GetCreditCardByNumber(kFirstCardNumber));
EXPECT_EQ(nullptr, personal_data_->GetCreditCardByNumber(kSecondCardNumber));
}
// Ensures that rejecting the main migration dialog adds 3 strikes.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClosingDialogAddsLocalCardMigrationStrikes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillLocalCardMigrationUsesStrikeSystemV2);
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
// Click the [Cancel] button, should add and log 3 strikes.
ClickOnCancelButton(GetLocalCardMigrationMainDialogView());
// Metrics
EXPECT_THAT(histogram_tester.GetAllSamples(
"Autofill.StrikeDatabase.NthStrikeAdded.LocalCardMigration"),
ElementsAre(Bucket(3, 1)));
}
// Ensures that rejecting the migration bubble adds 2 strikes.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClosingBubbleAddsLocalCardMigrationStrikes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillLocalCardMigrationUsesStrikeSystemV2);
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
// No bubble should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// Metrics
EXPECT_THAT(histogram_tester.GetAllSamples(
"Autofill.StrikeDatabase.NthStrikeAdded.LocalCardMigration"),
ElementsAre(Bucket(2, 1)));
}
// Ensures that rejecting the migration bubble repeatedly adds 2 strikes every
// time, even for the same tab.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ClosingBubbleAgainAddsLocalCardMigrationStrikes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillLocalCardMigrationUsesStrikeSystemV2);
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
// Do it again for the same tab.
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
// No bubble should be showing.
EXPECT_EQ(nullptr, GetLocalCardMigrationOfferBubbleViews());
// Metrics: Added 2 strikes each time, for totals of 2 then 4.
EXPECT_THAT(histogram_tester.GetAllSamples(
"Autofill.StrikeDatabase.NthStrikeAdded.LocalCardMigration"),
ElementsAre(Bucket(2, 1), Bucket(4, 1)));
}
// Ensures that reshowing and closing bubble after previously closing it does
// not add strikes.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTest,
ReshowingBubbleDoesNotAddStrikes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillLocalCardMigrationUsesStrikeSystemV2);
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
base::HistogramTester histogram_tester;
ClickOnView(GetLocalCardMigrationIconView());
// Clicking the icon should reshow the bubble.
EXPECT_TRUE(
FindViewInDialogById(DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_BUBBLE,
GetLocalCardMigrationOfferBubbleViews())
->GetVisible());
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
// Metrics
histogram_tester.ExpectTotalCount(
"Autofill.LocalCardMigrationBubbleOffer.FirstShow", 0);
}
// TODO(crbug.com/932818): Remove the condition once the experiment is enabled
// on ChromeOS.
#if !defined(OS_CHROMEOS)
// Ensures that the credit card icon will show in status chip.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
CreditCardIconShownInStatusChip) {
SaveServerCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
}
// TODO(crbug.com/999510): Crashes flakily on Linux.
#if defined(OS_LINUX)
#define MAYBE_ClickingOmniboxIconReshowsBubble \
DISABLED_ClickingOmniboxIconReshowsBubble
#else
#define MAYBE_ClickingOmniboxIconReshowsBubble ClickingOmniboxIconReshowsBubble
#endif
// Ensures that clicking on the credit card icon in the status chip reopens the
// offer bubble after closing it.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
MAYBE_ClickingOmniboxIconReshowsBubble) {
base::HistogramTester histogram_tester;
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
ClickOnDialogViewAndWait(GetCloseButton(),
GetLocalCardMigrationOfferBubbleViews());
ClickOnView(GetLocalCardMigrationIconView());
// Clicking the icon should reshow the bubble.
EXPECT_TRUE(
FindViewInDialogById(DialogViewId::MAIN_CONTENT_VIEW_MIGRATION_BUBBLE,
GetLocalCardMigrationOfferBubbleViews())
->GetVisible());
// Metrics
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationOrigin.UseOfLocalCard"),
ElementsAre(Bucket(AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1)));
EXPECT_THAT(
histogram_tester.GetAllSamples(
"Autofill.LocalCardMigrationBubbleOffer.Reshows"),
ElementsAre(
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED, 1),
Bucket(AutofillMetrics::LOCAL_CARD_MIGRATION_BUBBLE_SHOWN, 1)));
}
#if defined(OS_MACOSX)
// TODO(crbug.com/823543): Widget activation doesn't work on Mac.
#define MAYBE_ActivateFirstInactiveBubbleForAccessibility \
DISABLED_ActivateFirstInactiveBubbleForAccessibility
#else
#define MAYBE_ActivateFirstInactiveBubbleForAccessibility \
ActivateFirstInactiveBubbleForAccessibility
#endif
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
MAYBE_ActivateFirstInactiveBubbleForAccessibility) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
ToolbarView* toolbar_view = browser_view->toolbar();
EXPECT_FALSE(toolbar_view->toolbar_account_icon_container()
->page_action_icon_container()
->ActivateFirstInactiveBubbleForAccessibility());
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Ensures the bubble's widget is visible, but inactive. Active widgets are
// focused by accessibility, so not of concern.
views::Widget* widget = GetLocalCardMigrationOfferBubbleViews()->GetWidget();
widget->Deactivate();
widget->ShowInactive();
EXPECT_TRUE(widget->IsVisible());
EXPECT_FALSE(widget->IsActive());
EXPECT_TRUE(toolbar_view->toolbar_account_icon_container()
->page_action_icon_container()
->ActivateFirstInactiveBubbleForAccessibility());
// Ensure the bubble's widget refreshed appropriately.
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_TRUE(widget->IsVisible());
EXPECT_TRUE(widget->IsActive());
}
// Ensures the credit card icon updates its visibility when switching between
// tabs.
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
IconAndBubbleVisibilityAfterTabSwitching) {
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Ensures flow is triggered, and bubble and icon view are visible.
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_TRUE(GetLocalCardMigrationOfferBubbleViews()->GetVisible());
AddTabAtIndex(1, GURL("http://example.com/"), ui::PAGE_TRANSITION_TYPED);
TabStripModel* tab_model = browser()->tab_strip_model();
tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
// Ensures bubble and icon go away if user navigates to another tab.
EXPECT_FALSE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_FALSE(GetLocalCardMigrationOfferBubbleViews());
tab_model->ActivateTabAt(0, {TabStripModel::GestureType::kOther});
// If the user navigates back, shows only the icon not the bubble.
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_FALSE(GetLocalCardMigrationOfferBubbleViews());
}
IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
Feedback_CardSavingAnimation) {
SaveLocalCard(kFirstCardNumber);
SaveLocalCard(kSecondCardNumber);
UseCardAndWaitForMigrationOffer(kFirstCardNumber);
// Click the [Continue] button in the bubble.
ClickOnOkButton(GetLocalCardMigrationOfferBubbleViews());
test_url_loader_factory()->ClearResponses();
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_FALSE(GetLocalCardMigrationIconView()
->loading_indicator_for_testing()
->IsAnimating());
// Click the [Save] button in the dialog.
ResetEventWaiterForSequence({DialogEvent::SENT_MIGRATE_CARDS_REQUEST});
ClickOnOkButton(GetLocalCardMigrationMainDialogView());
WaitForObservedEvent();
// No dialog should be showing, but icon should display throbber animation.
EXPECT_EQ(nullptr, GetLocalCardMigrationMainDialogView());
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_TRUE(GetLocalCardMigrationIconView()
->loading_indicator_for_testing()
->IsAnimating());
SetUpMigrateCardsRpcPaymentsAccepts();
ResetEventWaiterForSequence({DialogEvent::RECEIVED_MIGRATE_CARDS_RESPONSE});
WaitForObservedEvent();
// Icon animation should stop. Dialog stays hidden.
EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
EXPECT_FALSE(GetLocalCardMigrationIconView()
->loading_indicator_for_testing()
->IsAnimating());
}
#endif // !defined(OS_CHROMEOS)
// TODO(crbug.com/897998):
// - Update test set-up and add navigation tests.
// - Add more tests for feedback dialog.
} // namespace autofill