blob: 509491950357e1eb3708942068dbcda33e17a16c [file] [log] [blame]
// 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/ui/views/autofill/edit_address_profile_view.h"
#include <memory>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/autofill/edit_address_profile_dialog_controller.h"
#include "chrome/browser/ui/views/autofill/address_editor_view.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/common/autofill_features.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget.h"
namespace autofill {
// |arg| must be of type base::optional_ref<const AutofillProfile>.
MATCHER_P2(AutofillProfileHasInfo, type, expected_value, "") {
EXPECT_TRUE(arg.has_value());
return arg.value().GetRawInfo(type) == expected_value;
}
class MockEditAddressProfileDialogController
: public EditAddressProfileDialogController {
public:
MOCK_METHOD(std::u16string, GetWindowTitle, (), (const, override));
MOCK_METHOD(const std::u16string&, GetFooterMessage, (), (const, override));
MOCK_METHOD(std::u16string, GetOkButtonLabel, (), (const, override));
MOCK_METHOD(const AutofillProfile&, GetProfileToEdit, (), (const, override));
MOCK_METHOD(bool, GetIsValidatable, (), (const, override));
MOCK_METHOD(void,
OnDialogClosed,
(AutofillClient::AddressPromptUserDecision decision,
base::optional_ref<const AutofillProfile> profile),
(override));
};
class EditAddressProfileViewTest : public ChromeViewsTestBase {
public:
EditAddressProfileViewTest() = default;
~EditAddressProfileViewTest() override = default;
void CreateViewAndShow() { CreateViewAndShow(address_profile_to_edit()); }
void CreateViewAndShow(const AutofillProfile& address_profile);
void SetUp() override {
ChromeViewsTestBase::SetUp();
address_profile_to_edit_ = test::GetFullProfile();
test_web_contents_ =
content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
ON_CALL(mock_controller_, GetFooterMessage)
.WillByDefault(::testing::ReturnRefOfCopy(std::u16string()));
}
void TearDown() override {
dialog_ = nullptr;
if (widget_) {
std::exchange(widget_, nullptr)->Close();
}
parent_widget_.reset();
ChromeViewsTestBase::TearDown();
}
const AutofillProfile& address_profile_to_edit() {
return address_profile_to_edit_;
}
content::WebContents* test_web_contents() { return test_web_contents_.get(); }
EditAddressProfileView* dialog() { return dialog_; }
MockEditAddressProfileDialogController* mock_controller() {
return &mock_controller_;
}
private:
base::test::ScopedFeatureList feature_list_;
TestingProfile profile_;
AutofillProfile address_profile_to_edit_{
i18n_model_definition::kLegacyHierarchyCountryCode};
// This enables uses of TestWebContents.
content::RenderViewHostTestEnabler test_render_host_factories_;
std::unique_ptr<content::WebContents> test_web_contents_;
std::unique_ptr<views::Widget> parent_widget_;
raw_ptr<views::Widget> widget_ = nullptr;
raw_ptr<EditAddressProfileView> dialog_ = nullptr;
testing::NiceMock<MockEditAddressProfileDialogController> mock_controller_;
};
void EditAddressProfileViewTest::CreateViewAndShow(
const AutofillProfile& address_profile) {
ON_CALL(*mock_controller(), GetWindowTitle())
.WillByDefault(testing::Return(std::u16string()));
ON_CALL(*mock_controller(), GetProfileToEdit())
.WillByDefault(testing::ReturnRef(address_profile));
dialog_ = new EditAddressProfileView(mock_controller());
dialog_->ShowForWebContents(test_web_contents());
gfx::NativeView parent = gfx::NativeView();
#if BUILDFLAG(IS_MAC)
// We need a native view parent for the dialog to avoid a DCHECK
// on Mac.
parent_widget_ = CreateTestWidget();
parent = parent_widget_->GetNativeView();
#endif
widget_ =
views::DialogDelegate::CreateDialogWidget(dialog_, GetContext(), parent);
widget_->SetVisibilityChangedAnimationsEnabled(false);
widget_->Show();
#if BUILDFLAG(IS_MAC)
// Necessary for Mac. On other platforms this happens in the focus
// manager, but it's disabled for Mac due to crbug.com/650859.
parent_widget_->Activate();
widget_->Activate();
#endif
}
TEST_F(EditAddressProfileViewTest, Sanity) {
CreateViewAndShow();
// Check that both OK and cancel button are enabled.
EXPECT_TRUE(dialog()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
EXPECT_TRUE(dialog()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));
}
TEST_F(EditAddressProfileViewTest, SaveInvokesTheCallbackWithEditedFullname) {
CreateViewAndShow();
const std::u16string kNewFirstName = u"New First Name";
const std::string locale = g_browser_process->GetApplicationLocale();
// Confirm that the new name is indeed different from the original one from
// the controller.
ASSERT_NE(kNewFirstName, address_profile_to_edit().GetInfo(
autofill::FieldType::NAME_FULL, locale));
AddressEditorView* editor_view = dialog()->GetAddressEditorViewForTesting();
DCHECK(editor_view);
editor_view->SetTextInputFieldValueForTesting(autofill::FieldType::NAME_FULL,
kNewFirstName);
EXPECT_CALL(
*mock_controller(),
OnDialogClosed(AutofillClient::AddressPromptUserDecision::kEditAccepted,
AutofillProfileHasInfo(autofill::FieldType::NAME_FULL,
kNewFirstName)));
dialog()->Accept();
}
TEST_F(EditAddressProfileViewTest,
SaveInvokesTheCallbackWithEditedInvalidPhoneNumber) {
CreateViewAndShow();
const std::u16string kNewPhoneNumber = u"123456789";
const std::string locale = g_browser_process->GetApplicationLocale();
// Confirm that the new phone number is indeed different from the original one
// from the controller.
ASSERT_NE(kNewPhoneNumber,
address_profile_to_edit().GetInfo(
autofill::FieldType::PHONE_HOME_WHOLE_NUMBER, locale));
// Set the phone number in the editor to the new invalid value. Make sure that
// this value is respected and sent to the backend.
AddressEditorView* editor_view = dialog()->GetAddressEditorViewForTesting();
DCHECK(editor_view);
editor_view->SetTextInputFieldValueForTesting(
autofill::FieldType::PHONE_HOME_WHOLE_NUMBER, kNewPhoneNumber);
EXPECT_CALL(
*mock_controller(),
OnDialogClosed(
AutofillClient::AddressPromptUserDecision::kEditAccepted,
AutofillProfileHasInfo(autofill::FieldType::PHONE_HOME_WHOLE_NUMBER,
kNewPhoneNumber)));
dialog()->Accept();
}
TEST_F(EditAddressProfileViewTest, SaveInvokesTheCallbackWithEditedEmail) {
CreateViewAndShow();
const std::u16string kNewEmail = u"abc@xyz.com";
const std::string locale = g_browser_process->GetApplicationLocale();
// Confirm that the new email is indeed different from the original one
// from the controller.
ASSERT_NE(kNewEmail, address_profile_to_edit().GetInfo(
autofill::FieldType::EMAIL_ADDRESS, locale));
AddressEditorView* editor_view = dialog()->GetAddressEditorViewForTesting();
DCHECK(editor_view);
editor_view->SetTextInputFieldValueForTesting(
autofill::FieldType::EMAIL_ADDRESS, kNewEmail);
EXPECT_CALL(
*mock_controller(),
OnDialogClosed(AutofillClient::AddressPromptUserDecision::kEditAccepted,
AutofillProfileHasInfo(autofill::FieldType::EMAIL_ADDRESS,
kNewEmail)));
dialog()->Accept();
}
TEST_F(EditAddressProfileViewTest, InvalidFormIsNotSent) {
ON_CALL(*mock_controller(), GetIsValidatable)
.WillByDefault(::testing::Return(true));
CreateViewAndShow(AutofillProfile(AddressCountryCode("US")));
// `kIgnored` is sent as the decision when the dialog is closed (on test end).
EXPECT_CALL(
*mock_controller(),
OnDialogClosed(AutofillClient::AddressPromptUserDecision::kIgnored,
::testing::_));
EXPECT_CALL(
*mock_controller(),
OnDialogClosed(AutofillClient::AddressPromptUserDecision::kEditAccepted,
::testing::_))
.Times(0);
dialog()->Accept();
}
TEST_F(EditAddressProfileViewTest, GetInitiallyFocusedView) {
auto dialog = std::make_unique<EditAddressProfileView>(mock_controller());
EXPECT_EQ(dialog->GetInitiallyFocusedView(), nullptr);
AutofillProfile profile(AddressCountryCode("US"));
ON_CALL(*mock_controller(), GetProfileToEdit())
.WillByDefault(testing::ReturnRef(profile));
dialog->ShowForWebContents(test_web_contents());
EXPECT_NE(dialog->GetInitiallyFocusedView(), nullptr);
EXPECT_EQ(
std::string(
dialog->GetInitiallyFocusedView()->GetClassMetaData()->type_name()),
"Combobox");
}
} // namespace autofill