// Copyright 2021 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/autofill/android/save_update_address_profile_message_controller.h"
#include "base/android/jni_android.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/android/android_theme_resources.h"
#include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_testing_helper.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/autofill_profile_test_api.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/messages/android/mock_message_dispatcher_bridge.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/strings/grit/components_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
using testing::_;
using profile_ref = base::optional_ref<const AutofillProfile>;
using ::testing::Property;
class SaveUpdateAddressProfileMessageControllerTest
: public ChromeRenderViewHostTestHarness {
public:
SaveUpdateAddressProfileMessageControllerTest() = default;
protected:
void SetUp() override;
void TearDown() override;
TestingProfile::TestingFactories GetTestingFactories() const override;
void SigninUser(const std::string& email, signin::ConsentLevel consent_level);
void EnqueueSaveMessage(
const AutofillProfile& profile,
bool is_migration_to_account,
AutofillClient::AddressProfileSavePromptCallback save_callback,
SaveUpdateAddressProfileMessageController::PrimaryActionCallback
action_callback) {
EnqueueMessage(profile, nullptr, is_migration_to_account,
std::move(save_callback), std::move(action_callback));
}
void EnqueueUpdateMessage(
const AutofillProfile& profile,
const AutofillProfile* original_profile,
AutofillClient::AddressProfileSavePromptCallback save_callback,
SaveUpdateAddressProfileMessageController::PrimaryActionCallback
action_callback) {
EnqueueMessage(profile, original_profile, /*is_migration_to_account=*/false,
std::move(save_callback), std::move(action_callback));
}
void ExpectDismissMessageCall();
void TriggerActionClick();
void TriggerMessageDismissedCallback(messages::DismissReason dismiss_reason);
messages::MessageWrapper* GetMessageWrapper();
std::unique_ptr<AutofillProfile> profile_;
std::unique_ptr<AutofillProfile> original_profile_;
base::MockCallback<AutofillClient::AddressProfileSavePromptCallback>
save_callback_;
base::MockCallback<
SaveUpdateAddressProfileMessageController::PrimaryActionCallback>
action_callback_;
private:
void EnqueueMessage(
const AutofillProfile& profile,
const AutofillProfile* original_profile,
bool is_migration_to_account,
AutofillClient::AddressProfileSavePromptCallback save_callback,
SaveUpdateAddressProfileMessageController::PrimaryActionCallback
action_callback);
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_adaptor_;
SaveUpdateAddressProfileMessageController controller_;
messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
base::test::ScopedFeatureList feature_list_;
};
void SaveUpdateAddressProfileMessageControllerTest::SetUp() {
ChromeRenderViewHostTestHarness::SetUp();
messages::MessageDispatcherBridge::SetInstanceForTesting(
&message_dispatcher_bridge_);
identity_test_env_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
profile_ = std::make_unique<AutofillProfile>(test::GetFullProfile());
original_profile_ =
std::make_unique<AutofillProfile>(test::GetFullProfile2());
}
void SaveUpdateAddressProfileMessageControllerTest::TearDown() {
messages::MessageDispatcherBridge::SetInstanceForTesting(nullptr);
ChromeRenderViewHostTestHarness::TearDown();
}
TestingProfile::TestingFactories
SaveUpdateAddressProfileMessageControllerTest::GetTestingFactories() const {
return IdentityTestEnvironmentProfileAdaptor::
GetIdentityTestEnvironmentFactories();
}
void SaveUpdateAddressProfileMessageControllerTest::SigninUser(
const std::string& email,
signin::ConsentLevel consent_level) {
identity_test_env_adaptor_->identity_test_env()->MakePrimaryAccountAvailable(
email, consent_level);
}
void SaveUpdateAddressProfileMessageControllerTest::EnqueueMessage(
const AutofillProfile& profile,
const AutofillProfile* original_profile,
bool is_migration_to_account,
AutofillClient::AddressProfileSavePromptCallback save_callback,
SaveUpdateAddressProfileMessageController::PrimaryActionCallback
action_callback) {
EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
controller_.DisplayMessage(web_contents(), profile, original_profile,
is_migration_to_account, std::move(save_callback),
std::move(action_callback));
EXPECT_TRUE(controller_.IsMessageDisplayed());
}
void SaveUpdateAddressProfileMessageControllerTest::ExpectDismissMessageCall() {
EXPECT_CALL(message_dispatcher_bridge_, DismissMessage)
.WillOnce([](messages::MessageWrapper* message,
messages::DismissReason dismiss_reason) {
message->HandleDismissCallback(base::android::AttachCurrentThread(),
static_cast<int>(dismiss_reason));
});
}
void SaveUpdateAddressProfileMessageControllerTest::TriggerActionClick() {
GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
EXPECT_TRUE(controller_.IsMessageDisplayed());
}
void SaveUpdateAddressProfileMessageControllerTest::
TriggerMessageDismissedCallback(messages::DismissReason dismiss_reason) {
GetMessageWrapper()->HandleDismissCallback(
base::android::AttachCurrentThread(), static_cast<int>(dismiss_reason));
EXPECT_FALSE(controller_.IsMessageDisplayed());
}
messages::MessageWrapper*
SaveUpdateAddressProfileMessageControllerTest::GetMessageWrapper() {
return controller_.message_.get();
}
// Tests that the save message properties (title, description with profile
// details, primary button text, icon) are set correctly during local or sync
// address profile saving process.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
SaveMessageContent_LocalOrSyncAddressProfile) {
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/false,
save_callback_.Get(), action_callback_.Get());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_TITLE),
GetMessageWrapper()->GetTitle());
EXPECT_EQ(u"John H. Doe, 666 Erebus St.",
GetMessageWrapper()->GetDescription());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_OK_BUTTON_LABEL),
GetMessageWrapper()->GetPrimaryButtonText());
EXPECT_EQ(1, GetMessageWrapper()->GetPrimaryButtonTextMaxLines());
EXPECT_EQ(SaveUpdateAddressProfileMessageController::kDescriptionMaxLines,
GetMessageWrapper()->GetDescriptionMaxLines());
EXPECT_EQ(ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_ADDRESS),
GetMessageWrapper()->GetIconResourceId());
TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
}
// Tests that the save message properties (title, description with profile
// details, primary button text, icon) are set correctly during address profile
// migration flow.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
SaveMessageContent_AddressProfileMigrationFlow) {
test_api(*profile_).set_record_type(AutofillProfile::RecordType::kAccount);
test_api(*original_profile_)
.set_record_type(AutofillProfile::RecordType::kAccount);
SigninUser(TestingProfile::kDefaultProfileUserName,
signin::ConsentLevel::kSignin);
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/true,
save_callback_.Get(), action_callback_.Get());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_AUTOFILL_ACCOUNT_MIGRATE_ADDRESS_PROMPT_TITLE),
GetMessageWrapper()->GetTitle());
EXPECT_EQ(
l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_IN_ACCOUNT_MESSAGE_ADDRESS_MIGRATION_SOURCE_NOTICE),
GetMessageWrapper()->GetDescription());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_OK_BUTTON_LABEL),
GetMessageWrapper()->GetPrimaryButtonText());
EXPECT_EQ(1, GetMessageWrapper()->GetPrimaryButtonTextMaxLines());
EXPECT_EQ(SaveUpdateAddressProfileMessageController::kDescriptionMaxLines,
GetMessageWrapper()->GetDescriptionMaxLines());
EXPECT_EQ(
ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_UPLOAD_ADDRESS),
GetMessageWrapper()->GetIconResourceId());
TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
}
// Tests that the save message properties (title, description with profile
// details, primary button text, icon) are set correctly when a new address
// profile is saved in account.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
SaveMessageContent_AccountAddressProfile) {
test_api(*profile_).set_record_type(AutofillProfile::RecordType::kAccount);
test_api(*original_profile_)
.set_record_type(AutofillProfile::RecordType::kAccount);
SigninUser(TestingProfile::kDefaultProfileUserName,
signin::ConsentLevel::kSignin);
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/false,
save_callback_.Get(), action_callback_.Get());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_TITLE),
GetMessageWrapper()->GetTitle());
EXPECT_EQ(l10n_util::GetStringFUTF16(
IDS_AUTOFILL_SAVE_IN_ACCOUNT_MESSAGE_ADDRESS_SOURCE_NOTICE,
base::ASCIIToUTF16(TestingProfile::kDefaultProfileUserName)),
GetMessageWrapper()->GetDescription());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_OK_BUTTON_LABEL),
GetMessageWrapper()->GetPrimaryButtonText());
EXPECT_EQ(1, GetMessageWrapper()->GetPrimaryButtonTextMaxLines());
EXPECT_EQ(SaveUpdateAddressProfileMessageController::kDescriptionMaxLines,
GetMessageWrapper()->GetDescriptionMaxLines());
EXPECT_EQ(ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_ADDRESS),
GetMessageWrapper()->GetIconResourceId());
TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
}
// Tests that the update message properties (title, description with original
// profile details, primary button text, icon) are set correctly.
TEST_F(SaveUpdateAddressProfileMessageControllerTest, UpdateMessageContent) {
EnqueueUpdateMessage(*profile_, original_profile_.get(), save_callback_.Get(),
action_callback_.Get());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTOFILL_UPDATE_ADDRESS_PROMPT_TITLE),
GetMessageWrapper()->GetTitle());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_AUTOFILL_UPDATE_ADDRESS_PROMPT_OK_BUTTON_LABEL),
GetMessageWrapper()->GetPrimaryButtonText());
EXPECT_EQ(1, GetMessageWrapper()->GetPrimaryButtonTextMaxLines());
EXPECT_EQ(u"Jane A. Smith, 123 Main Street",
GetMessageWrapper()->GetDescription());
EXPECT_EQ(SaveUpdateAddressProfileMessageController::kDescriptionMaxLines,
GetMessageWrapper()->GetDescriptionMaxLines());
EXPECT_EQ(ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_ADDRESS),
GetMessageWrapper()->GetIconResourceId());
TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
}
// Tests that the action callback is triggered when the user clicks on the
// primary action button of the save message.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
ProceedOnActionClickWhenSave) {
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/false,
save_callback_.Get(), action_callback_.Get());
EXPECT_CALL(action_callback_, Run(_, *profile_, nullptr, false, _));
TriggerActionClick();
EXPECT_CALL(save_callback_, Run(_, Property(&profile_ref::has_value, false)))
.Times(0);
TriggerMessageDismissedCallback(messages::DismissReason::PRIMARY_ACTION);
}
// Tests that the action callback is triggered when the user clicks on the
// primary action button of the update message.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
ProceedOnActionClickWhenUpdate) {
EnqueueUpdateMessage(*profile_, original_profile_.get(), save_callback_.Get(),
action_callback_.Get());
EXPECT_CALL(action_callback_,
Run(_, *profile_, original_profile_.get(), _, _));
TriggerActionClick();
EXPECT_CALL(save_callback_, Run(_, Property(&profile_ref::has_value, false)))
.Times(0);
TriggerMessageDismissedCallback(messages::DismissReason::PRIMARY_ACTION);
}
// Tests that the save callback is triggered with
// |AddressPromptUserDecision::kMessageDeclined| when the user
// dismisses the message via gesture.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
DecisionIsMessageDeclinedOnGestureDismiss) {
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/false,
save_callback_.Get(), action_callback_.Get());
EXPECT_CALL(save_callback_,
Run(AutofillClient::AddressPromptUserDecision::kMessageDeclined,
Property(&profile_ref::has_value, false)));
TriggerMessageDismissedCallback(messages::DismissReason::GESTURE);
}
// Tests that the save callback is triggered with
// |AddressPromptUserDecision::kMessageTimeout| when the message is
// auto-dismissed after a timeout.
TEST_F(SaveUpdateAddressProfileMessageControllerTest,
DecisionIsMessageTimeoutOnTimerAutodismiss) {
EnqueueSaveMessage(*profile_, /*is_migration_to_account=*/false,
save_callback_.Get(), action_callback_.Get());
EXPECT_CALL(save_callback_,
Run(AutofillClient::AddressPromptUserDecision::kMessageTimeout,
Property(&profile_ref::has_value, false)));
TriggerMessageDismissedCallback(messages::DismissReason::TIMER);
}
// Tests that the previous prompt gets dismissed when the new one is enqueued.
TEST_F(SaveUpdateAddressProfileMessageControllerTest, OnlyOnePromptAtATime) {
EnqueueUpdateMessage(*profile_, original_profile_.get(), save_callback_.Get(),
action_callback_.Get());
AutofillProfile another_profile = test::GetFullProfile();
base::MockCallback<AutofillClient::AddressProfileSavePromptCallback>
another_save_callback;
base::MockCallback<
SaveUpdateAddressProfileMessageController::PrimaryActionCallback>
another_action_callback;
EXPECT_CALL(save_callback_,
Run(AutofillClient::AddressPromptUserDecision::kIgnored,
Property(&profile_ref::has_value, false)));
ExpectDismissMessageCall();
EnqueueSaveMessage(another_profile, /*is_migration_to_account=*/false,
another_save_callback.Get(),
another_action_callback.Get());
TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
}
} // namespace autofill